我一直在开发一些批处理工具作为QGIS 1.8的python插件。
我发现在我的工具运行时,GUI变得无响应。
一般的看法是,应该在工作线程上完成工作,并将状态/完成信息作为信号传递回GUI。
我已经通过阅读河岸文档,并研究doGeometry.py源(从工作实施ftools)。
使用这些资源,我尝试构建一个简单的实现,以便在更改已建立的代码库之前探索此功能。
整体结构是“插件”菜单中的一个条目,它展开了一个带有“开始”和“停止”按钮的对话框。这些按钮控制着一个计数为100的线程,将每个数字的信号发送回GUI。GUI接收每个信号,然后发送一个字符串,其中包含消息日志和窗口标题的编号。
此实现的代码在这里:
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()
不幸的是,它并不是我所希望的那样安静:
- 窗口标题正在使用计数器更新“实时”,但是如果单击对话框,它将无响应。
- 直到计数器结束,消息日志才处于活动状态,然后立即显示所有消息。这些消息由QgsMessageLog用时间戳标记,并且这些时间戳指示它们是通过计数器“实时”接收的,即它们没有被辅助线程或对话框排队。
日志中消息的顺序(后面是摘录)表示startButtonHandler在工作线程开始工作之前完成了执行,即该线程表现为线程。
end: startButtonHandler Emit: starting work Emit: 0 ... Emit: 99 Emit: finshed work
似乎工作线程没有与GUI线程共享任何资源。在以上源代码的末尾有几行注释掉的行,我尝试调用msleep()和yieldCurrentThread(),但似乎都没有帮助。
有任何经验的人都能发现我的错误吗?我希望这是一个简单但基本的错误,一旦被识别,就可以轻松纠正。