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

2024-05-15 12:12:21 发布

您现在位置:Python中文网/ 问答频道 /正文

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

下面是我如何设置的一个简短解释,以及一个最小的可复制示例。 我有一个文件,比如说它叫做“sou helpers”,用于演示。内部有两个类,一个是名为“External”的QThread,我试图以智能方式构造和运行它(显然不是,因为它不工作),另一个是Updater,我试图将其方法传递给外部执行。以下是我的“so_助手”的代码:

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_())

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


Tags: fromimportselfuisignaldefsysthread
1条回答
网友
1楼 · 发布于 2024-05-15 12:12:21

您的代码存在多个问题

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

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

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

最后,QThread已经有一个finished()信号,您不应该覆盖它,也不应该使用您自己的信号来重新启动线程:当您发出自己的信号时,线程仍在运行,结果可能是它不会再次启动,如有关^{}的文档中所述:

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

因此,您可以使用原始的QThread finished信号,或者使用^{}等待完成,然后再次启动它

这是对代码的可能更正:

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:供将来参考:如果为了更好地理解代码而不需要文档字符串/注释,请删除它们。

相关问题 更多 >