Saya telah mengembangkan beberapa alat pemrosesan batch sebagai plugin python untuk QGIS 1.8.
Saya menemukan bahwa ketika alat saya menjalankan GUI menjadi tidak responsif.
Kebijaksanaan umum adalah bahwa pekerjaan harus dilakukan pada utas pekerja, dengan informasi status / penyelesaian dikembalikan ke GUI sebagai sinyal.
Saya telah membaca dokumen tepi sungai , dan mempelajari sumber doGeometry.py (implementasi kerja dari ftools ).
Dengan menggunakan sumber-sumber ini saya telah mencoba membangun implementasi sederhana untuk menjelajahi fungsionalitas ini sebelum membuat perubahan pada basis kode yang sudah ada.
Struktur keseluruhan adalah entri dalam menu plugins, yang menghubungkan dialog dengan tombol start dan stop. Tombol-tombol mengontrol utas yang diperhitungkan hingga 100, mengirimkan sinyal kembali ke GUI untuk setiap nomor. GUI menerima setiap sinyal dan mengirimkan string yang berisi nomor log pesan, dan judul jendela.
Kode implementasi ini ada di sini:
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()
Sayangnya itu tidak tenang bekerja seperti yang saya harapkan:
- Judul jendela memperbarui "langsung" dengan penghitung tetapi jika saya mengklik pada dialog, itu tidak responsif.
- Log pesan tidak aktif hingga penghitung berakhir, kemudian menyajikan semua pesan sekaligus. Pesan-pesan ini ditandai dengan cap waktu oleh QgsMessageLog dan perangko waktu ini menunjukkan bahwa mereka diterima "langsung" dengan penghitung yaitu mereka tidak sedang antri oleh thread pekerja, atau dialog.
Urutan pesan dalam log (kutipan berikut) menunjukkan bahwa startButtonHandler menyelesaikan eksekusi sebelum utas pekerja mulai berfungsi yaitu utas berperilaku sebagai utas.
end: startButtonHandler Emit: starting work Emit: 0 ... Emit: 99 Emit: finshed work
Tampaknya utas pekerja tidak membagikan sumber daya apa pun dengan utas GUI. Ada beberapa baris komentar di akhir sumber di atas di mana saya mencoba memanggil msleep () dan yieldCurrentThread (), tetapi sepertinya tidak ada yang membantu.
Adakah yang punya pengalaman dengan ini dapat menemukan kesalahan saya? Saya berharap ini adalah kesalahan sederhana namun mendasar yang mudah untuk diperbaiki setelah diidentifikasi.
sumber
Jawaban:
Jadi saya melihat masalah ini lagi. Saya mulai dari awal dan sukses, kemudian kembali untuk melihat kode di atas dan masih tidak dapat memperbaikinya.
Demi memberikan contoh kerja bagi siapa pun yang meneliti subjek ini, saya akan memberikan kode fungsional di sini:
Struktur sampel ini adalah kelas ThreadManagerDialog yang dapat ditetapkan sebagai WorkerThread (atau subclass). Ketika metode jalankan dialog dipanggil, pada gilirannya akan memanggil metode doWork pada pekerja. Hasilnya adalah kode apa pun di doWork akan berjalan di utas terpisah, sehingga GUI bebas merespons input pengguna.
Dalam contoh ini contoh CounterThread ditugaskan sebagai pekerja dan beberapa progress bar akan tetap sibuk selama satu menit atau lebih.
Catatan: ini diformat sehingga siap untuk ditempelkan ke konsol python. Tiga baris terakhir harus dihapus sebelum menyimpan ke file .py.
sumber
CounterThread
hanyalah contoh sederhana dari kelas anakWorkerThread
. Jika Anda membuat kelas anak Anda sendiri dengan implementasi yang lebih bermaknadoWork
maka Anda harus baik-baik saja.