2009/12/15

PyGTK下编写多线程程序

没有评论:
虽然在PyGTK中Python有关多线程的库(thread,threading)可以使用,但其行为常现异常,所以一定要按照PyGTK的要求编写多线程应用程序。

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加到GTK的主循環中。
gobject.idle_add(新线程函数, 参数)
注意如果新線程函數返回True,則該函數仍在GTK的主循環中,如果返回False,則退出主循環。下面的例子是PyGTK.org上給出的:
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