Turtle graphics – screen updates with tracer not working as expected
Question:
I’m trying to put in scheduled refreshes for a pong game, so the screen updates after a time interval so the paddles (which I have segmented) move in sync on the screen. What is the functional difference between these two blocks of code? I was trying to get the first one to work but wasn’t able to get it to do what I wanted but after some experimenting I landed at the second one which does:
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
time.sleep(0.5)
balltiming()
balltiming()
pongscreen.listen()
pongscreen.mainloop()
This second one works, but functionally they seem pretty much the same in what they’re doing. Why doesn’t the first one work but this does?
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
pongscreen.ontimer(balltiming, 300)
balltiming()
pongscreen.listen()
pongscreen.mainloop()
Answers:
The first block is genuinely recursive, so it’ll blow the call stack after ~1000 frames on a typical CPython implementation.
The second block isn’t recursive; the initial balltiming
function is completely cleared from the call stack after it sets an ontimer
event. Turtle internally calls the next iteration of balltiming
that has been registered as a callback to ontimer
without unbounded stack growth.
If you want to use sleep
manually, use a while
loop rather than recursion. However, sleep
blocks the thread and is less amenable to precise framerate control, so generally ontimer
is used.
A consequence of the blocking recursive calls and sleep
is that in the first code block, pongscreen.listen()
and pongscreen.mainloop()
won’t be reached, while in the second version, both will be reached and the main thread will block at pongscreen.mainloop()
; the typical flow for a turtle program.
I’m trying to put in scheduled refreshes for a pong game, so the screen updates after a time interval so the paddles (which I have segmented) move in sync on the screen. What is the functional difference between these two blocks of code? I was trying to get the first one to work but wasn’t able to get it to do what I wanted but after some experimenting I landed at the second one which does:
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
time.sleep(0.5)
balltiming()
balltiming()
pongscreen.listen()
pongscreen.mainloop()
This second one works, but functionally they seem pretty much the same in what they’re doing. Why doesn’t the first one work but this does?
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
pongscreen.ontimer(balltiming, 300)
balltiming()
pongscreen.listen()
pongscreen.mainloop()
The first block is genuinely recursive, so it’ll blow the call stack after ~1000 frames on a typical CPython implementation.
The second block isn’t recursive; the initial balltiming
function is completely cleared from the call stack after it sets an ontimer
event. Turtle internally calls the next iteration of balltiming
that has been registered as a callback to ontimer
without unbounded stack growth.
If you want to use sleep
manually, use a while
loop rather than recursion. However, sleep
blocks the thread and is less amenable to precise framerate control, so generally ontimer
is used.
A consequence of the blocking recursive calls and sleep
is that in the first code block, pongscreen.listen()
and pongscreen.mainloop()
won’t be reached, while in the second version, both will be reached and the main thread will block at pongscreen.mainloop()
; the typical flow for a turtle program.