2013-04-26 4 views
7

Ich versuche zu verstehen, wie Signalisierung von einem Qthread zurück zu der Gui-Schnittstelle, die gestartet wurde, zu verwenden.Wie kann ich von einem laufenden QThread zurück zu dem PyQt Gui signalisieren, mit dem es gestartet wurde?

Setup: Ich habe einen Prozess (eine Simulation), die fast unbegrenzt (oder zumindest für sehr lange Zeiträume) ausgeführt werden muss. Während es läuft, führt es verschiedene Berechnungen aus, und einige der Ergebnisse müssen sein zurück an die GUI gesendet, die sie in Echtzeit entsprechend anzeigt. Ich verwende PyQt für die GUI. Ich habe ursprünglich versucht, das Python-Threading-Modul zu verwenden, und bin dann zu QThreads gewechselt, nachdem ich mehrere Beiträge sowohl hier auf SO als auch anderswo gelesen hatte.

Nach diesem Beitrag auf dem Qt Blog You're doing it wrong, ist die bevorzugte Methode, QThread zu verwenden, indem Sie ein QObject erstellen und dann zu einem Qthread verschieben. Also habe ich den Ratschlag in Hintergrund Thread mit QThread in PyQt "> diese SO Frage und versuchte eine einfache Test-App (Code unten): Es öffnet sich eine einfache GUI, lassen Sie den Hintergrundprozess starten, und es ist erforderlich, den Schritt Wert zu aktualisieren ... eine Spinnbox

Aber es funktioniert nicht Die GUI wird nie aktualisiert Was mache ich falsch

import time, sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

class SimulRunner(QObject): 
    'Object managing the simulation' 

    stepIncreased = pyqtSignal(int, name = 'stepIncreased') 
    def __init__(self): 
     super(SimulRunner, self).__init__() 
     self._step = 0 
     self._isRunning = True 
     self._maxSteps = 20 

    def longRunning(self): 
     while self._step < self._maxSteps and self._isRunning == True: 
      self._step += 1 
      self.stepIncreased.emit(self._step) 
      time.sleep(0.1) 

    def stop(self): 
     self._isRunning = False 

class SimulationUi(QDialog): 
    'PyQt interface' 

    def __init__(self): 
     super(SimulationUi, self).__init__() 

     self.goButton = QPushButton('Go') 
     self.stopButton = QPushButton('Stop') 
     self.currentStep = QSpinBox() 

     self.layout = QHBoxLayout() 
     self.layout.addWidget(self.goButton) 
     self.layout.addWidget(self.stopButton) 
     self.layout.addWidget(self.currentStep) 
     self.setLayout(self.layout) 

     self.simulRunner = SimulRunner() 
     self.simulThread = QThread() 
     self.simulRunner.moveToThread(self.simulThread) 
     self.simulRunner.stepIncreased.connect(self.currentStep.setValue) 


     self.connect(self.stopButton, SIGNAL('clicked()'), self.simulRunner.stop) 
     self.connect(self.goButton, SIGNAL('clicked()'), self.simulThread.start) 
     self.connect(self.simulRunner,SIGNAL('stepIncreased'), self.currentStep.setValue) 


if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    simul = SimulationUi() 
    simul.show() 
    sys.exit(app.exec_()) 

Antwort

7

Das Problem hier ist einfach:? Ihr SimulRunner wird niemals ein Signal gesendet, die sie verursacht starten Eine Möglichkeit, dies zu tun, wäre es, es an das started Signal des Threads anzuschließen.

A Auch in Python sollten Sie die neue Art der Verbindung von Signalen verwenden:

... 
self.simulRunner = SimulRunner() 
self.simulThread = QThread() 
self.simulRunner.moveToThread(self.simulThread) 
self.simulRunner.stepIncreased.connect(self.currentStep.setValue) 
self.stopButton.clicked.connect(self.simulRunner.stop) 
self.goButton.clicked.connect(self.simulThread.start) 
# start the execution loop with the thread: 
self.simulThread.started.connect(self.simulRunner.longRunning) 
... 
+1

Sie haben absolut Recht, ich habe gerade den Fehler erkannt. Ich habe Code aus einer früheren Version ausgeschnitten und eingefügt, in der ich Qthread als Unterklasse erstellt habe, anstatt moveToThread zu verwenden, und die starte() -Methode komplett vergessen habe. Danke, dass du es aufgezeigt hast. – stefano

+0

Dies ist ein sehr interessantes Beispiel. Folgt diesem Vorschlag jedoch, startet der Thread, aber die Stopp-Schaltfläche stoppt ihn nicht. Es wäre schön, ein funktionierendes Beispiel zu haben. Und vielleicht eine Go-Taste, die das ganze Ding neu starten kann – Fabrizio

+0

Der Stop sendet nur ein Signal, aber da 'simulThread' gerade mit' longRunning' läuft, kann dieses Signal nur verarbeitet werden, nachdem diese Methode beendet wurde, darum geht es nicht wirklich Stoppen Sie den Zähler. Um dies zu tun, müsste 'stop' von einem anderen Thread (z. B. dem Haupt-GUI-Thread) aufgerufen werden. Um den Zähler neu zu starten, müssen Sie das Flag _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | Nichtsdestotrotz habe ich ein etwas vollständigeres Beispiel [hier] (http://pastebin.com/kmvLb6Y2) gepostet (da wären noch einige Dinge, die sich stark verbessern ließen ...) – mata