Como Nathan W aponta, a maneira de fazer isso é com multithreading, mas a subclasse de QThread não é a melhor prática. Veja aqui: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Veja abaixo um exemplo de como criar um QObject
e mova-o para um QThread
(ou seja, a maneira "correta" de fazê-lo). Este exemplo calcula a área total de todos os recursos em uma camada vetorial (usando a nova API do QGIS 2.0!).
Primeiro, criamos o objeto "worker" que fará o trabalho pesado para nós:
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 o trabalhador, precisamos inicializá-lo com uma camada vetorial, mova-o para o encadeamento, conecte alguns sinais e inicie-o. Provavelmente, é melhor consultar o blog vinculado acima para entender o que está acontecendo aqui.
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 exemplo ilustra alguns pontos principais:
- Tudo dentro do
run()
método do trabalhador está dentro de uma instrução try-except. É difícil recuperar quando seu código falha dentro de um thread. Emite o rastreio através do sinal de erro, ao qual costumo conectar QgsMessageLog
.
- O sinal finalizado informa ao método conectado se o processo foi concluído com êxito, bem como o resultado.
- O sinal de progresso é chamado apenas quando a porcentagem concluída é alterada, em vez de uma vez para cada recurso. Isso evita muitas chamadas para atualizar a barra de progresso, retardando o processo do trabalhador, o que anularia todo o ponto de execução do trabalhador em outro encadeamento: para separar o cálculo da interface do usuário.
- O trabalhador implementa um
kill()
método, que permite que a função termine normalmente. Não tente usar o terminate()
método QThread
- coisas ruins podem acontecer!
Certifique-se de acompanhar seus objetos thread
e worker
em algum lugar da estrutura de plugins. Qt fica bravo se você não. A maneira mais fácil de fazer isso é armazená-los na sua caixa de diálogo quando você os cria, por exemplo:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
Ou você pode deixar o Qt se apropriar do QThread:
thread = QtCore.QThread(self)
Levei muito tempo para desenterrar todos os tutoriais para montar este modelo, mas desde então eu tenho reutilizado tudo isso em todo o lugar.
worker.progress.connect(self.ui.progressBar)
para outra coisa, mas sempre que o executo, o qgis-bin está travando. Não tenho experiência em depurar código python ou qgis. Tudo o que estou recebendo éAccess violation reading location 0x0000000000000008
que parece que algo é nulo. Existe algum código de configuração ausente para poder usá-lo em um script de processamento?Sua única maneira verdadeira de fazer isso é por multithreading.
Alguma leitura extra http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Nota Algumas pessoas não gostam de herdar o QThread e, aparentemente, essa não é a maneira "correta" de fazê-lo, mas funciona assim ....
fonte
Como essa pergunta é relativamente antiga, merece uma atualização. Com o QGIS 3, há uma abordagem com QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () e QgsApplication.taskManager (). AddTask ().
Mais sobre isso, por exemplo, em Usando Threads no PyQGIS3 POR MARCO BERNASOCCHI
fonte