2015-09-08 18 views
11

これはwxPythonのPhoenixフォークにあります。wxPythonスレッドのブロック

私はGUIをブロックしないように、いくつかのスレッドを実行しようとしています。

私のスレッドのうちの2つはうまく動作しますが、もう1つは決してその結果の結果関数を打つことはありません。私はそれが実行されていることを伝えることができます、それはちょうど適切にイベントを投稿するようではありません。

は、ここでの主な計算スレッドの結果関数です:

def on_status_result(self, event): 
    if not self.panel.progress_bar.GetRange(): 
     self.panel.progress_bar.SetRange(event.data.parcel_count) 
    self.panel.progress_bar.SetValue(event.data.current_parcel) 
    self.panel.status_label.SetLabel(event.data.message) 

は、ここで私はそれらを結合しています方法は次のとおりです。

from wx.lib.pubsub.core import Publisher 
PUB = Publisher() 

ここで私が方法結合てる方法は次のとおりです。

def post_event(message, data): 
    wx.CallAfter(lambda *a: Publisher().sendMessage(message, data=data)) 

ここにスレッドがあります。一つ目は動作しませんが、次の二つの操作を行います。私は/サブスクライブ開始/作成しています方法です。ここ

class Status: 
    def __init__(self, parcel_count, current_parcel, total, message): 
     self.parcel_count = parcel_count 
     self.current_parcel = current_parcel 
     self.total = total 
     self.message = message 

class PrepareThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      for status in prepare_collection(DATABASE, self._previous_id, self._current_id, self._year, self._col_type, 
              self._lock): 
       post_event('prepare.running', status) 
     post_event('prepare.complete', None) 
     return None 

    def abort(self): 
     self._want_abort = True 


class SetupThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      do_more_stuff_with_the_database() 
      return None 

    def abort(self): 
     self._want_abort = True 


class LatestCollectionsThread(threading.Thread): 
    def __init__(self, notify_window): 
     threading.Thread.__init__(self) 
     self._notify_window = notify_window 
     self._want_abort = False 

    def run(self): 
     while not self._want_abort: 
      do_stuff_with_my_database() 
      return None 

    def abort(self): 
     self._want_abort = True 

prepare_collectionは、次のようになりますStatusオブジェクトを生成する関数でありますPrepareThread:

MainForm(wx.Form): 
    prepare_thread = PrepareThread(self) 
    prepare_thread.start() 

    self.pub = Publisher() 
    self.pub.subscribe(self.on_status_result, 'prepare.running') 
    self.pub.subscribe(self.on_status_result, 'prepare.complete') 

    def on_status_result(self, event): 
     if not self.panel.progress_bar.GetRange(): 
      self.panel.progress_bar.SetRange(event.data.parcel_count) 
     self.panel.progress_bar.SetValue(event.data.current_parcel) 
     self.panel.status_label.SetLabel(event.data.message) 

私はrange(10)prepare_collectionをスタブ試してみたが、私はまだ今まで前夜にヒットしません。 ntハンドラ。

+0

こんにちはモルガン申し訳ありません...私はおそらくこれを見直すチャンスを今夜は得られない...ちょうどスーパービジー:/ –

+0

@ヨランそれはすべて良いです。 –

+0

ああダン...申し訳ありません...私は試してみて、あなたの今週末を助けようとします。ちょうど狂った週でした。 –

答えて

4

ような何かをできるようにし、それが正確に動作するように少し難しいいるのあなたのコードの各部分にあなたのスニペット(つまり、それらがすべてどのファイルに入っているか)を確認します。私はこれを行う方法をpubsubのままにしたいと思っています(これは良い考えです)。あなたの本当のプロジェクトの構造が非常に複雑である場合は、私がここで使うよりも、あなたはPublisherのより複雑な管理を必要とするかもしれない - 私に知らせて...


