PyQt/PySide2 QThread 实现问题,我试图将另一个 class 的方法传递给线程 class 构造函数

PyQt/PySide2 problem with QThread implementation where I'm trying to pass a method of another class to the thread class constructor

所以,我是一个新手,正在尝试重新发明轮子并在 PySide2 中以正确的方式进行线程化。 我的问题是我的线程似乎启动得很好,但我没有从它应该执行的函数中得到任何输出,我不明白为什么。

这里是我如何设置的简要说明和一个最小的可重现示例。 我有一个文件,假设它的名称为 "so_helpers" 用于我的演示。里面有 2 个 类,一个是我正在尝试构建的名为 "External" 的 QThread 和 运行(显然不是,因为它不起作用)和另一个是 Updater,我试图将其方法传递给 External 来执行。这是我的代码 "so_helpers":

from PySide2.QtCore import QThread, Signal

class Updater: 
    def __init__(self):
        print("connection to db established")

    def process(self):
        print("started")
        print("doing the update")
        print("finished")

class External(QThread):
    finished = Signal()

    def __init__(self, func):
        super().__init__()
        self.func = func

    def run(self):
        self.func()
        self.finished.emit()

这是我的 ui,为了这个例子的目的,我们称它为 "for_SO_ui"。它是由 pyuic5 从 .ui 文件自动生成的:

from PySide2 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(644, 187)
        MainWindow.setMinimumSize(QtCore.QSize(644, 187))
        MainWindow.setMaximumSize(QtCore.QSize(644, 187))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(80, 30, 491, 111))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "THREADING IS AWESOME!"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

这里是 main.py 所有这些都应该放在一起的地方(但它没有,这让我很难过):

from so_helpers import *
from for_SO_ui import *
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import QThread, Signal
import sys

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    startThread = Signal()
    threadDone = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignals()

    def connectSignals(self):
        self.pushButton.clicked.connect(self.onButtonClick)
        self.startThread.connect(self.onThreadStart)
        self.threadDone.connect(self.onThreadDone)

    def onButtonClick(self):
        self.startThread.emit()

    def onThreadStart(self):
        self.thread_object = External(self.threadFunction)
        self.thread = QThread()
        self.thread_object.moveToThread(self.thread)
        self.thread_object.finished.connect(self.onThreadDone)
        self.thread.start()

    def onThreadDone(self):
        self.thread.start()

    def threadFunction(self):
        func = Updater.process
        return func

    def closeEvent(self, event):
        self.thread.quit()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ui = MainWindow()
    ui.show()
    sys.exit(app.exec_())

非常感谢任何帮助、解释和建议!

您的代码存在多个问题。

首先,External已经是一个QThread子class,没必要再新建一个线程放入。即使在那种情况下,无论何时将 QObject 移动到另一个线程,您都应该将线程的 started 信号连接到应该执行的实际函数(在您的情况下,run)。

调用 self.func() 不是 运行 process 函数,而是 threadFunction,它只是 returns Updater.process.

然后,您想要访问一个函数,但您返回的是未绑定的 class 方法(Updater.process),而不是实例方法,因此调用它会导致异常,因为process 至少需要一个参数 (self)。
您可以使用 @staticmethod(并删除 self),或创建 class.

的实例

最后,QThread 已经有一个 finished() 信号,你不应该覆盖它,也不应该使用你自己的信号来重启线程:当你发出你自己的信号时,线程仍然是 运行ning,结果是可能不会像文档中解释的那样再次启动 QThread.start():

[...] If the thread is already running, this function does nothing.

所以你要么使用原来的QThread finished信号,要么你使用QThread.wait()等待完成再启动。

这可能是对您的代码的更正:

class External(QThread):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def run(self):
        self.func()


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    startThread = pyqtSignal()
    threadDone = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignals()

    def connectSignals(self):
        self.pushButton.clicked.connect(self.onButtonClick)
        self.startThread.connect(self.onThreadStart)
        self.threadDone.connect(self.onThreadDone)

    def onButtonClick(self):
        self.startThread.emit()

    def onThreadStart(self):
        self.thread = External(self.threadFunction())
        self.thread.finished.connect(self.onThreadDone)
        self.thread.start()

    def onThreadDone(self):
        self.thread.start()

    def threadFunction(self):
        self.updater = Updater()
        return self.updater.process

    def closeEvent(self, event):
        self.thread.finished.disconnect(self.onThreadDone)
        self.thread.quit()

PS:供将来参考:如果不需要 docstrings/comments 以便更好地理解您的代码,请删除它们。