We're back after a server migration that caused effbot.org to fall over a bit harder than expected. Expect some glitches.

Using generators to sleep without blocking

February 12, 2002 | Fredrik Lundh

The following Tkinter snippet draws a random polyline, one segment at a time.

The main work is done inside the draw method, which uses yield x to sleep for x milliseconds. The yield statement transfers control to the slowmotion wrapper, which tells Tkinter to process events as usual, and call back after the given time. When Tkinter calls the scheduled function, we end up in draw, just after the yield statement. The function draws another line, and goes back to sleep.

from __future__ import generators # only needed in 2.2

import random
from Tkinter import *

w, h = 500, 500

root = Tk()
cnv = Canvas(root, width=w, height=h)
cnv.pack(side=TOP)
Button(root, text='Enough', command=root.quit).pack(side=TOP)

def draw():
    x, y = random.randrange(w), random.randrange(h)
    while 1:
        oldx, oldy = x, y
        x, y = random.randrange(w), random.randrange(h)
        cnv.create_line(oldx, oldy, x, y)
        yield 500 # sleep for 500 ms

def slowmotion(iterator):
    # sleep a while, and then go back to the loop
    root.after(iterator.next(), slowmotion, iterator)

slowmotion(draw()) # schedule the first round

mainloop() # get things going

The important thing here is that the event loop keeps running. Most of the time is spent inside the Tkinter event loop, waiting to call the slowmotion helper again. Other widgets can process their events as usual, and you can click the “Enough” button at any time.