回答:
以下のようネイサンWが指摘、これを行う方法は、マルチスレッドであるが、サブクラスQThreadはベストプラクティスではありません。ここを参照してください:http : //mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
以下のを作成しQObject
、それをに移動する方法の例を参照してくださいQThread
(つまり、「正しい」方法で)。この例では、ベクターレイヤーのすべてのフィーチャの総面積を計算します(新しいQGIS 2.0 APIを使用して!)。
最初に、私たちのために重い作業を行う「ワーカー」オブジェクトを作成します。
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)
ワーカーを使用するには、ベクターレイヤーで初期化する必要があります。ワーカーをスレッドに移動し、いくつかの信号を接続してから開始します。上でリンクされているブログを見て、ここで何が起こっているのかを理解するのがおそらく最善です。
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()
この例は、いくつかの重要なポイントを示しています。
run()
ワーカーのメソッド内のすべてがtry-exceptステートメント内にあります。コードがスレッド内でクラッシュすると、回復が困難になります。通常はに接続するエラー信号を介してトレースバックを送信しますQgsMessageLog
。kill()
メソッドを実装し、関数が正常に終了できるようにします。terminate()
メソッドを試して使用しないでくださいQThread
-悪いことが起こる可能性があります!プラグイン構造のどこかにthread
とworker
オブジェクトを追跡するようにしてください。そうしないと、Qtは怒ります。これを行う最も簡単な方法は、作成時にダイアログに保存することです。例:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
または、QtにQThreadの所有権を取得させることもできます。
thread = QtCore.QThread(self)
このテンプレートをまとめるためにすべてのチュートリアルを掘り下げるのに長い時間がかかりましたが、それ以来、私はそれをあちこちで再利用しています。
worker.progress.connect(self.ui.progressBar)
他のものに変更しましたが、実行するたびにqgis-binがクラッシュします。Pythonコードやqgisのデバッグ経験はありません。私が得ているのはAccess violation reading location 0x0000000000000008
何かがnullのようです。これを処理スクリプトで使用するために欠落しているセットアップコードはありますか?
これを行う唯一の真の方法は、マルチスレッド化です。
class MyLongRunningStuff(QThread):
progressReport = pyqtSignal(str)
def __init__(self):
QThread.__init__(self)
def run(self):
# do your long runnning thing
self.progressReport.emit("I just did X")
thread = MyLongRunningStuff()
thread.progressReport.connect(self.updatetheuimethod)
thread.start()
追加の読書http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
Note一部の人々はQThreadからの継承を嫌い、そしてこれは明らかにこれを行うための「正しい」方法ではありませんが、それはそうです...
この質問は比較的古いので、更新する価値があります。QGIS 3では、QgsTask.fromFunction()、QgsProcessingAlgRunnerTask()およびQgsApplication.taskManager()。addTask()によるアプローチがあります。