Ich habe einige Stapelverarbeitungswerkzeuge als Python-Plugins für QGIS 1.8 entwickelt.
Ich habe festgestellt, dass die GUI während der Ausführung meiner Tools nicht mehr reagiert.
Die allgemeine Weisheit ist, dass die Arbeit an einem Arbeitsthread ausgeführt werden sollte, wobei die Status- / Abschlussinformationen als Signale an die GUI zurückgegeben werden sollten.
Ich habe die Dokumente zum Flussufer gelesen und die Quelle von doGeometry.py (eine funktionierende Implementierung von ftools ) untersucht.
Mit diesen Quellen habe ich versucht, eine einfache Implementierung zu erstellen, um diese Funktionalität zu untersuchen, bevor Änderungen an einer etablierten Codebasis vorgenommen werden.
Die Gesamtstruktur ist ein Eintrag im Plugins-Menü, der einen Dialog mit Start- und Stopp-Schaltflächen führt. Die Schaltflächen steuern einen Thread, der bis 100 zählt, und senden für jede Nummer ein Signal an die GUI zurück. Die GUI empfängt jedes Signal und sendet eine Zeichenfolge, die die Nummer sowohl des Nachrichtenprotokolls als auch den Fenstertitel enthält.
Der Code dieser Implementierung ist hier:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
class ThreadTest:
def __init__(self, iface):
self.iface = iface
def initGui(self):
self.action = QAction( u"ThreadTest", self.iface.mainWindow())
self.action.triggered.connect(self.run)
self.iface.addPluginToMenu(u"&ThreadTest", self.action)
def unload(self):
self.iface.removePluginMenu(u"&ThreadTest",self.action)
def run(self):
BusyDialog(self.iface.mainWindow())
class BusyDialog(QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent)
self.parent = parent
self.setLayout(QVBoxLayout())
self.startButton = QPushButton("Start", self)
self.startButton.clicked.connect(self.startButtonHandler)
self.layout().addWidget(self.startButton)
self.stopButton=QPushButton("Stop", self)
self.stopButton.clicked.connect(self.stopButtonHandler)
self.layout().addWidget(self.stopButton)
self.show()
def startButtonHandler(self, toggle):
self.workerThread = WorkerThread(self.parent)
QObject.connect( self.workerThread, SIGNAL( "killThread(PyQt_PyObject)" ), \
self.killThread )
QObject.connect( self.workerThread, SIGNAL( "echoText(PyQt_PyObject)" ), \
self.setText)
self.workerThread.start(QThread.LowestPriority)
QgsMessageLog.logMessage("end: startButtonHandler")
def stopButtonHandler(self, toggle):
self.killThread()
def setText(self, text):
QgsMessageLog.logMessage(str(text))
self.setWindowTitle(text)
def killThread(self):
if self.workerThread.isRunning():
self.workerThread.exit(0)
class WorkerThread(QThread):
def __init__(self, parent):
QThread.__init__(self,parent)
def run(self):
self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: starting work" )
self.doLotsOfWork()
self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: finshed work" )
self.emit( SIGNAL( "killThread(PyQt_PyObject)"), "OK")
def doLotsOfWork(self):
count=0
while count < 100:
self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: " + str(count) )
count += 1
# if self.msleep(10):
# return
# QThread.yieldCurrentThread()
Leider funktioniert es nicht so ruhig, wie ich es mir erhofft hatte:
- Der Fenstertitel aktualisiert "live" mit dem Zähler, aber wenn ich auf den Dialog klicke, reagiert er nicht.
- Das Nachrichtenprotokoll ist inaktiv, bis der Zähler endet, und zeigt dann alle Nachrichten gleichzeitig an. Diese Nachrichten werden von QgsMessageLog mit einem Zeitstempel versehen. Diese Zeitstempel zeigen an, dass sie "live" mit dem Zähler empfangen wurden, dh sie werden weder vom Arbeitsthread noch vom Dialog in die Warteschlange gestellt.
Die Reihenfolge der Nachrichten im Protokoll (Auszug folgt) gibt an, dass startButtonHandler die Ausführung abschließt, bevor die Kommentare des Arbeitsthreads funktionieren, dh der Thread verhält sich wie ein Thread.
end: startButtonHandler Emit: starting work Emit: 0 ... Emit: 99 Emit: finshed work
Es scheint, dass der Worker-Thread keine Ressourcen mit dem GUI-Thread teilt. Es gibt ein paar auskommentierte Zeilen am Ende der obigen Quelle, in denen ich versucht habe, msleep () undieldCurrentThread () aufzurufen, aber keine schien zu helfen.
Kann jemand, der Erfahrung damit hat, meinen Fehler erkennen? Ich hoffe, es ist ein einfacher, aber grundlegender Fehler, der leicht zu korrigieren ist, sobald er identifiziert ist.