python - 为什么需要 processEvents() 才能让 QThread 工作?

标签 python multithreading qt events pyside

下面是我的代码,用于列出目录的所有子目录。我用它来理解 PySide 中的 QThread 以及信号和槽。问题是,当我没有在 Main 类的 scan() 方法中使用 Qtcore.QApplication.processEvents() 时,代码不起作用。事件循环还没有运行吗?

import sys
import os
import time
from PySide import QtGui, QtCore

class Scanner(QtCore.QObject):
    folderFound = QtCore.Signal(str)
    done = QtCore.Signal()
    def __init__(self, path):
        super(Scanner, self).__init__()
        self.path = path

    @QtCore.Slot()
    def scan(self):
        for folder in os.listdir(self.path):
            time.sleep(1)
            self.folderFound.emit(folder)

class Main(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.resize(420,130)
        self.setWindowTitle('Threads')
        self.lyt = QtGui.QVBoxLayout()
        self.setLayout(self.lyt)

        self.topLyt = QtGui.QHBoxLayout()
        self.lyt.addLayout(self.topLyt)
        self.scanBtn = QtGui.QPushButton('Scan')
        self.scanBtn.clicked.connect(self.scan)
        self.clearBtn = QtGui.QPushButton('Clear')
        self.topLyt.addWidget(self.scanBtn)
        self.topLyt.addWidget(self.clearBtn)

        self.folders = list()

        self.show()

    def scan(self):
        self.th = QtCore.QThread()
        scanner = Scanner(r"D:\\")
        scanner.moveToThread(self.th)

        scanner.folderFound.connect(self.addFolder)
        scanner.done.connect(scanner.deleteLater)
        scanner.done.connect(self.quit)

        self.th.started.connect(scanner.scan)
        self.th.start()

        QtCore.QApplication.processEvents()

    @QtCore.Slot()
    def addFolder(self, folder):
        lbl = QtGui.QLabel(folder)
        self.folders.append(lbl)

        self.lyt.addWidget(lbl)

    @QtCore.Slot()
    def quit(self):
        self.th.quit()
        self.th.wait()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    main = Main()    
    app.exec_()

最佳答案

您的示例能够正常工作纯属侥幸。

当您尝试调用QtCore.QApplication.processEvents()时,将会引发NameError,因为QApplication类实际上位于QtGui 模块,而不是 QtCore 模块。但是,引发异常会产生副作用,导致您的 scanner 对象无法被垃圾收集,因此线程看起来会正常运行。

修复代码的正确方法是保留对 scanner 对象的引用,并删除不需要的 processEvents 行:

def scan(self):
    self.th = QtCore.QThread()
    # keep a reference to the scanner
    self.scanner = Scanner(r"D:\\")
    self.scanner.moveToThread(self.th)

    self.scanner.folderFound.connect(self.addFolder)
    self.scanner.done.connect(self.scanner.deleteLater)
    self.scanner.done.connect(self.quit)

    self.th.started.connect(self.scanner.scan)
    self.th.start()

其余代码没问题,该示例现在将按预期工作。

关于python - 为什么需要 processEvents() 才能让 QThread 工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48801992/

相关文章:

python - 如何使用 Tkinter 在屏幕的每个顶角分组两组按钮?

multithreading - Scala 和 Erlang 使用绿色线程吗?

java - 方法参数在 Java 中是线程安全的吗?

java - 让线程 hibernate 的更好方法

c++ - 将QMetaMethod作为参数传递给使用新QObject::connect语法的函数

python - 在 Python 3 中使用 dbm 模块

python - 通过子进程将python的文件之类的对象传递给ffmpeg

c++ - QT 防止用户输入非法数据,但使用 QCompletor

c++ - 如何避免 QlistView 中的数据在双击时被清空?

python - 如何使用不变的 URL 抓取多个页面 - python