我在Windows上遇到线程和PyGTK的问题。 根据PyGTK FAQ (以及我自己的实验),从子线程可靠地更新GUI的唯一方法是使用gobject.idle_add
函数。 但是,这个函数被调用的时候不能保证。 我怎样才能保证gobject.idle_add
后面的行在它指向的函数后被调用?
非常简单和人为的例子:
import gtk import gobject from threading import Thread
class Gui(object): def __init__(self): self.button = gtk.Button("Click") self.button.connect("clicked", self.onButtonClicked) self.textEntry = gtk.Entry() self.content = gtk.HBox() self.content.pack_start(self.button) self.content.pack_start(self.textEntry) self.window = gtk.Window() self.window.connect("destroy", self.quit) self.window.add(self.content) self.window.show_all()
def onButtonClicked(self, button): Thread(target=self.startThread).start()
def startThread(self): #I want these next 2 lines to run in order gobject.idle_add(self.updateText) print self.textEntry.get_text()
def updateText(self): self.textEntry.set_text("Hello!")
def quit(self, widget): gtk.main_quit()
import gtk import gobject from threading import Thread
class Gui(object): def __init__(self): self.button = gtk.Button("Click") self.button.connect("clicked", self.onButtonClicked) self.textEntry = gtk.Entry() self.content = gtk.HBox() self.content.pack_start(self.button) self.content.pack_start(self.textEntry) self.window = gtk.Window() self.window.connect("destroy", self.quit) self.window.add(self.content) self.window.show_all()
def onButtonClicked(self, button): Thread(target=self.startThread).start()
def startThread(self): #I want these next 2 lines to run in order gobject.idle_add(self.updateText) print self.textEntry.get_text()
def updateText(self): self.textEntry.set_text("Hello!")
def quit(self, widget): gtk.main_quit()
gobject.threads_init()
x = Gui()
gtk.main()
不要尝试从线程更新或访问您的GUI。 你只是在问问题。 例如,“ get_text
”在一个线程中的作用几乎是一个意外。 你可能可以依靠它在GTK中 – 虽然我甚至不确定 – 但是在其他GUI工具包中你将无法做到这一点。
如果你有真正需要在线程中做的事情,你应该在启动线程之前从GUI获取所需的数据,然后使用idle_add
从线程更新GUI,如下所示:
import time import gtk import gobject from threading import Thread w = gtk.Window() h = gtk.HBox() v = gtk.VBox() addend1 = gtk.Entry() h.add(addend1) h.add(gtk.Label(" + ")) addend2 = gtk.Entry() h.add(addend2) h.add(gtk.Label(" = ")) summation = gtk.Entry() summation.set_text("?") summation.set_editable(False) h.add(summation) v.add(h) progress = gtk.ProgressBar() v.add(progress) b = gtk.Button("Do It") v.add(b) w.add(v) status = gtk.Statusbar() v.add(status) w.show_all() def hardWork(a1, a2): messages = ["Doing the hard work to add %s to %s..." % (a1, a2), "Oof, I'm working so hard...", "Almost done..."] for index, message in enumerate(messages): fraction = index / float(len(messages)) gobject.idle_add(progress.set_fraction, fraction) gobject.idle_add(status.push, 4321, message) time.sleep(1) result = a1 + a2 gobject.idle_add(summation.set_text, str(result)) gobject.idle_add(status.push, 4321, "Done!") gobject.idle_add(progress.set_fraction, 1.0) def addthem(*ignored): a1 = int(addend1.get_text()) a2 = int(addend2.get_text()) Thread(target=lambda : hardWork(a1, a2)).start() b.connect("clicked", addthem) gtk.gdk.threads_init() gtk.main()
如果真的,绝对需要从线程中间的GUI读取数据(这是一个非常糟糕的主意,不要这样做 – 你可以进入真正令人惊讶的死锁,特别是当程序关闭时)一个在Twisted中的实用程序, blockingCallFromThread ,这将为你做的辛苦工作。 你可以像这样使用它:
from twisted.internet.gtk2reactor import install install() from twisted.internet import reactor from twisted.internet.threads import blockingCallFromThread from threading import Thread import gtk w = gtk.Window() v = gtk.VBox() e = gtk.Entry() b = gtk.Button("Get Text") v.add(e) v.add(b) w.add(v) def inThread(): print 'Getting value' textValue = blockingCallFromThread(reactor, e.get_text) print 'Got it!', repr(textValue) def kickOffThread(*ignored): Thread(target=inThread).start() b.connect("clicked", kickOffThread) w.show_all() reactor.run()
如果你想看看魔法是如何运作的,你可以随时阅读资料来源 。
你可以把这两个函数包装到另一个函数中,并在这个函数上调用idle_add:
def update_and_print(self): self.updateText() print self.textEntry.get_text() def startThread(self): gobject.idle_add(self.update_and_print)