How threads works in pygtk using the threading module?
Threads are a way for a program to split itself into two or more simultaneously running tasks. The way that we create threads in python is writing subclasses of the threading.Thread class, the method run, should be overloaded so we include there the code that we want to be rant on each thread.
In order to create a new thread, we should create a new Thread object and then, call the start method which creates the new thread, and executes the run method in its context.
The problem with PyGTK is that the gtk.main() loop won't refresh the screen each time that the fraction is set, we need to use the gtk thread engine. We need to call the gtk.thread_init() function to start the thread engine, this call should be made on the same thread that the gtk.main() function is called. In fact, calling it right afer importing the gtk module is a good practice.
Thus, we should use gtk.threads_enter() before any access to gtk object, and, once we end up doing transformations, we should call gtk.threads_leave().
We should call to the stop method in the exit callback so when the window is closed the application ends the thread and the script doesn't hangs.
只允許主線程修改圖形界面
這是較常用的一種方式,但編寫時略顯複雜。首先,在程序初始化時調用(一定在gtk.main()之前運行):gobject.threads_init()多線程的實現方式與純Python相同:
t=threading.Thread(target=新线程的函数) t.start()
gobject.idle_add(新线程函数, 参数)
import threading import time import gobject import gtk gobject.threads_init() class MyThread(threading.Thread): def __init__(self, label): super(MyThread, self).__init__() self.label = label self.quit = False def update_label(self, counter): self.label.set_text("Counter: %i" % counter) return False def run(self): counter = 0 while not self.quit: counter += 1 gobject.idle_add(self.update_label, counter) time.sleep(0.1) w = gtk.Window() l = gtk.Label() w.add(l) w.show_all() w.connect("destroy", lambda _: gtk.main_quit()) t = MyThread(l) t.start() gtk.main() t.quit = True
允許所有線程修改圖形界面
這種方法要簡單很多。與前面類似,在程序初始化時調用:gtk.gdk.threads_init()不過要把gtk.main()用下面兩個函數括起來:
gtk.threads_enter() gtk.main() gtk.threads_leave()其它線程也是如此,在修改界面內容的代碼前後加上
gtk.threads_enter()
...
gtk.threads_leave()
The following example shows how to do it using a random fraction for the progress bar:
import threading import random, time import gtk #Initializing the gtk's thread engine gtk.threads_init() class FractionSetter(threading.Thread): """This class sets the fraction of the progressbar""" #Thread event, stops the thread if it is set. stopthread = threading.Event() def run(self): """Run method, this is the code that runs while thread is alive.""" #Importing the progressbar widget from the global scope global progressbar #While the stopthread event isn't setted, the thread keeps going on while not self.stopthread.isSet() : # Acquiring the gtk global mutex gtk.threads_enter() #Setting a random value for the fraction progressbar.set_fraction(random.random()) # Releasing the gtk global mutex gtk.threads_leave() #Delaying 100ms until the next iteration time.sleep(0.1) def stop(self): """Stop method, sets the event to terminate the thread's main loop""" self.stopthread.set() def main_quit(obj): """main_quit function, it stops the thread and the gtk's main loop""" #Importing the fs object from the global scope global fs #Stopping the thread and the gtk's main loop fs.stop() gtk.main_quit() #Gui bootstrap: window and progressbar window = gtk.Window() progressbar = gtk.ProgressBar() window.add(progressbar) window.show_all() #Connecting the 'destroy' event to the main_quit function window.connect('destroy', main_quit) #Creating and starting the thread fs = FractionSetter() fs.start() gtk.main()
Refer to:
http://aruiz.typepad.com/siliconisland/2006/04/threads_on_pygt.html
http://docs.python.org/library/threading.html