Slot eines QThreads aus dem GUi Thread aufrufen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hi Community,

ich hab mal wieder ein kleines Problem, die Lösung ist wahrscheinlich mal wieder trivial.

Ich starte von meinem GUI Thread einen anderen Thread mit Hilfe von thread.start(). In diesem Thread starte ich nun die Event-Loop mit self.exec_().

Der Thread arbeitet ein Weilchen und emitiert ein Signal welches vom GUI Thread entgegengenommen wird. Der GUI Thread startet eine Benutzterabfrage. Anhand dieser wird entschieden ob thread.search() aufgerufen wird.

Das Problem ist, dass der Aufruf von thread.search() die GUI blockiert. Natürlich könnte ich thread.search() in eine andere Threadklasse auslagern und dann einfach thread.start() aufrufen.

Wie ich das ganze mit Signal und Slot machen würde, wüsste ich nicht auf anhieb, da thread nichts von der GUI wissen soll.

Grüße,
anogayales
Zuletzt geändert von anogayales am Freitag 25. Juni 2010, 12:45, insgesamt 1-mal geändert.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Nachtrag:

Minimalbeispiel:

Code: Alles auswählen

from PyQt4 import QtGui
from PyQt4 import QtCore


class Worker(QtCore.QThread):
    def __init__(self):
        QtCore.QThread.__init__(self)
    
    def run(self):
        print "mach was ganz kurzes"
        
    def search(self):
        for number in range(20000000):
            print number

class Example(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)
        vbox.addWidget(QtGui.QTextEdit())
        button = QtGui.QPushButton("Work")
        vbox.addWidget(button)
        
        self.thread = Worker()
        self.thread.start()
        
        self.move(250, 200)
        
        button.clicked.connect(self.thread.search)


app = QtGui.QApplication([])
exm = Example()
exm.show()
app.exec_()
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Vllt. so:

Code: Alles auswählen

class Worker(QtCore.QThread):
    def __init__(self):
        QtCore.QThread.__init__(self)
        self.search = False
    def run(self):
        print "mach was ganz kurzes"
        while True:
            if self.search:
                self.do_search()
            time.sleep(0.1)
       
    def do_search(self):
        for number in range(20000000):
            print number
        self.search = False
the more they change the more they stay the same
lunar

Wieso lagerst Du die Suche nicht einfach in einen zweiten Thread aus, welchen Du startest, wenn die Abfrage vom Benutzer positiv beschieden wurde?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@anogayales:
Ja die Dokumentation von Qt suggeriert, dass das so funktionieren könnte, dem liegt aber ein entscheidender Denkfehler zugrunde. Bei genauerem Lesen der Dokumentation wird klar, das nur der Code unter run() einen eigenen Threadkontext erhält, d.h. das Threadobjekt selbst "lebt" dort, wo es erzeugt wurde - im Hauptthread. Das gilt für alle Deklarationen ausserhalb von run(). Deshalb blockiert Dein Buttonklick die GUI, die search-Methode wird im Hauptthread ausgeführt.
Der Code unter run() fungiert als main() des neuen Threads, von dort aus kannst Du weitere Objekte erstellen und auch einen eigenen Eventhandler anstossen. Dieser "interne" Eventhandler dient dann dazu, Events von Objekten, die innerhalb von run() erzeugt wurden, zu verwalten bzw. Signale von anderen Threads mit den eigenen threadinternen Events zu sychronisieren (siehe ConnectionType für Signal/Slot-Verbindungen).

Kleines Bsp., um den Threadkontext aufzuzeigen:

Code: Alles auswählen

from PyQt4 import QtGui, QtCore

class SubThread(QtCore.QThread):
    def run(self):
        print 'SubThread.run:', self.currentThread()
        self.slotMethod()
        print 'starte SubThread event loop..'
        self.exec_()
        print 'stoppe SubThread event loop..'
    def slotMethod(self):
        print 'SubThread.slotMethod:', self.currentThread()

class MyThread(QtCore.QThread):
    def run(self):
        print 'MyThread.run:', self.currentThread()
        self.slotMethod()
        self.subthread = SubThread()
        self.subthread.start()
        print 'starte MyThread event loop..'
        self.exec_()
        print 'stoppe MyThread event loop..'
    def slotMethod(self):
        print 'MyThread.slotMethod:', self.currentThread()

app = QtGui.QApplication([])
print 'Main thread:', app.instance().thread()
thread = MyThread()
thread.start()
QtCore.QTimer.singleShot(200, thread.slotMethod)
QtCore.QTimer.singleShot(200, thread.subthread.slotMethod)
QtCore.QTimer.singleShot(2200, app.quit)
QtCore.QTimer.singleShot(2100, thread.quit)
QtCore.QTimer.singleShot(2000, thread.subthread.quit)
app.exec_()
print "stoppe Main event loop.."
ergibt:

Code: Alles auswählen

Main thread: <PyQt4.QtCore.QThread object at 0x81ae5ac>
MyThread.run: <__main__.MyThread object at 0x81ae5ac>
MyThread.slotMethod: <__main__.MyThread object at 0x81ae5ac>
SubThread.run: <__main__.SubThread object at 0x81ae5ec>
SubThread.slotMethod: <__main__.SubThread object at 0x81ae5ec>
starte MyThread event loop..
starte SubThread event loop..
MyThread.slotMethod: <PyQt4.QtCore.QThread object at 0x81ae62c>
SubThread.slotMethod: <__main__.MyThread object at 0x81ae5ac>
stoppe SubThread event loop..
stoppe MyThread event loop..
stoppe Main event loop..
Hier siehst Du, das MyThread.slotMethod() einmal in MyThread (direkter Aufruf) und einmal im Hauptthread (Signal/Slot) auftaucht. Für den direkten Aufruf ist es klar, hier entscheidet der Caller-Kontext die Threadzugehörigkeit. Für die Signal/Slot-Sache ist es dagegen wichtig zu wissen, in welchem Thread das Objekt "lebt". So zeigt der Aufruf von SubThread.slotMethod via Signal vom Hauptthread auf den Kontext MyThread, da das Objekt SubThread hier erstellt wurde. Ohne Eventhandler in MyThread wird das Signal nicht mehr ausgeliefert und die Methode nicht mehr aufgerufen.

Zurück zu Deinem Problem, die search-Methode muß in irgendeiner Weise mit run() assoziiert sein, entweder durch direkten Aufruf oder durch ein dort erzeugtes Objekt mit der entsprechenden Methode. Einfacher gehts, wie lunar angedeutet hat. ;)
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Vielen Dank für die ausführliche Klarstellung! Ich hab mich für lunars methode entschieden :P
Antworten