PyQt | Signale werden nicht in QThread, sondern im Hauptthread verarbeitet

In diesem einfachen PyQt-Demoprogramm sende ich Signale vom Hauptthread aus. In einem Worker-Thread verbinde ich mich mit ihnen, aber die Signal-Handler werden im Haupt-Thread ausgeführt:

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

Das Ergebnis zeigt jedoch, dass die Rückrufe im Hauptthread ausgeführt werden:

[<_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

Was mache ich falsch? Leider sind die PyQt-Dokumente dazu sehr unvollständig und widersprüchlich.

Ich erhalte ähnliche Ergebnisse, wenn ich das verwendemoveToThread Technik:

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

Was in ... endet:

[<_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

Antworten auf die Frage(1)

Ihre Antwort auf die Frage