ここに行く - 私が最初にスポイラーを入れます - ここですあなたが望むもののためのone file solution - 準備が完了したときの準備スレッド、ステータスバー、ハンドラを開始するボタンが付いたパネル。 wxPythonのPhoenix 64ビットPython 3とPython 2.7の旧式のwxPythonでテストされています。どちらもWindows上にありますが、必要に応じてLinuxボックスでスピンアップすることができます。そのファイルの重要な(非ボイラープレート)ビット

あなたのスレッドがにメッセージを送信し、あなたのメインスレッド(私は推測するあなたの例ではMainForm)が加入する単一Publisherオブジェクトが必要にまとめる

。スレッドごとにPublisherを管理することができますが、ここではPrepareThreadに1つしか必要ないと思いますので、今はそのモデルを使用します。あなたのファイルの先頭に

、これはpubsubは単一Publisherオブジェクトをインスタンス化管理できます

from wx.lib.pubsub import pub 

を使用しています。あなたのスレッドで

、あなたがやっているように、そこにメッセージを公開 - あなたのpost_eventヘルパーへのわずかなammendmentを:あなたのメインスレッドで

def post_event(message, data): 
    wx.CallAfter(lambda *a: pub.sendMessage(message, data=data)) 

- それらのメッセージを購読してください。私はそれはむしろあなたがあったように同じハンドラに二つの異なるメッセージを送信するよりも、メッセージごとに1つのハンドラを持っていることは通常最も簡単だと言うと思いますので、私は

pub.subscribe(self.on_status_result, 'prepare.running') 
pub.subscribe(self.on_status_finished, 'prepare.complete') 

に行ったことがあるとしてあなたはon_status_resultを残すことができ、同様の定義しますon_status_finished。私の例では、後で実際の作業をするための新しいボタンを有効にしました。

N.B.メッセージのペイロードに名前を付けるときは注意が必要です。pubsubは、そこに何が期待されているかについての情報をかなり推測し、最初に私を捕まえました。


P.S.この答えを準備し終えたら、私はthis blog postを見つけました。それは私が上に持っているものに類似しているので、私はそれを再現しませんが、元の例のようにPublisher()をインスタンス化する他の方法を使用します。そこに言葉が好きかもしれません。 Simlarly - this wxPython wikiページが便利です。

+0

これは私が必要とするもののように見えます。私は約1時間を与えて、それから私はそれを試してみましょう! –

+0

それは動作します! J、あなたは美しい人です。 –

+0

良い仕事..申し訳ありません私は忙しかった... wx2.8の呼び出しでpublisher sendMessage subscribeがすべてのインスタンスで共有されています(実際にPublisher()が同じインスタンスを返すかもしれません...) –

4

問題は、イベントシステムがスレッド自体から更新関数(イベントハンドラ)を呼び出すことになります。あなたはそれほど多くは決してしません(基本的には奇妙な競合条件と成果物で終わります)。メインスレッドのコールバック

wxPythonはこれを考慮に入れており、wx.CallAfterで呼び出されたメソッドは常にメインスレッドで実行されているメインプログラムループから呼び出されます。 wx.pubsubモジュールと組み合わせて、これは...あなたは簡単に独自のイベントフレームワークを作成するために、これはかなり複雑な答えかもしれない。この

def MyPostEvent(event_name,event_data): 
    #just a helper that triggers the event with wx.CallAfter 
    wx.CallAfter(lambda *a:Publisher().sendMessage(event_name,data=event_data)) 

#then to post an event 

MyPostEvent("some_event.i_made_up",{"payload":True}) 

#then in your main thread subscribe 

def OnEventHandler(evt): 
    print "EVT.data",evt.data 

pub = Publisher() 
pub.subscribe("some_event.i_made_up",OnEventHandler) 
+0

"some_event.i_made_up"はMyPostEventに渡すのと同じものでしょうか?また、これはPhoenixで動作しますか? –

+0

フェニックスで動作するはずです(マイナーな変更が必要かもしれませんが、一般的な考え方が適用されます) –

+0

これはあなたが渡すものです:P(イベントを受信して​​放出する例を参照してください) –

関連する問題