pyqt - PyQt | 信号不是在QThread中处理的,而是在主线程中处理

  显示原文与译文双语对照的内容

在这个简单的PyQt演示程序中,我从主线程发出。 在工作线程中,我连接到它们,但信号处理程序在主线程中运行:


from PyQt4 import QtGui, QtCore


import threading


from time import sleep


import sys



class Data():


 def __init__(self, a, b):


 self.a = a


 self.b = b



 def __str__(self):


 return"Data having %d and %d" % (self.a, self.b)



class Worker(QtCore.QThread):


 def __init__(self, parent):


 QtCore.QThread.__init__(self)


 self.p = parent



 def run(self):


 self.connect(self.p, QtCore.SIGNAL("newTask"), self.task)


 print"[%s] running exec_()" % threading.currentThread()


 self.exec_()



 def task(self, dataobj):


 print"[%s] Processing" % threading.currentThread(), dataobj


 sleep(3)


 print"Done with", dataobj


 self.emit(QtCore.SIGNAL("taskDone"), str(dataobj))



class App(QtCore.QObject):


 def __init__(self):


 QtCore.QObject.__init__(self)


 self.w = Worker(self)


 self.connect(self.w, QtCore.SIGNAL("taskDone"), self.on_task_done)


 self.w.start()



 def assign_tasks(self):


 self.emit(QtCore.SIGNAL("newTask"), Data(3, 4))


 self.emit(QtCore.SIGNAL("newTask"), Data(5, 6))


 print"[%s] Tasks sent" % threading.currentThread()



 @staticmethod


 def on_task_done(objstr):


 print"[%s] App: Worker finished with" % threading.currentThread(), objstr



if __name__ == '__main__':


 app = QtGui.QApplication(sys.argv)


 a = App()


 sleep(1)


 a.assign_tasks()


 sleep(20)


 sys.exit(app.exec_())



但是结果显示回调是在主线程中运行的:


[<_DummyThread(Dummy-1, started daemon 105564)>] running exec_()


[<_MainThread(MainThread, started 105612)>] Processing Data having 3 and 4


Done with Data having 3 and 4


[<_MainThread(MainThread, started 105612)>] App: Worker finished with Data having 3 and 4


[<_MainThread(MainThread, started 105612)>] Processing Data having 5 and 6


Done with Data having 5 and 6


[<_MainThread(MainThread, started 105612)>] App: Worker finished with Data having 5 and 6


[<_MainThread(MainThread, started 105612)>] Tasks sent



我做错什么了? 不幸的是,PyQt文档是非常不完整和矛盾的。

如果使用 moveToThread 技术,我将得到类似的结果:


from PyQt4 import QtGui, QtCore


import threading


from time import sleep


import sys



class Data():


 def __init__(self, a, b):


 self.a = a


 self.b = b



 def __str__(self):


 return"Data having %d and %d" % (self.a, self.b)



class Worker(QtCore.QObject):


 def __init__(self, parent):


 QtCore.QObject.__init__(self)


 self.connect(parent, QtCore.SIGNAL("newTask"), self.task)



 def task(self, dataobj):


 print"[%s] Processing" % threading.currentThread(), dataobj


 sleep(3)


 print"Done with", dataobj


 self.emit(QtCore.SIGNAL("taskDone"), str(dataobj))



 def start(self):


 print"[%s] start()" % threading.currentThread()



class App(QtCore.QObject):


 def __init__(self):


 QtCore.QObject.__init__(self)



 self.w = Worker(self)


 self.t = QtCore.QThread(self)


 self.w.moveToThread(self.t)


 self.connect(self.w, QtCore.SIGNAL("taskDone"), self.on_task_done)


 self.connect(self.t, QtCore.SIGNAL("started()"), self.w.start)


 self.t.start()



 def assign_tasks(self):


 self.emit(QtCore.SIGNAL("newTask"), Data(3, 4))


 self.emit(QtCore.SIGNAL("newTask"), Data(5, 6))


 print"[%s] Tasks sent" % threading.currentThread()



 @staticmethod


 def on_task_done(objstr):


 print"[%s] App: Worker finished with" % threading.currentThread(), objstr



