Jak zauważa Nathan W , sposobem na to jest wielowątkowość, ale podklasa QThread nie jest najlepszą praktyką. Zobacz tutaj: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Zobacz poniżej przykład tworzenia a QObject
, a następnie przenieś go do QThread
(tj. „Prawidłowego” sposobu na zrobienie tego). Ten przykład oblicza całkowity obszar wszystkich funkcji w warstwie wektorowej (przy użyciu nowego API QGIS 2.0!).
Najpierw tworzymy obiekt „roboczy”, który wykona za nas ciężkie podnoszenie:
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)
Aby użyć pracownika, musimy zainicjować go warstwą wektorową, przenieść go do wątku, podłączyć niektóre sygnały, a następnie uruchomić. Prawdopodobnie najlepiej jest zajrzeć na powyższego bloga, aby zrozumieć, co się tutaj dzieje.
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()
Ten przykład ilustruje kilka kluczowych punktów:
- Wszystko w
run()
metodzie pracownika znajduje się w instrukcji try-wyjątkiem. Trudno go odzyskać, gdy kod ulega awarii w wątku. Emituje ślad poprzez sygnał błędu, który zwykle podłączam do QgsMessageLog
.
- Gotowy sygnał informuje podłączoną metodę, czy proces zakończył się powodzeniem, a także wynik.
- Sygnał postępu jest wywoływany tylko wtedy, gdy zmienia się procent ukończenia, a nie raz dla każdej funkcji. Zapobiega to zbyt dużej liczbie wywołań, aby zaktualizować pasek postępu, spowalniając proces roboczy, co zniweczyłoby cały punkt uruchamiania pracownika w innym wątku: oddzielenie obliczeń od interfejsu użytkownika.
- Pracownik implementuje
kill()
metodę, która umożliwia łagodne zakończenie funkcji. Nie próbuj używać tej terminate()
metody w QThread
- mogą się zdarzyć złe rzeczy!
Pamiętaj, aby śledzić swoje obiekty thread
i worker
obiekty gdzieś w strukturze wtyczek. Qt się denerwuje, jeśli nie. Najprostszym sposobem na to jest przechowywanie ich w oknie dialogowym podczas ich tworzenia, np .:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
Lub możesz pozwolić Qt przejąć na własność QThread:
thread = QtCore.QThread(self)
Dużo czasu zajęło mi wykopanie wszystkich samouczków, aby złożyć ten szablon razem, ale od tego czasu używam go wszędzie.
worker.progress.connect(self.ui.progressBar)
na coś innego, ale za każdym razem, gdy go uruchamiam, qgis-bin ulega awarii. Nie mam doświadczenia w debugowaniu kodu Python ani qgis. Dostaję tylko to,Access violation reading location 0x0000000000000008
więc wydaje się, że coś jest nieważne. Czy brakuje jakiegoś kodu konfiguracji, aby móc go użyć w skrypcie przetwarzania?Twoim jedynym prawdziwym sposobem na to jest wielowątkowość.
Trochę dodatkowych lektur http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Uwaga Niektóre osoby nie lubią dziedziczyć po QThread i najwyraźniej nie jest to „poprawny” sposób, ale działa, więc…
źródło
Ponieważ to pytanie jest stosunkowo stare, zasługuje na aktualizację. W QGIS 3 dostępne jest podejście z QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () i QgsApplication.taskManager (). AddTask ().
Więcej na ten temat na przykład przy użyciu wątków w PyQGIS3 BY MARCO BERNASOCCHI
źródło