2013-06-05 68 views
2

我有這樣的主線程:如何在gtk3-python中做後臺任務?

Gui.py

from gi.repository import Gtk, Gdk 
import Process 
import gobject 

class gui(): 
    def __init__(self): 
     self.window = Gtk.Window() 
     self.window.connect('delete-event', Gtk.main_quit) 

     self.box = Gtk.Box() 
     self.window.add(self.box) 

     self.label = Gtk.Label('idle') 
     self.box.pack_start(self.label, True, True, 0) 

     self.progressbar = Gtk.ProgressBar() 
     self.box.pack_start(self.progressbar, True, True, 0) 

     self.button = Gtk.Button(label='Start') 
     self.button.connect('clicked', self.on_button_clicked) 
     self.box.pack_start(self.button, True, True, 0) 

     self.window.show_all() 
     gobject.threads_init() 

     Gdk.threads_enter() 
     Gtk.main() 
     Gdk.threads_leave() 

    def working1(): 
     self.label.set_text('working1') 
     t = Process.Heavy() 
     t.heavyworks1() 
     self.label.set_text('idle') 

    def on_button_clicked(self, widget): 
     Gdk.threads_enter() 
     working1() 
     Gdk.threads_leave() 

if __name__ == '__main__': 
    gui = gui() 

此代碼將產生該GUI: img http://i42.tinypic.com/33nvrx2.png

和我有第二模件,其將執行邏輯。

Process.py

import threading 

class Heavy(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def heavyworks1(self): 
     #doing heavy works1 
     #return result 

    def heavyworks2(self, *param): 
     #doing heavy works2 
     #return result 

當我執行此,操作的工作,但是GUI變得凍結。如何做得好?

編輯:

爲user4815162342說,我改變我的代碼如下:

from gi.repository import Gtk, Gdk, GLib 
import Process 
import gobject 
import threading 

class gui(): 
def __init__(self): 
    self.window = Gtk.Window() 
    self.window.connect('delete-event', Gtk.main_quit) 

    self.box = Gtk.Box() 
    self.window.add(self.box) 

    self.label = Gtk.Label('idle') 
    self.box.pack_start(self.label, True, True, 0) 

    self.progressbar = Gtk.ProgressBar() 
    self.box.pack_start(self.progressbar, True, True, 0) 

    self.button = Gtk.Button(label='Start') 
    self.button.connect('clicked', self.on_button_clicked) 
    self.box.pack_start(self.button, True, True, 0) 

    self.window.show_all() 

    gobject.threads_init() 
    GLib.threads_init() 
    Gdk.threads_init() 
    Gdk.threads_enter() 
    Gtk.main() 
    Gdk.threads_leave() 

def init_progress(self, func, arg): 
    self.label.set_text('working1') 
    self.worker = threading.Thread(target=func, args=[arg]) 
    self.running = True 
    gobject.timeout_add(200, self.update_progress) 
    self.worker.start() 

def update_progress(self): 
    if self.running: 
     self.progressbar.pulse() 
    return self.running 

def working(self, num): 
    Process.heavyworks2(num)  
    gobject.idle_add(self.stop_progress) 

def stop_progress(self): 
    self.running = False 
    self.worker.join() 
    self.progressbar.set_fraction(0) 
    self.label.set_text('idle') 

def on_button_clicked(self, widget): 
    self.init_progress(self.working, 100000) 

if __name__ == '__main__': 
    gui = gui() 

與該代碼,程序有時工作,但有時收到此錯誤。

1.

** 
Gtk:ERROR:/build/buildd/gtk+3.0-3.4.2/./gtk/gtktextview.c:3726:gtk_text_view_validate_onscreen: assertion failed: (priv->onscreen_validated) 
Aborted (core dumped) 

2.

*** glibc detected *** python: free(): invalid next size (fast): 0x09c9f820 *** 

3.

Segmentation fault (core dumped) 

回答

3

你實際上並沒有開始線程,你只實例化,可以用來啓動它的對象。完整的解決方案需要仔細分離GUI線程和工作線程之間的責任。你想要做的是:

  1. 不要在單獨的線程,催生並通過GUI代碼加入你的繁重的計算。計算不應該產生自己的線程,也不需要知道線程(當然,除了線程安全外)。

  2. 線程完成後,使用gobject.idle_add()告訴GUI可以撤銷進度指示器。 (gobject.idle_add是他們唯一GTK功能安全地從另一個線程調用。)

有了這樣的設置中,GUI仍然充分響應和進度條無論計算做什麼更新和GUI線程保證在計算完成時注意。關於當前的代碼另外有兩點:

  • 實例化threading.Thread而不是從它繼承。這樣你就不必費心執行run()。不過,在這兩種情況下,您必須致電thread.start()才能啓動線程。

  • 不要叫threads_enter()threads_leave(),除非你真的知道你在做什麼。只要記住,只要你從一個線程(與初始化GTK相同的線程)調用所有的GTK函數,你就會好起來的。

這裏是證明了概念代碼實現上述建議:

def working1(self): 
     self.label.set_text('working1') 
     self.work_thread = threading.Thread(self.run_thread) 
     self.running = True 
     gobject.timeout_add(200, self.update_progress) 
     self.work_thread.start() 
     # the GUI thread now returns to the mainloop 

    # this will get periodically called in the GUI thread 
    def update_progress(self): 
     if self.running: 
      self.progressbar.pulse() # or set_fraction, etc. 
     return self.running 

    # this will get run in a separate thread 
    def run_thread(self): 
     Process.heavyworks1()  # or however you're starting your calculation 
     gobject.idle_add(self.stop_progress) 

    # this will get run in the GUI thread when the worker thread is done 
    def stop_progress(self): 
     self.running = False 
     self.work_thread.join() 
     self.label.set_text('idle') 
+0

謝謝,它的工作原理,但有時我得到錯誤。看看我的更新。 – user2435611

+0

啊,我知道我的問題。還有一些代碼可以從我的線程訪問gtk ui:p – user2435611

+0

@ user2435611很好聽。這個答案可能對其他嘗試相同的人有用;如何正確使用PyGTK和線程並不明顯。此外,如果您的崩潰已解決,請不要忘記回答[其他問題](http://stackoverflow.com/questions/16947842/segmentation-fault-in-python)並接受它。 – user4815162342

0

如你所說,你需要啓動另一個線程這一點。通常使用python進行線程化非常簡單,但使用GUI可能會變得棘手。

這應該是幫助:Python. Doing some work on background with Gtk GUI

+0

不錯,我已經看到了,但我錯過的是'GLib.threads_init()':d但,由於重工不能迭代,所以如何使進度條脈動? – user2435611

+0

當我點擊那個按鈕,而仍在運行的東西,爲什麼我得到這個錯誤? '*** glibc detected *** python:double free or corruption(!prev):0x09154320 ***'還有另一個像這樣的錯誤'Gtk:ERROR:/build/buildd/gtk+3.0-3.4.2/ ./gtk/gtktextview.c:3726:gtk_text_view_validate_onscreen:斷言失敗:(priv-> onscreen_validated) 中止(核心轉儲)' – user2435611

+0

sry這是非常gtk特定的,我沒有與此合作。 – pypat