2011-12-21 1 views
5

私は完了するまでに(おそらく1分または2分かかる)プロセスがあります。これをpygtkのGUIから呼び出すと、約10秒後にウィンドウがロックアップ(暗くなり、ユーザーの操作を妨げる)します。長時間実行中のプロセスでpygtk GUIがロックされないようにします

私はこれをやめないようにしたいと思いますが、どうしたらよいか分かりません。私はマルチスレッドが答えになると思っていましたが、動作していないようです。私はオンラインで見つけた2つの方法を試しました。まず、this FAQを変更して長時間実行するようにしました。第二に私はthis answerのように直接threading.Threadを使ってみましたが、それもロックアップします。

私の2つのサンプルは以下の通りです。私はマルチスレッド化に新しいので、おそらくそれは私が探している解決策ではありません。私は基本的にGUIをロックしないようにしているので、プログレスバーで更新して、ユーザーにキャンセルボタンを使用させることができます。

#Sample 1 
import threading 
import time 
import gobject 
import gtk 

gobject.threads_init() 

class MyThread(threading.Thread): 
    def __init__(self, label, button): 
     super(MyThread, self).__init__() 
     self.label = label 
     self.button = button 
     self.counter = 0 
     button.connect("clicked", self.on_button_click) 
     self.quit = False 

    def update_label(self, counter): 
     self.label.set_text("Counter: %i" % counter) 
     time.sleep(20) 
     return False 

    def on_button_click(self, widget): 
     self.counter += 1 
     gobject.idle_add(self.update_label, self.counter) 

window = gtk.Window() 
label = gtk.Label() 
box = gtk.VBox() 
button = gtk.Button("Test") 
box.pack_start(label) 
box.pack_start(button) 
window.add(box) 
window.show_all() 
window.connect("destroy", lambda _: gtk.main_quit()) 
thread = MyThread(label, button) 
thread.start() 

gtk.main() 
thread.quit = True 

##################################### 
#Sample 2 

from threading import Thread 
import time 
import gobject 
import gtk 

class Test(): 
    def __init__(self): 
     self.counter = 0 
     self.label = gtk.Label() 
     button = gtk.Button("Test") 

     window = gtk.Window() 
     box = gtk.VBox() 
     box.pack_start(self.label) 
     box.pack_start(button) 
     window.add(box) 

     window.connect("destroy", lambda _: gtk.main_quit()) 
     button.connect("clicked", self.on_button_click) 
     window.show_all() 

    def update_label(self, counter): 
     self.label.set_text("Counter: %i" % counter) 
     time.sleep(20) 
     return False 

    def on_button_click(self, widget): 
     self.counter += 1 
     thread = Thread(target=self.update_label, args=(self.counter,)) 
     thread.start() 
     while thread.is_alive(): 
      pass 
     thread.stop() 

test = Test() 
gtk.main() 

答えて

7

私の作品第二の例の修正バージョンの下に見つけてください:

import threading 
import time 
import gtk, gobject, glib 

gobject.threads_init() 

class Test(): 
    def __init__(self): 
     self.counter = 0 
     self.label = gtk.Label() 
     self.progress_bar = gtk.ProgressBar() 
     self.progress_bar_lock = threading.Lock() 
     button = gtk.Button("Test") 

     window = gtk.Window() 

     box = gtk.VBox() 
     box.pack_start(self.label) 
     box.pack_start(self.progress_bar) 
     box.pack_start(button) 
     window.add(box) 

     window.connect("destroy", lambda _: gtk.main_quit()) 
     button.connect("clicked", self.on_button_click) 
     window.show_all() 

    def update_label(self, counter): 
     self.label.set_text("Thread started (counter: {0})" 
          .format(counter)) 
     time.sleep(5) 
     self.label.set_text("Thread finished (counter: {0})" 
          .format(counter)) 
     return False 

    def pulse_progress_bar(self): 
     print threading.active_count() 
     if threading.active_count() > 1: 
      self.progress_bar.pulse() 
      return True 

     self.progress_bar.set_fraction(0.0) 
     self.progress_bar_lock.release() 
     return False 

    def on_button_click(self, widget): 
     self.counter += 1 
     thread = threading.Thread(target=self.update_label, 
            args=(self.counter,)) 
     thread.start() 

     if self.progress_bar_lock.acquire(False): 
      glib.timeout_add(250, self.pulse_progress_bar) 


if __name__ == '__main__': 
    test = Test() 
    gtk.main() 

行われた変更は、次のとおりです。

  • は保つために最後までスレッドのコールバックで待っていないでくださいメインループ処理イベント。
  • スレッドが実行されているときに表示する進行状況バーを追加しました。
  • glib.timeout_addを使用して、スレッドが実行されているときにプログレスバーにパルスを送るコールバックをスケジュールします。これは、スレッドをポーリングするのと同じ効果を持ちますが、メインループが他のイベントに応答するという利点があります。
  • threading.Lockを使用すると、ボタンがクリックされた回数に関係なく、コールバックが複数回スケジュールされるのを防ぐことができます。
  • gobject.threads_initがこの例では欠けていました(以前のものではありません)。

ここでボタンをクリックすると、スレッドが実行されている限り、ラベルがどのようにクリックされ、進行状況バーが脈打っているかがわかります。

+0

これは意味があります。私はスレッドの状態を常にポーリングできない理由を理解しています。しかし、私はスレッドが終了するときを知る必要があります。スレッドの進捗状況のステータスバーを追加すると、ステータスバーをいつ停止するかをどのように知ることができますか? –

+0

例に進捗バーを追加しました。 'glib.timeout_add'は、あなたのアプリケーションが応答しなくてもスレッドの状態をポーリングすることができます。 – jcollado

+0

大丈夫です。意味あり。私は 'glib.timeout_add'について知らなかった。ご協力いただきありがとうございます。 –

0

あなたは、スレッドごとに、Thread.runを再実装し、それらのイベントループを開始する必要があります。

また、ボタンを押してスレッドのstartメソッドを呼び出すと、runが呼び出され、長い時間がかかることがあります。この方法では、各スレッドにイベントループは必要ありません。

class MyThread(threading.Thread): 

    def __init__(self, label, button): 
     threading.Thread.__init__(self) 
     self.label = label 
     self.button = button 
     self.counter = 0 

    def run(self): 
     time.sleep(20) 

def callback(): 
    label.set_text("Counter: %i" % thread.counter) 
    thread.start() 

window = gtk.Window() 
label = gtk.Label() 
box = gtk.VBox() 
button = gtk.Button('Test') 
box.pack_start(label) 
box.pack_start(button) 
window.add(box) 
window.show_all() 

thread = MyThread(label, button) 
button.connect('clicked', callback) 

私はset_textは、スレッドセーフであることを疑うので、私は、コールバック関数を使用します。ここでは

は、私は2番目のオプションのために何を意味するかを説明するためにいくつかの簡単なコードです。

+0

申し訳ありませんが、私は同じ例を2度貼り付けました。 2番目の例では、ボタン呼び出しthread.start()を実行してから、スレッドが終了するまで待ちます。これは私のアプリケーションをロックします。 –

+0

2番目の例では、 'thread.is_alive'をコールバックメソッドの中でポーリングしています。そのため、コールバックはスレッド自体と同じくらい時間がかかり、コールバックが終了するまで他のイベントは処理されません。 – jcollado

+0

@ d-k私は実際のコードでそれを見たことがないので、各スレッドでイベントループを実行するオプションに興味があります。あなたはそのことを詳しく説明できますか? – jcollado

関連する問題