Nathan W'ın işaret ettiği gibi , bunu yapmanın yolu çok iş parçacıklı olmaktır, ancak QThread'i alt sınıflamak en iyi uygulama değildir. Buraya bakın: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Aşağıda a'nın nasıl oluşturulacağına ilişkin bir örneğe bakın QObject
, sonra onu a'ya taşıyın QThread
(yani "doğru" şekilde). Bu örnek, bir vektör katmanındaki tüm özelliklerin toplam alanını hesaplar (yeni QGIS 2.0 API! 'Yı kullanarak).
İlk olarak, bizim için ağır kaldırmayı yapacak "işçi" nesnesini yaratırız:
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)
İşçiyi kullanmak için onu bir vektör katmanıyla başlatmamız, iş parçacığına taşımamız, bazı sinyalleri bağlamamız ve sonra başlatmamız gerekir. Burada neler olduğunu anlamak için yukarıda bağlantılı blog'a bakmak en iyisidir .
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()
Bu örnek birkaç önemli noktayı göstermektedir:
- İşçinin
run()
yöntemindeki her şey bir dene-hariç ifadesinin içindedir. Kodunuz bir iş parçacığının içinde çöktüğünde kurtarmak zordur. Geri izlemeyi genellikle bağlandığım hata sinyali aracılığıyla yayar QgsMessageLog
.
- Bitmiş sinyal, bağlı yönteme, işlemin başarıyla tamamlanıp tamamlanmadığını ve sonucunu bildirir.
- İlerleme sinyali, her özellik için bir defa yerine yalnızca tamamlanma yüzdesi değiştiğinde çağrılır. Bu, ilerleme çubuğunu yavaşlatmak için çok fazla çağrı yapılmasını önler, bu da çalışanı başka bir iş parçacığında çalıştırmanın tüm noktasını yener: hesaplamayı kullanıcı arabiriminden ayırmak için.
- Çalışan
kill()
, işlevin zarif bir şekilde sonlandırılmasına izin veren bir yöntem uygular . terminate()
Yöntemi kullanmaya çalışmayın QThread
- kötü şeyler olabilir!
Eklentinizin yapısında thread
ve worker
herhangi bir yerindeki nesneleri izlediğinizden emin olun . Eğer istemezsen kızar. Bunu yapmanın en kolay yolu, bunları oluşturduğunuzda bunları iletişim kutunuzda saklamaktır, örneğin:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
Veya Qt'nin QThread'in mülkiyetini almasına izin verebilirsiniz:
thread = QtCore.QThread(self)
Bu şablonu bir araya getirmek için tüm dersleri kazmak uzun zaman aldı, ama o zamandan beri her yerde yeniden kullanıyorum.