2

次に例を示します。

class DemoFrame(wx.Frame):
    def __init__(self, parent):
         wx.Frame.__init__(self, parent)
         self.panel = wx.Panel(self, -1)
         ...
         initialize other elements
         ...
         self.DoStuff()


    def DoStuff(self):
         self.panel.SetBackGroundColour(wx.Colour(240, 240, 240))
         ...
         do something
         ...

ご存知do somethingのように、これは実行中に GUI をフリーズさせる可能性が高いため、GUI の初期化の良い例ではないため、次のように微調整しました。

import threading

class DemoFrame(wx.Frame):
    def __init__(self, parent):
         wx.Frame.__init__(self, parent)
         self.panel = wx.Panel(self, -1)
         ...
         initialize other elements
         ...
         DoStuffThead = threading.Thread(target = self.DoStuff, ())
         DoStuffThead.start()


    def DoStuff(self):
         wx.CallAfter(self.ChangeBG, )

         ...
         do something
         ...

    def ChangeBG(self):
         self.panel.SetBackGroundColour(wx.Colour(240, 240, 240))

上記のコードは、空白の場合は最初のコードとまったく同じように機能するはずですdo somethingが、驚いたことに、後者のコードを実行すると、背景描画の不具合がほとんどないことに気付きました。

どの部分が間違っていましたか?これは、スレッドで GUI を更新する正しい方法ではありませんか?

4

2 に答える 2

1

しばらく wxpython を検索して遊んだ後、最終的にこれに対する解決策を見つけました。実際には非常に簡単です。パネルを更新するだけですべてがうまくいきます(この行をChangeBGメソッドに追加します): self.panel.refresh(). なぜグリッチが存在するのかわかりません。

Rostyslavの答えについては、どうもありがとう!

「ワーカースレッドからGUIを更新するのは悪いアプローチです:」、スレッドセーフの観点から、GUIコードをワーカースレッドに直接挿入するのは失礼だと思います(これはまさに最初の例で行ったことです)。 GUI コードは、スレッド セーフな方法 (2 番目の例でまさに私がやろうとしていたこと) にラップしてから、GUI メイン スレッドのキューに入れる必要があります。

ワーカー スレッドでの GUI の更新に関しては、基本的に 3 つのスレッド セーフな方法があることがわかりました: wx.PostEventwx.CallAfter、。私にとって、それはよりPythonicで使いやすく、実際には次のソースコードをチェックアウトする場合の高レベルのラッパーのようなものです:wx.CallLaterwx.PostEventwx.CallAfterwx.CallAfterwx.PostEvent_core.py

def CallAfter(callable, *args, **kw):
    """
    Call the specified function after the current and pending event
    handlers have been completed.  This is also good for making GUI
    method calls from non-GUI threads.  Any extra positional or
    keyword args are passed on to the callable when it is called.

    :see: `wx.CallLater`
    """
    app = wx.GetApp()
    assert app is not None, 'No wx.App created yet'

    if not hasattr(app, "_CallAfterId"):
        app._CallAfterId = wx.NewEventType()
        app.Connect(-1, -1, app._CallAfterId,
                    lambda event: event.callable(*event.args, **event.kw) )
    evt = wx.PyEvent()
    evt.SetEventType(app._CallAfterId)
    evt.callable = callable
    evt.args = args
    evt.kw = kw
    wx.PostEvent(app, evt)

まあ、私はwx.PostEvent自分のアプリで実装を試みたことはありませんが、それもうまくいくと確信しています.

ああ、この記事もとても役に立ちました: wxPython and Threads

于 2013-08-16T15:32:22.870 に答える
1

スレッドセーフではないというイベントではなく、ワーカースレッドから GUI を更新するのは悪いアプローチです。GUI を更新するには、メイン スレッドと通信する必要があります。

目的の結果を達成するための最良の方法は、ユーザーwx.PostEventメソッドを使用することです。wx.PyEventから継承して、ニーズに合わせてカスタムイベントを作成できます。継承して、threading.Threadそのスレッドクラス内で通信するウィンドウをインスタンス変数として保持することをお勧めします。

実行時間の長いタスクを持つ GUI を更新する方法の最良の例は、wxPython wiki (最初の例) にあります。

于 2013-08-16T14:30:27.060 に答える