2011-12-21 2 views
5

완료하는 데 다소 시간이 걸리는 프로세스가 있습니다. 필자가 pygtk GUI에서 이것을 호출하면 약 10 초 후에 창이 잠기고 (어둡게되어 사용자 행동을 막는다).pygtk GUI가 장시간 실행되는 프로세스에서 잠기는 것을 막으십시오.

나는 이런 일이 발생하지 않도록하고 싶지만, 어떻게해야할지 모르겠다. 나는 멀티 스레딩이 답이라고 생각했지만, 작동하지 않는 것 같습니다. 나는 온라인에서 찾은 두 가지 방법을 시도했다. 먼저, 장기 실행 기능을 사용하기 위해 this FAQ을 수정했습니다. 둘째로 나는 this answer 에서처럼 threading.Thread를 직접 사용해 보았습니다.하지만 역시 잠겨 있습니다.

두 개의 샘플이 아래에 있습니다. 나는 멀티 쓰레딩에 익숙하지 않기 때문에 내가 원하는 해결책이 아닐 수도있다. 나는 기본적으로 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을 사용하면 일부 스레드가 실행될 때 진행률 표시 줄에 펄스를 발생시키는 콜백을 예약 할 수 있습니다. 이것은 스레드를 폴링하는 것과 동일한 효과를 가지지 만 메인 루프가 다른 이벤트에 여전히 응답한다는 장점이 있습니다.
  • 을 사용하면 버튼을 몇 번 클릭했는지에 관계없이 콜백을 두 번 이상 예약 할 수 없습니다.
  • 이 예제에서 누락 된 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는 스레드 안전하다는 것을 의심하기 때문에 콜백 기능을 사용 : 여기

내가 두 번째 옵션에 대한 의미를 설명하는 몇 가지 간단한 코드입니다.

+0

죄송합니다. 같은 예제를 두 번 붙여 넣었습니다. 두 번째 예제에서는 button.start()를 호출 한 다음 스레드가 완료 될 때까지 기다립니다. 이것은 여전히 ​​내 응용 프로그램을 잠급니다. –

+0

두 번째 예제에서는 콜백 메소드 내에'thread.is_alive'를 폴링합니다. 이로 인해 콜백은 스레드 자체만큼 완료하는 데 시간이 오래 걸리고 콜백이 종료 될 때까지 응용 프로그램이 다른 이벤트를 처리하지 못하게됩니다. – jcollado

+0

@ d-k 나는 실제 코드에서 본 적이 없기 때문에 각 스레드에서 이벤트 루프를 실행하는 옵션에 관심이 있습니다.저것 좀 더 자세히 설명해 주시겠습니까? – jcollado

관련 문제