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 以便更好地理解您的代码,请删除它们。
所以,我是一个新手,正在尝试重新发明轮子并在 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 以便更好地理解您的代码,请删除它们。