if __name__ == '__main__':


 app = QtGui.QApplication(sys.argv)


 a = App()


 sleep(1)


 a.assign_tasks()


 sleep(20)


 sys.exit(app.exec_())



结果是:


[<_DummyThread(Dummy-1, started daemon 108992)>] start()


[<_MainThread(MainThread, started 107004)>] Processing Data having 3 and 4


Done with Data having 3 and 4


[<_MainThread(MainThread, started 107004)>] App: Worker finished with Data having 3 and 4


[<_MainThread(MainThread, started 107004)>] Processing Data having 5 and 6


Done with Data having 5 and 6


[<_MainThread(MainThread, started 107004)>] App: Worker finished with Data having 5 and 6


[<_MainThread(MainThread, started 107004)>] Tasks sent



时间: 原作者:

在主线程中 Worker 对象'实时',这意味着它们的所有信号都将由线程循环的主事件处理。 这些对象是 QThread的事实并没有改变。

如果希望信号由其他线程处理,首先需要使用该线程的moveToThread 方法将worker对象移动到该线程。

因此,在你的情况下,只有 run 方法在不同线程中执行,方法仍在主线程中执行。 改变这一切的方法是:

  • 使你的Worker 成为常规 QObject,而不是 QThread
  • App 中创建一个 QThread,启动它并将工作线程移到该线程
  • 然后将信号发送给工作人员,使它的开始处理

你应该检查这些引用:

编辑:

我在你的代码中注意到了一些其他事项:

  • 你正在混合 python 线程和qt线程。 threading.currentThread 不能正确地反映当前qt线程。 将 QThread.currentThread() 用于。
  • 将你称为 pyqtSlots的槽装饰为,这样可以能会导致这样的问题。
  • 使用新样式信号。 旧风格信号不再支持 PyQt5,新的样式信号使用起来更容易和更好。

下面是应该工作的代码版本:


from PyQt4 import QtGui, QtCore


import threading


from time import sleep


import sys



class Data():


 def __init__(self, a, b):


 self.a = a


 self.b = b



 def __str__(self):


 return"Data having %d and %d" % (self.a, self.b)



class Worker(QtCore.QObject):



 taskDone = QtCore.pyqtSignal(str)



 def __init__(self, parent):


 QtCore.QObject.__init__(self)


 parent.newTask.connect(self.task)



 @QtCore.pyqtSlot(object)


 def task(self, dataobj):


 print"[%s] Processing" % QtCore.QThread.currentThread().objectName(), dataobj


 sleep(3)


 print"Done with", dataobj


 self.taskDone.emit(str(dataobj))



 @QtCore.pyqtSlot()


 def start(self):


 print"[%s] start()" % QtCore.QThread.currentThread().objectName()



class App(QtCore.QObject):



 newTask = QtCore.pyqtSignal(object)



 def __init__(self):


 QtCore.QObject.__init__(self)


 self.w = Worker(self)


 self.t = QtCore.QThread(self, objectName='workerThread')


 self.w.moveToThread(self.t)


 self.w.taskDone.connect(self.on_task_done)


 self.t.started.connect(self.w.start)


 self.t.start()



 def assign_tasks(self):


 self.newTask.emit(Data(3, 4))


 self.newTask.emit(Data(5, 6))


 print"[%s] Tasks sent" % QtCore.QThread.currentThread().objectName()



 @staticmethod


 def on_task_done(objstr):


 print"[%s] App: Worker finished with" % QtCore.QThread.currentThread().objectName(), objstr



if __name__ == '__main__':


 app = QtGui.QApplication(sys.argv)


 QtCore.QThread.currentThread().setObjectName('main')


 a = App()


 sleep(1)


 a.assign_tasks()


 from utils import sigint


 sys.exit(app.exec_())



我已经设置了线程的objectName,以使输出更好的可读性:

[workerThread] start()
[main] Tasks sent
[workerThread] Processing Data having 3 and 4
Done with Data having 3 and 4
[workerThread] Processing Data having 5 and 6
[main] App: Worker finished with Data having 3 and 4
Done with Data having 5 and 6
[main] App: Worker finished with Data having 5 and 6
原作者:
...