Comme le souligne Nathan W , la façon de le faire est d'utiliser le multithreading, mais le sous-classement de QThread n'est pas la meilleure pratique. Voir ici: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Voir ci-dessous un exemple de la façon de créer un QObject
, puis de le déplacer vers un QThread
(c'est-à-dire la manière "correcte" de le faire). Cet exemple calcule la surface totale de toutes les entités d'une couche vectorielle (à l'aide de la nouvelle API QGIS 2.0!).
Tout d'abord, nous créons l'objet "travailleur" qui fera le gros du travail pour nous:
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)
Pour utiliser l'ouvrier, nous devons l'initaliser avec un calque vectoriel, le déplacer vers le thread, connecter certains signaux, puis le démarrer. Il est probablement préférable de consulter le blog lié ci-dessus pour comprendre ce qui se passe ici.
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()
Cet exemple illustre quelques points clés:
- Tout à l'intérieur de la
run()
méthode de l'ouvrier est à l'intérieur d'une instruction try-except. Il est difficile de récupérer lorsque votre code se bloque dans un thread. Il émet la trace via le signal d'erreur, que je connecte habituellement au QgsMessageLog
.
- Le signal terminé indique à la méthode connectée si le processus s'est terminé avec succès, ainsi que le résultat.
- Le signal de progression n'est appelé que lorsque le pourcentage d'achèvement change, plutôt qu'une fois pour chaque fonctionnalité. Cela empêche trop d'appels pour mettre à jour la barre de progression, ce qui ralentit le processus de travail, ce qui irait à l'encontre de l'intérêt de l'exécution du travailleur dans un autre thread: séparer le calcul de l'interface utilisateur.
- L'ouvrier implémente une
kill()
méthode qui permet à la fonction de se terminer correctement. N'essayez pas d'utiliser la terminate()
méthode dans QThread
- de mauvaises choses pourraient arriver!
Assurez-vous de garder une trace de vos objets thread
et worker
quelque part dans la structure de votre plugin. Qt se met en colère si vous ne le faites pas. La façon la plus simple de le faire est de les stocker dans votre boîte de dialogue lorsque vous les créez, par exemple:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
Ou vous pouvez laisser Qt s'approprier le QThread:
thread = QtCore.QThread(self)
Il m'a fallu beaucoup de temps pour déterrer tous les tutoriels afin de rassembler ce modèle, mais depuis lors, je l'ai réutilisé partout.
worker.progress.connect(self.ui.progressBar)
pour autre chose, mais chaque fois que je l'exécute, qgis-bin plante. Je n'ai aucune expérience du débogage de code python ou qgis. Tout ce que je reçois c'estAccess violation reading location 0x0000000000000008
qu'il semble que quelque chose soit nul. Y a-t-il du code de configuration qui manque pour pouvoir l'utiliser dans un script de traitement?Votre seule véritable façon de procéder est le multithreading.
Quelques lectures supplémentaires http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Remarque Certaines personnes n'aiment pas hériter de QThread, et apparemment ce n'est pas la manière "correcte" de le faire, mais cela fonctionne donc ....
la source
Cette question étant relativement ancienne, elle mérite une mise à jour. Avec QGIS 3, il existe une approche avec QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () et QgsApplication.taskManager (). AddTask ().
Plus d'informations à ce sujet, par exemple à l' aide de threads dans PyQGIS3 PAR MARCO BERNASOCCHI
la source