Wie Nathan W betont, ist der Weg, dies zu tun, Multithreading, aber die Unterklasse von QThread ist keine bewährte Methode. Siehe hier: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Unten sehen Sie ein Beispiel, wie Sie ein erstellen QObject
und dann auf ein verschieben QThread
(dh die "richtige" Vorgehensweise). In diesem Beispiel wird die Gesamtfläche aller Features in einer Vektorebene berechnet (mithilfe der neuen QGIS 2.0-API!).
Zuerst erstellen wir das "Arbeiter" -Objekt, das das schwere Heben für uns erledigt:
class Worker(QtCore.QObject):
def __init__(self, layer, *args, **kwargs):
QtCore.QObject.__init__(self, *args, **kwargs)
self.layer = layer
self.total_area = 0.0
self.processed = 0
self.percentage = 0
self.abort = False
def run(self):
try:
self.status.emit('Task started!')
self.feature_count = self.layer.featureCount()
features = self.layer.getFeatures()
for feature in features:
if self.abort is True:
self.killed.emit()
break
geom = feature.geometry()
self.total_area += geom.area()
self.calculate_progress()
self.status.emit('Task finished!')
except:
import traceback
self.error.emit(traceback.format_exc())
self.finished.emit(False, self.total_area)
else:
self.finished.emit(True, self.total_area)
def calculate_progress(self):
self.processed = self.processed + 1
percentage_new = (self.processed * 100) / self.feature_count
if percentage_new > self.percentage:
self.percentage = percentage_new
self.progress.emit(self.percentage)
def kill(self):
self.abort = True
progress = QtCore.pyqtSignal(int)
status = QtCore.pyqtSignal(str)
error = QtCore.pyqtSignal(str)
killed = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal(bool, float)
Um den Worker zu verwenden, müssen wir ihn mit einer Vektorebene initialisieren, in den Thread verschieben, einige Signale verbinden und dann starten. Am besten schauen Sie sich den oben verlinkten Blog an, um zu verstehen, was hier vor sich geht.
thread = QtCore.QThread()
worker = Worker(layer)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.progress.connect(self.ui.progressBar)
worker.status.connect(iface.mainWindow().statusBar().showMessage)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
worker.finished.connect(thread.quit)
thread.start()
Dieses Beispiel zeigt einige wichtige Punkte:
- Alles in der
run()
Methode des Workers befindet sich in einer try-without-Anweisung. Es ist schwierig, sich zu erholen, wenn Ihr Code in einem Thread abstürzt. Es gibt den Traceback über das Fehlersignal aus, das ich normalerweise mit dem verbinde QgsMessageLog
.
- Das fertige Signal teilt der verbundenen Methode mit, ob der Prozess erfolgreich abgeschlossen wurde, sowie das Ergebnis.
- Das Fortschrittssignal wird nur aufgerufen, wenn sich der Prozentsatz der Fertigstellung ändert, und nicht einmal für jede Funktion. Dies verhindert, dass zu viele Aufrufe zum Aktualisieren des Fortschrittsbalkens den Arbeitsprozess verlangsamen, was den gesamten Punkt der Ausführung des Arbeiters in einem anderen Thread zunichte machen würde: um die Berechnung von der Benutzeroberfläche zu trennen.
- Der Worker implementiert eine
kill()
Methode, mit der die Funktion ordnungsgemäß beendet werden kann. Versuchen Sie nicht, die terminate()
Methode anzuwenden QThread
- es können schlimme Dinge passieren!
Achten Sie darauf, Ihre thread
und worker
Objekte irgendwo in Ihrer Plugin-Struktur zu verfolgen . Qt wird wütend, wenn Sie nicht. Der einfachste Weg, dies zu tun, besteht darin, sie in Ihrem Dialog zu speichern, wenn Sie sie erstellen, z.
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
Oder Sie können Qt das Eigentum an QThread übertragen lassen:
thread = QtCore.QThread(self)
Ich habe lange gebraucht, um alle Tutorials zu finden, um diese Vorlage zusammenzustellen, aber seitdem habe ich sie überall wiederverwendet.