Como señala Nathan W , la forma de hacerlo es con subprocesos múltiples, pero subclasificar QThread no es la mejor práctica. Ver aquí: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Vea a continuación un ejemplo de cómo crear un QObject
, luego muévalo a un QThread
(es decir, la forma "correcta" de hacerlo). Este ejemplo calcula el área total de todas las características en una capa vectorial (¡usando la nueva API QGIS 2.0!).
Primero, creamos el objeto "trabajador" que hará el trabajo pesado por nosotros:
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)
Para usar el trabajador necesitamos inicializarlo con una capa vectorial, moverlo al hilo, conectar algunas señales y luego iniciarlo. Probablemente sea mejor mirar el blog vinculado anteriormente para comprender lo que está sucediendo aquí.
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()
Este ejemplo ilustra algunos puntos clave:
- Todo dentro del
run()
método del trabajador está dentro de una declaración try-except. Es difícil recuperarse cuando su código se bloquea dentro de un hilo. Emite el rastreo a través de la señal de error, que generalmente conecto con el QgsMessageLog
.
- La señal finalizada le dice al método conectado si el proceso se completó con éxito, así como el resultado.
- La señal de progreso solo se llama cuando cambia el porcentaje completado, en lugar de una vez para cada función. Esto evita demasiadas llamadas para actualizar la barra de progreso, lo que ralentiza el proceso del trabajador, lo que anularía el punto de ejecutar el trabajador en otro hilo: separar el cálculo de la interfaz de usuario.
- El trabajador implementa un
kill()
método que permite que la función finalice correctamente. No intentes usar el terminate()
método QThread
, ¡podrían pasar cosas malas!
Asegúrese de realizar un seguimiento de sus objetos thread
y worker
en algún lugar de su estructura de complementos. Qt se enoja si no lo haces. La forma más fácil de hacer esto es almacenarlos en su diálogo cuando los cree, por ejemplo:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
O puede dejar que Qt tome posesión del QThread:
thread = QtCore.QThread(self)
Me llevó mucho tiempo desenterrar todos los tutoriales para armar esta plantilla, pero desde entonces la he reutilizado por todas partes.
worker.progress.connect(self.ui.progressBar)
a algo más, pero cada vez que lo ejecuto qgis-bin se bloquea. No tengo experiencia depurando código python o qgis. Todo lo que obtengo esAccess violation reading location 0x0000000000000008
que parece que algo es nulo. ¿Falta algún código de configuración para poder usar esto en un script de procesamiento?Su única forma verdadera de hacerlo es mediante subprocesos múltiples.
Algunas lecturas adicionales http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Nota A algunas personas no les gusta heredar de QThread, y aparentemente esta no es la forma "correcta" de hacerlo, pero funciona así ...
fuente
Como esta pregunta es relativamente antigua, merece una actualización. Con QGIS 3 hay un acercamiento con QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () y QgsApplication.taskManager (). AddTask ().
Más sobre esto, por ejemplo, en Usar hilos en PyQGIS3 POR MARCO BERNASOCCHI
fuente