0

外部データを監視および表示する非常に単純な wxPython GUI を構築しようとしています。モニタリングのオン/オフを切り替えるボタンがあります。監視がオンになると、GUI はリアルタイム データで 2 つの wx StaticLabels を更新します。モニタリングがオフになると、GUI はアイドル状態になります。

私がそれを構築しようとした方法は、かなり単純な Python スレッド レイアウトを使用することでした。[監視の開始] ボタンをクリックすると、プログラムはリアルタイムの情報でラベルを更新するスレッドを生成します。「監視の停止」ボタンをクリックすると、thread.join() が呼び出され、停止するはずです。

開始機能は機能し、リアルタイムのデータ更新はうまく機能しますが、[停止] をクリックすると、プログラム全体がフリーズします。これを Windows 7 64 ビットで実行しているため、通常の「このプログラムは応答を停止しました」という Windows ダイアログが表示されます。

関連するコードは次のとおりです。

class MonGUI(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        ...
        ... other code for the GUI here ...
        ...
        # Create the thread that will update the VFO information
        self.monThread = Thread(None, target=self.monThreadWork)
        self.monThread.daemon = True
        self.runThread = False

    def monThreadWork(self):
        while self.runThread:
            ...
            ... Update the StaticLabels with info
            ... (This part working)
            ...

    # Turn monitoring on/off when the button is pressed.
    def OnClick(self, event):
        if self.isMonitoring:
            self.button.SetLabel("Start Monitoring")
            self.isMonitoring = False
            self.runThread = False
            self.monThread.join()
        else:
            self.button.SetLabel("Stop Monitoring")
            self.isMonitoring = True

            # Start the monitor thread!
            self.runThread = True
            self.monThread.start()

これを行うためのより良い方法があると確信していますが、私は GUI プログラミングと Python スレッドにかなり慣れていないため、これが最初に思いついたものでした。

では、スレッドを停止するボタンをクリックすると、全体がフリーズするのはなぜでしょうか?

4

3 に答える 3

2

wxPython では、GUI 操作はメイン スレッドで行う必要があります。コード内の場所で、別のスレッドから GUI を呼び出しています。

最も簡単な解決策は、 を使用することwx.CallAfter()です。コード行は次のようになります

wx.CallAfter(self.button.SetLabel, “Start Monitoring”)

関数が完了すると、メイン スレッドから self.button.SetLabel(“Start Monitoring”) が呼び出されます。

Python スレッド化 Queue や wx.PostEvent を使用するなど、これを回避する方法は他にもありますが、最も簡単な Cal​​lAfter から始めます。

同じスレッドを再起動できないなど、他の問題も関連していますが、CallAfter を使用するとクラッシュが停止します。

于 2010-05-17T20:46:31.320 に答える
1

tom10 は、モニター スレッドからの UI 更新を回避するという正しい考えを持っています。

self.monThread.join()また、 UI スレッドでブロッキング呼び出しを行うことはおそらくお勧めできません。モニター スレッドが実際に終了したというフィードバックを UI に表示させたい場合は、monThreadWorker が終了する直前に wx.CallAfter() または wx.PostEvent() を発行するようにします。

UI スレッドでブロックするものをすべて回避すると、UI のデッドロックを回避できます

于 2010-05-17T22:42:30.470 に答える
1

これは、メソッドが呼び出されたスレッドが正常に終了するか、未処理の例外によって終了するか、オプションのタイムアウトが発生するjoin([timeout])まで、呼び出し元のスレッドをブロックする可能性があります。join()

スレッドに内部ループがあるか、または決して来ない可能性のあるデータ ソースを待機するブロッキング呼び出しがありますか? COM ポート データを取得する基本的なシリアル プログラムを作成したとき、スレッド内の読み取り関数が何かを取得するまでブロックされるため、ハングすることがありました。

何が起こっているのかを確認するために、いくつかのデバッグprintステートメントを振りかけます。

編集:

threading.Event()ブール値フラグの代わりに aも使用します。たとえば、次のようになります。

# in the init code...
self.runThread = threading.Event()

# when starting thread...
self.runThread.set()
self.monThread.start()

# in the thread...
while self.runThread.isSet():
    pass # do stuff

# killing the thread...
self.runThread.clear()
self.monThread.join()

これで動作が変わるわけではありませんが、少し安全な方法です。

于 2010-05-17T20:12:34.687 に答える