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.
worker.progress.connect(self.ui.progressBar)
in etwas anderes geändert , aber jedes Mal, wenn ich es ausführe, stürzt qgis-bin ab. Ich habe keine Erfahrung mit dem Debuggen von Python-Code oder QGIS. Alles was ich bekomme ist,Access violation reading location 0x0000000000000008
dass es so aussieht, als ob etwas null ist. Fehlt ein Setup-Code, um diesen in einem Verarbeitungsskript verwenden zu können?Ihr einzig wahrer Weg, dies zu tun, ist Multithreading.
Einige zusätzliche Informationen http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Hinweis Einige Leute erben nicht gerne von QThread, und anscheinend ist dies nicht die "richtige" Methode, aber es funktioniert so ...
quelle
Da diese Frage relativ alt ist, verdient sie ein Update. Mit QGIS 3 gibt es einen Ansatz mit QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () und QgsApplication.taskManager (). AddTask ().
Mehr dazu zum Beispiel unter Verwenden von Threads in PyQGIS3 VON MARCO BERNASOCCHI
quelle