Don't try to update or access your GUI from a thread. You're just asking for trouble. For example, the fact that "get_text
" works at all in a thread is almost an accident. You might be able to rely on it in GTK - although I'm not even sure about that - but you won't be able to do so in other GUI toolkits.
If you have things that really need doing in threads, you should get the data you need from the GUI before launching the thread, and then update the GUI from the thread by using idle_add
, like this:
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()
If you really, absolutely need to read data from the GUI in the middle of a thread (this is a really bad idea, don't do it - you can get into really surprising deadlocks, especially when the program is shutting down) there is a utility in Twisted, blockingCallFromThread, which will do the hard work for you. You can use it like this:
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()
If you want to see how the magic works, you can always read the source.