PyQt:単純なスレッド化

多くの場合、プログラムではマルチスレッドを使用する必要があります。 これらは複雑な相互作用を伴うモンスターのようなスレッドプールである場合もありますが、多くの場合、これは単純なコードであり、その主な要件はインターフェイスをフリーズしないことです。



PyQtには、高レベルのスレッドを操作するための2つの主要なツールがあります。PythonスレッドとQt QThreadです。 私にとっては、Qtのシグナルスロットメカニズムとの接続が良好であるため、QThreadの方が望ましいと判明しました。



そのため、ツールが選択され、すべてが正常に機能しますが、時間が経つにつれて、ストリームの処理を多少簡単で便利にしたいと思いました。 単純なケースでは、 自転車をフローのある仕事のモジュールにするアイデアがありました。



simple_thread


このモジュールは 、QObjectから継承されたクラスのストリームで動作するように設計されています。 これを使用すると、任意のクラスメソッドを別のスレッドで強制的に実行できます。一方、メソッド内では、クラスの属性とメソッドに(限定的ではありますが)アクセスできます。



簡単な例を見てみましょう:

#!/usr/bin/env python # -*- coding: utf-8 -*- import sys from time import sleep from PyQt4.QtCore import * from PyQt4.QtGui import * from simple_thread import SimpleThread class Foo(QLabel): def __init__(self, parent = None): QLabel.__init__(self, parent) self.setFixedSize(320, 240) self.digits = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'] @SimpleThread def bar(self, primaryText): rows = [] digits = self.digits for item in digits: rows.append('%s: %s' % (primaryText, item)) self.setText('\n'.join(rows), thr_method = 'b') sleep(0.5) def setText(self, text): QLabel.setText(self, text) if __name__ == "__main__": app = QApplication(sys.argv) foo = Foo() foo.show() foo.bar('From thread', thr_start = True) app.exec_()
      
      







FooクラスはQLabelを継承しており 、インターフェイスをフリーズせずに0.5秒ごとにラベルテキストを変更します。 barメソッドはテキスト出力を処理します 。 このメソッドを別のスレッドで動作させるには、メソッドを宣言する前に@SimpleThreadデコレーターを配置します。



メソッド内から、 digits属性(表示される単語のリスト)にアクセスする必要があります。 また、ラベルテキストを更新する必要があります。そのために、 setTextメソッドを呼び出します。



属性アクセス


最初の問題は、このスレッドだけでなく数字も使用できることです。

simple_threadモジュールはこの問題を回避します。 属性を受け取ると、この属性へのリンクではなく、そのコピーが返されます。 同時に、すべてのアクションはメインスレッドのコンテキストで発生するため、複数のスレッドから同時に属性にアクセスすることを心配する必要はありません。

この方法で、リスト、辞書、およびすべての不変のクラス属性(文字列、タプルなど)にアクセスできます。

ここには1つのポイントがあります。動作が非常に遅いため、属性にアクセスして無理をしないでください。



メソッド呼び出し


問題2はsetTextメソッドを呼び出しています。 問題は最初の問題と似ています-メインスレッドからではなくグラフィッククラスメソッドにアクセスしようとすると、Qtは例外をスローします。 最初の場合と同様に、これはスレッドを中断し、メインスレッドからメソッドを呼び出すことで解決されます。



thr_method引数に応じて、メソッドを呼び出す3つの異なる方法があります。



thr_method引数は、呼び出されたメソッドに渡されません。



属性を設定する


別のスレッドから属性を設定することもできます。

 self.newAttr = 'text'
      
      





属性はどのタイプでもかまいません。

他の場合と同様に、すべての作業はメインスレッドで実行され、問題は発生しません。属性を設定することで、既存の属性を消去できることを覚えておく必要があります。



ストリーム開始


コードを実行するには、スレッドがすぐに開始されるようにthr_start = True引数を指定してbarメソッドを実行するだけです。

別の方法があります。別のスレッドから呼び出されたシグナルまたはクラスQThreadのシグナル( startedfinishedterminate )を処理する場合に便利です。

  thread = foo.bar('From thread') thread.finished.connect(self.barFinished) thread.start()
      
      





ここでは、 終了したストリームの終了信号をbarFinishedメソッドに接続し、ストリームを開始しました。



フローストップ


何らかの理由で実行中のスレッドを停止する必要がある場合、 thr_stopメソッドを呼び出すことでこれを実行できます。

  thread = foo.bar('From thread', thr_start = True) ... thread.thr_stop()
      
      





このメソッドはフラグthr_stopFlag = Trueを設定します。この状態はメソッドで監視する必要があり、trueの場合はメソッドを終了します。



thr_stopメソッドスレッドが停止するのを待たず、すぐに制御を返すことに注意してください 。 スレッドの動作が完了するのを待つ必要がある場合は、 thr_stopの後にwaitメソッドを呼び出す必要があります。



simple_threadモジュールには、すべてのアクティブなスレッドを停止するための2つの関数-terminateThreadscloseThreadsがあります。 最初の関数は、すべてのスレッドの実行を著しく中断しますが、これは安全ではない場合があります。 2番目の関数は、スレッドごとにthr_stopを呼び出したように機能し、 待機します。



すべてのコメント、提案などを歓迎します。 このようなものが既に誰かによって実装されている場合、私はリンクさせていただきます。



All Articles