In this simple PyQt demo program, I emit signals from the main thread. In a worker thread I connect to them, but the signal handlers are run in the main thread:
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_())
But the result reveals that the callbacks are run in the main thread:
[<_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
What am I doing wrong? Unfortunately, the PyQt docs on this are very incomplete and contradicting.
I'm getting similar results if I use the moveToThread
technique:
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_())
Which results in:
[<_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
Your
Worker
objects 'live' in the main thread, that means all their signals will be handled by the main thread's event loop. The fact that these objects areQThread
s doesn't change that.If you want signals to be processed by a different thread, you first need to move the worker object to that thread by using it's
moveToThread
method.So in your case, only the
run
method is acutally executed in a different thread, thetask
method is still executed in the main thread. A way of changing this would be:Worker
a regularQObject
, not aQThread
QThread
in yourApp
, start it and move the worker to that threadAnd you should check out these references:
edit:
A few other things I noticed in your code:
threading.currentThread
will not correctly reflect the current qt thread. useQThread.currentThread()
for that.pyqtSlots
, not doing so can be a cause of problems like these.So here is a version of your code that should work:
I've set the thread's
objectName
s to make the output better readable: