繁体   English   中英

PyQt5 应用程序 GUI 响应缓慢,原因不明

[英]PyQt5 application slow GUI response for unknown reasons

我正在创建一个带有套接字的 PyQt5 应用程序。 为了防止套接字阻塞 GUI 更新,我将套接字外包给了它自己的线程。 但是,我仍然遇到一些 GUI 冻结或更新缓慢的问题。 它通常在套接字接收数据并将其传递给 GUI 时恢复,我很难弄清楚在哪里。 当调整或移动主 window 或其子 window 的大小或移动时,此问题尤其出现。

我已将以下代码简化为基础:创建一个 GUI,启动套接字线程,从套接字读取数据,用它做一些事情,并在其控制台缓冲区中显示结果。 另外,用控制台输入做一些事情(将结果传递给套接字还没有实现)。

数据通过inQueueoutQueue在 GUI 和套接字之间传递,并且 GUI 应该从触发updateGui() function 的QTimer()更新。 我现在将其设置为 500,将其增加到 5 秒(5000)似乎可以减少 GUI 的故障,但并非始终如一。 理想情况下,我想将其降低到 100 毫秒甚至更低,以获得更灵敏的 GUI,但现在我将其设置为 500 以用于调试目的。

import os
import sys
import socket
import time

from PyQt5.QtWidgets import QApplication, QMainWindow, QMdiArea
from PyQt5.Qt import QWidget, QMdiSubWindow, QVBoxLayout, QGridLayout, QHBoxLayout, QTextEdit, QLineEdit, QFormLayout, QObject
from PyQt5.QtCore import QTimer
import threading
from threading import Thread
from queue import Queue

class Gui(QMainWindow):
    def __init__(self):

        # Set up GUI with a sub-window named `consoleWindow` from the `ConsoleWindow()` class
        super(Gui, self).__init__()
        self.mainWin = QWidget()
        self.mainWin.resize(640, 480)
        self.winArea = QMdiArea(self.mainWin)
        self.mainLayout = QVBoxLayout()
        self.mainLayout.setStretch(1, 1)
        self.mainLayout.addWidget(self.winArea)
        self.mainWin.setLayout(self.mainLayout)

        # set up consoleWindow
        self.consoleWindow = ConsoleWindow(self)
        self.winArea.addSubWindow(self.consoleWindow)
        self.mainWin.show()

        # Set up message queues for passing data between socketthread and GUI
        self.outQueue = Queue()
        self.inQueue = Queue()

        # Set up and launch socketthread
        self.socket = SocketWorker(self, SETTINGS_HOST, SETTINGS_PORT, self.inQueue, self.outQueue)
        self.socketThread = threading.Thread(target=self.socket.createAndRun, args=(self.inQueue, self.outQueue))
        self.socketThread.start()

        # Periodically do stuff
        self.timer = QTimer(self)
        self.timer.setInterval(500)
        self.timer.timeout.connect(self.updateGui)
        self.timer.start()

    def consoleInput(self, input):
        # TODO do stuff with input. Not a whole lot happening here yet.

    def updateGui(self):
        if self.inQueue.not_empty:
            self.consoleWindow.append(self.inQueue.get())

class ConsoleWindow(QMdiSubWindow):
    def __init__(self, gui):

        # Setup console
        super(ConsoleWindow, self).__init__()
        self.gui = gui
        self.consoleContents = QWidget()
        self.buffer = QTextEdit()
        self.buffer.setReadOnly(True)
        self.input = QLineEdit()
        self.input.returnPressed.connect(self.cmdinput)

        # Create console widgets
        self.consoleLayout = QVBoxLayout(self.consoleContents)
        self.consoleLayout.addWidget(self.buffer)
        self.consoleLayout.addWidget(self.input)

        self.consoleContents.setLayout(self.consoleLayout)
        self.setWidget(self.consoleContents)

    def append(self, string):
        self.buffer.append(string)

    def cmdinput(self):
        self.gui.consoleInput(self.input.text())
        self.input.clear()

class SocketWorker(Thread):
    def __init__(self, gui, host, port, inQueue, outQueue):
        Thread.__init__(self)
        self.gui = gui
        super(SocketWorker, self).__init__()

    def createAndRun(self, inQueue, outQueue):
        self.inQueue = inQueue
        self.outQueue = outQueue

        # SNIP: Create socket, continuously read from it, process incoming data, and shove the results into inQueue. 
        # SNIP: Process any data in outQueue as produced by the GUI, and shove through the socket.

理论:

  • 我是否以某种方式在自己的线程中启动 SocketWorker 失败了?
  • GUI 的设置是否效率极低?
  • 排队有什么问题吗?

更新 1:

我发现如果我在def updateGui()中注释掉self.consoleWindow.append(self.inQueue.get())问题就会消失,这表明Queue()有问题

更新 2:

我想到了。 请参阅下面的答案。

事实证明queue.Queue()有一个超时,因此导致了锁,因为它经常被轮询。 解决方法是改用get_nowait() 但是,这导致该方法发出异常raise Empty 所以我做了一个懒惰的修复,将它包装在一个try:except:块中,这就成功了。 它现在可以工作了,我已经将 QTimer 减少到 10 毫秒,没有任何问题。 function 现在看起来像这样:

def updateGui(self):
    if self.inQueue.not_empty:
        try:
            self.consoleWindow.append(self.inQueue.get_nowait())
        except:
            True

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM