Я розробляв деякі інструменти пакетної обробки як плагіни python для QGIS 1.8.
Я виявив, що під час роботи моїх інструментів графічний інтерфейс не реагує.
Загальна мудрість полягає в тому, що роботу слід виконувати на робочій нитці, а інформація про стан / завершення передається в графічний інтерфейс як сигнали.
Я читав документи річкового берега та вивчав джерело 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 () та returnCurrentThread (), але ні один з них не допоміг.
Хто-небудь, хто має досвід цього, може помітити мою помилку? Я сподіваюся, що це проста, але принципова помилка, яку легко виправити, як тільки вона виявиться.