简体   繁体   English

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

[英]PyQt5 application slow GUI response for unknown reasons

I'm creating a PyQt5 application with a socket.我正在创建一个带有套接字的 PyQt5 应用程序。 In an effort to prevent the socket from blocking GUI updates I outsourced the socket to its own thread.为了防止套接字阻塞 GUI 更新,我将套接字外包给了它自己的线程。 However, I am still getting some issues with the GUI freezing or being slow to update.但是,我仍然遇到一些 GUI 冻结或更新缓慢的问题。 It usually resumes when the socket receives data and passes it to the GUI, and I'm having a hard time figuring out where.它通常在套接字接收数据并将其传递给 GUI 时恢复,我很难弄清楚在哪里。 The issue is especially appearing when resizing or moving the main window or its sub window.当调整或移动主 window 或其子 window 的大小或移动时,此问题尤其出现。

I have stripped the below code down to its basics: Create a GUI, start socket thread, Read data from socket, do stuff with it, and display the results in its console buffer.我已将以下代码简化为基础:创建一个 GUI,启动套接字线程,从套接字读取数据,用它做一些事情,并在其控制台缓冲区中显示结果。 In addition, do stuff with console input (passing the results to the socket has not been implemented yet).另外,用控制台输入做一些事情(将结果传递给套接字还没有实现)。

Data is passed between GUI and socket via inQueue and outQueue , and the GUI is supposed to be updated from a QTimer() that triggers an updateGui() function.数据通过inQueueoutQueue在 GUI 和套接字之间传递,并且 GUI 应该从触发updateGui() function 的QTimer()更新。 I've set this to 500 for now, and increasing it to 5 seconds (5000) seems to reduce the glitchiness of the GUI, but not consistently.我现在将其设置为 500,将其增加到 5 秒(5000)似乎可以减少 GUI 的故障,但并非始终如一。 Ideally I would like to lower this to something like 100ms or even lower for a more responsive GUI, but for now I have it at 500 for debugging purposes.理想情况下,我想将其降低到 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.

Theories:理论:

  • Did I fail at launching SocketWorker in its own thread somehow?我是否以某种方式在自己的线程中启动 SocketWorker 失败了?
  • Is the GUI set up in a severely inefficient matter? GUI 的设置是否效率极低?
  • Something wrong with the queueing?排队有什么问题吗?

Update 1:更新 1:

I've found that the problem disappears if I comment out self.consoleWindow.append(self.inQueue.get()) in def updateGui() , indicating as issue with Queue()我发现如果我在def updateGui()中注释掉self.consoleWindow.append(self.inQueue.get())问题就会消失,这表明Queue()有问题

Update 2:更新 2:

I figured it out.我想到了。 See answer below.请参阅下面的答案。

It turns out that queue.Queue() has a timeout, and is therefore causing the lock, as it is being polled very often.事实证明queue.Queue()有一个超时,因此导致了锁,因为它经常被轮询。 The fix is to use get_nowait() instead.解决方法是改用get_nowait() However, that caused the method to croak with the exception raise Empty .但是,这导致该方法发出异常raise Empty So I did the lazy fix of wrapping it in a try:except: block, and that did the trick.所以我做了一个懒惰的修复,将它包装在一个try:except:块中,这就成功了。 It now works, and I've reduced the QTimer to 10ms, without any issues.它现在可以工作了,我已经将 QTimer 减少到 10 毫秒,没有任何问题。 The function now looks like this: 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