Redrwaing a widget or window

John Smith someone at microsoft.com
Thu Dec 26 09:24:51 EST 2002


Jeff:

Many thanks.  This is exactly what I am looking for.

Jeff Epler wrote:

> On Thu, Dec 26, 2002 at 02:57:59AM +0000, John Smith wrote:
>> I would like to draw an item on a canvas, wait a short period, draw
>> another
>> item, etc.  So far my class never draws until the loop has run its full
>> course.  Any way to force a canvas, frame, or window to redraw?
>> 
>> John Smith
>> --
>> http://mail.python.org/mailman/listinfo/python-list
> 
> Yes.  The .update, .update_idletasks, .after, and .after_idle methods are
> all possibly relevant to this.
> 
> If you want to update the GUI and then go on to do more calculation,
> call .update_idletasks or .update() (to also handle events).  If you want
> to delay an approximately fixed length of time and then move, use .after()
> with no function argument.  And if you want to act in an event-driven way,
> use .after() with a callback that does the next step.
> 
> For instance, here are several ways to draw a line gliding across a
> canvas.
> 
> The first method goes very quickly (there is no 'after' to impose a delay)
> and doesn't respond to events (since only update_idletasks() is called).
> 
> 
> In the second method, there's a >100ms (1/10 second) delay between
> updates. Because update_idletasks() is called, the GUI is nonresponsive
> during the
> ~10 seconds it takes to sweep the line.  If this call were changed to
> update() the GUI would be responsive, but this would also mean the user
> could enter demo1() or another function recursively.  In this case both
> incarnations of the function will use the same canvas 'c' and line 'l', so
> if you press 'demo1' again halfway through, the line will do the full 100
> pixel sweep (in the "inner" invocation of demo1) and then return to the
> halfway point to finish the other 100 pixel sweep (in the "outer"
> invocation of demo1).
> 
> In the third method, after(100, callback) is used.  The GUI remains
> responsive, and there's no way to enter the routine "recursively".
> However, there can still be two instances of the "demo2" class each being
> called periodically.  This time, in their "quarrel", the after to be run
> last "wins" and controls the position of the displayed line.
> 
> Note that if you are using a version which doesn't handle events, other
> parts of your window will not redraw, though the parts of the canvas that
> change will be updated.
> 
> If you are worried about the user invoking the same function twice, but
> need the GUI to refresh if it is exposed, you must insert functionality
> to disable the button before you do work that will service events.  This
> is akin to a "race condition" in threaded code, and can be similarly hard
> to
> trigger.  In this case, you'd do something like the following (untested):
> 
>     class Demo3:
> def __init__(self, b):
> self.b = b
> 
> def go(self):
> self.i = 0
> self.b.configure(state="disabled")
> c.after(10, self.step)
> 
> def step(self):
> i = self.i = self.i + 1
> c.coords(l, (i, 0, i, 100))
> if i < 100:
> c.after(10, self.step)
> else:
> self.b.configure(state="normal")
>     b = Tkinter.Button(t, text="demo 3")
>     b.pack()
>     b.configure(command = Demo3(b).go)
>     
> Anyway, here's the code for the three basic approaches I described above:
> 
> ------------------------------------------------------------------------
> # sweep.py
> # demo of different ways to animate a line on a canvas
> 
> import Tkinter
> 
> t = Tkinter.Tk()
> c = Tkinter.Canvas(t)
> c.pack()
> l = c.create_line((0,0,0,100))  # All demos use the same canvas and line
> Tkinter.Button(t, command=t.destroy, text="Close").pack()
> 
> def demo0():
>     for i in range(100):
> c.coords(l, (i, 0, i, 100))
> c.update_idletasks()
> Tkinter.Button(t, command=demo0, text="demo 0").pack()
> 
> 
> def demo1():
>     for i in range(100):
> c.coords(l, (i, 0, i, 100))
> c.update_idletasks()
> c.after(100)
> Tkinter.Button(t, command=demo1, text="demo 1").pack()
> 
> class demo2:
>     def __init__(self):
> self.i = 0
> c.after(10, self.step)
> 
>     def step(self):
> i = self.i = self.i + 1
> c.coords(l, (i, 0, i, 100))
> if i < 100:
> c.after(10, self.step)
> Tkinter.Button(t, command=demo2, text="demo 2").pack()
> 
> t.mainloop()
> ------------------------------------------------------------------------




More information about the Python-list mailing list