Recently I have been integrating some code which liberally used sleep() for its timing into a GTK main loop. I tried several different solutions and thought I’d mention a couple here.
The cutest one was to run the function with the sleep calls as a co-routine alongside a glib timer. "sleep (n)" gets replaced with "yield n".
import gobject import gtk def run_delayed_generator (generator): """Run this function with a generator as it's argument. Whenever that function performs a "yield n" then the function will be delayed by n seconds. i.e. "yield n" acts like "sleep (n)" without blocking the GTK main loop.""" try: t = generator.next () gobject.timeout_add (int (t*1000), run_delayed_generator, generator) except StopIteration: pass return False def testsleeper (): print "a" yield 1 print "b" yield 2 print "c" yield 3 print "d" gtk.main_quit () run_delayed_generator (testsleeper ()) gtk.main ()
Or, using a decorator:
def run_delayed (fn): def wrapper (*args): run_delayed_generator (fn (*args)) return wrapper @run_delayed def testsleeper (): . . .
It’s got aesthetic appeal, but as the name “run_delayed” suggests, it isn’t really a sleep function. All hell breaks loose if a function using sleep calls another function using sleep. This approach does have its uses though: aisleriot uses a similar approach (written in a mix of scheme an C) to do the animations for auto-moves.
What I actually used was this:
def sleep (t): gobject.timeout_add (int (t*1000), gtk.main_quit) gtk.main ()
Far simpler, but you have to be very careful about calling gtk.main_quit () and there has to be a trailing gtk.main () to catch everything that falls off the end – but the code would normally have that anyway.