PyQt、QThread:GUI 因来自另一个线程的大量信号而冻结(QListWidget 大量更新)

PyQt, QThread: GUI freeze with large amount of SIGNAL from another thread (QListWidget massive update)

我有一个启动 GUI 并使用 QThread 在另一个线程中执行 "the heavy part" 代码的应用程序。在此线程中,我发出连接到 GUI Class 并在 QListWidget 上执行 addItem 的信号。

从这个线程到 GUI 有大量 "signaling",它 "freeze"。

有没有办法避免这种情况?我是否必须在不同线程中仅对 QListWidget 使用另一个迷你 GUI?

谢谢

编辑: 这是执行重逻辑的线程

class YourThreadName(QThread):
    def __init__(self, some variables):
        QThread.__init__(self)

    def __del__(self):
        self.wait()

    def run(self):
        # Here there is a for cycle that emits a SIGNAL
        for ... :
            ...
            self.emit(SIGNAL("needed_variable"), needed_variable)
            ...

在 GUI Class 中有一些方法,特别是:

class GUI(QtGui.QMainWindow, GUI.Ui_MainWindow):
    def __init__(self, parent=None):
        super(GUI, self).__init__(parent)
        self.setupUi(self)

    def ... (self):
        ...

    def start_main_code(self):
        self.new_thread = YourThreadName(some variables)
        self.connect(self.new_thread, SIGNAL("finished()"), self.done)
        self.connect(self.new_thread, SIGNAL("needed_variable"), self.show_variable)
        self.new_thread.start()

    def show_variable(self, data):
        self.QListWidget_object.addItem(data)

    def ... (self):
        ...

下面的脚本是基于您的问题和评论中当前提供的信息的最小、完整且可验证的示例。它每 10 毫秒从工作线程发出数据并更新 GUI 中的列表小部件。在我的 Linux 系统(使用 Python-3.6.3、Qt-4.8.7 和 PyQt-4.12.1)上,它不会阻止或冻结 GUI。更新列表小部件时显然有一些闪烁,但我可以 select 项目,上下滚动,单击按钮等。如果我将睡眠时间增加到 25 毫秒,我不会甚至闪烁。

更新:

可以通过使用 setUniformItemSizes 和批量发送消息来提高性能。在我的系统上,经过轻微的初始延迟后,列表几乎立即填充了五万个项目。

import sys
from PyQt4 import QtCore, QtGui

class Worker(QtCore.QThread):
    message = QtCore.pyqtSignal(object)

    def run(self):
        batch = []
        for index in range(50000):
            if len(batch) < 200:
                batch.append(index)
                continue
            self.message.emit(batch)
            batch = []

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.listWidget = QtGui.QListWidget()
        self.listWidget.setUniformItemSizes(True)
        self.button = QtGui.QPushButton('Start')
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.listWidget)
        layout.addWidget(self.button)
        self.worker = Worker()
        self.worker.message.connect(self.handleMessages)

    def handleMessages(self, batch):
        for message in batch:
            self.listWidget.addItem('Item (%s)' % message)

    def handleButton(self):
        if not self.worker.isRunning():
            self.worker.start()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 200, 400)
    window.show()
    sys.exit(app.exec_())