I have a two-thread application: GUI, and some background work. I'm trying to send requests to the main thread to do GUI updates (move a progress bar), but it doesn't seem to work. I've boiled it down to a really minimal example:
import pygtk
pygtk.require('2.0')
import glib
import gtk
import threading
import sys
import time
def idle():
sys.stderr.write('Hello from another world.\n')
sys.stderr.flush()
gtk.main_quit()
def another_thread():
time.sleep(1)
glib.idle_add(idle)
thread = threading.Thread(target=another_thread)
thread.start()
gtk.main()
This should, I thought, print something to standard error from the main/GUI thread, but nothing happens. And it doesn't quit, either, so gtk.main_quit
isn't being called.
Also, adding more output to stderr
acts weirdly. If I change the thread's function to:
sys.stderr.write('----\n')
sys.stderr.write('----\n')
sys.stderr.flush()
sys.stderr.write('After.\n')
sys.stderr.flush()
I see 1, sometimes 2 lines out output. It looks like some kind of race condition with the main thread entering gtk.main
, but I don't know why this would be.
You need to init glib's thread support before using glib in a multi-threaded environment. Just call:
glib.threads_init()
Before calling into glib functions.
Why not use glib.timeout_add_seconds(1, idle)
and return False
from idle()
instead of starting a thread and then sleeping 1 second? Starting an idle function from another thread is quite redundant, since idle functions already run in another thread.
EDIT:
By "starting an idle function from another thread is redundant", I meant that you don't have to start an idle function in order to mess with the GUI and update the progress bar. It is a myth that you can't mess with the GUI in other threads.
Let me restate here what is needed to do GTK calls from other threads:
glib.threads_init()
or gobject.threads_init()
, whichever you have, as discussed in vanza's answer.gtk.gdk.threads_init()
. I am pretty sure you are right in your answer, this only has to be called before gtk.main()
. The C docs suggest calling it before gtk_init()
, but that's called at import time in PyGTK if I'm not mistaken.gtk.main()
between gtk.gdk.threads_enter()
and gtk.gdk.threads_leave()
.Bracket any calls to GTK functions from:
between gtk.gdk.threads_enter()
and gtk.gdk.threads_leave()
.
Note that instead of surrounding your calls with enter
/leave
pairs, you can also use with gtk.gdk.lock:
and do your calls within that with
-block.
Here are some resources which also explain the matter:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With