GUI in einem thread ausführen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Poseidonius
User
Beiträge: 63
Registriert: Montag 23. Januar 2006, 08:58

Hallo zusammen,

allen noch ein erfolgreiches neues Jahr mit vielen in Erfüllung gehenden Wünschen !

Ich bastle an einer Applikation, zu der der User verschiedene Ausgabemodule einhängen kann (Logger, Konsole, externes Display usw.) Nun wollte ich auch Klasse bereitstellen, mit der man ohne Änderungen am Hauptprogramm eine QtGUI hinzufügen kann, die neben der Anwendung in einem sepraten Thread läuft und bei bestimmten Triggerungen eine entsprechende Ausgabe fährt. Nun bekomme ich es noch nicht einmal hin die GUI (sicher im Unterschied zur übliche Verwendung) in einem QtThread anzuwerfen, sprich das Qt Fenster wird angezeigt und die Schleife in der Main beginnt zu laufen ...

Das unten beigefügte Beispiel zeigt das genau gegenteilige Verhalten, die for Schleife wartet, bis das Fenster geschlossen wurde.

Bin wie immer dankbar für alle Hinweise und Kritiken

Poseidonius

Code: Alles auswählen

from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QThread
import sys

import time
 
class QtFrame(QtGui.QMainWindow, QThread):
    def __init__(self):
        QThread.__init__(self)        
        QtGui.QMainWindow.__init__(self)
        self.setWindowTitle("Window started in a separate thread")
        self.resize(300, 300)
        
    def run(self, app):
        self.show()
        print "show gui"
        app.exec_()
        print "leaving thread"

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    ui=QtFrame()
    ui.run(app)
    
    for i in range(3):
        print "main running", " -- ", i
        time.sleep(1)
lunar

Die Manipulation der graphischen Oberfläche ist einzig und allein dem Hauptthread vorbehalten. Es ist nicht möglich, Steuerelemente in anderen Threads zu erzeugen. Es ist mehr oder weniger Glück, dass dieses Beispiel überhaupt funktioniert und nicht einfach abstürzt.

Steht im Übrigen alles in der Dokumentation über Threads in Qt.
passe.partout
User
Beiträge: 11
Registriert: Donnerstag 9. September 2010, 10:33

Mit etwas KnowHow kann man die Gui jedoch auch in einem anderen Thread legen und ohne Probleme mit anderen Dingen im Hauptthread fortfahren.

Code: Alles auswählen

from PyQt4 import QtCore, QtGui
import sys
import time
import threading

class QtFrame(QtGui.QMainWindow):
  def __init__(self):
    pass
       
  def initialize(self, app):
    QtGui.QMainWindow.__init__(self)
    self.setWindowTitle("Window started in a separate thread")
    self.resize(300, 300)
    self.show()
    print "show gui"
    app.exec_()
    print "leaving thread"
        
  def test(self, x):
    print "gui-test", x

class worker(threading.Thread): 
  def __init__(self, gui): 
    threading.Thread.__init__(self) 
    self.gui = gui
 
  def run(self):
    app = QtGui.QApplication(sys.argv)
    self.gui.initialize(app)

if __name__ == "__main__":
  ui=QtFrame()
  guiWorker=worker(ui)
  guiWorker.start()
   
  for i in range(10):
    print "main running", " -- ", i
    time.sleep(1)
    ui.test(i)
Bei dieser Lösung ist zu beachten, dass zwar ein Objekt vom Type QtFrame in der main-Funktion erzeugt wird, instanziert wird es aber erst richtig im worker-Thread durch aufruf der Methode initialize(). Du kannst nach dem Start des Threads ohne Probleme in deinem ursprünglichen Programm fortfahren. Bei dieser Lösung ist jedoch darauf zu auchten, dass so nur eine Gui erzeugt werden kann.

Gruß Passe
lunar

@passe.partout: Nach Dokumentation (Starting Threads with QThread, letzter Absatz) ist nicht garantiert, dass das funktioniert:
Note that QCoreApplication::exec() must always be called from the main thread (the thread that executes main()), not from a QThread. In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations.
Auf welchen Systemen hast Du diese Lösung probiert?
passe.partout
User
Beiträge: 11
Registriert: Donnerstag 9. September 2010, 10:33

Linux
Poseidonius
User
Beiträge: 63
Registriert: Montag 23. Januar 2006, 08:58

Hat auch unter Windows ohne Probleme funktioniert.

Dankeschön passe.partout
BlackJack

@Poseidonius: Das klingt jetzt so als wenn Du das tatsächlich so machst: Dein Programm ist damit aber fehlerhaft. Es ist nicht garantiert, dass das funktioniert und auch wenn es scheinbar läuft, kann es irgendwann sporadisch Probleme bis hin zu harten Abstürzen geben.
passe.partout
User
Beiträge: 11
Registriert: Donnerstag 9. September 2010, 10:33

Glaube ich nicht :wink:
lunar

@passe.partout: Was Du glaubst, ist egal. Wichtig ist, was in der Dokumentation steht, und die garantiert eben nicht, dass dergleichen funktioniert.
Antworten