1

I have read in several posts here on Stack Overflow that "An event-driven environment like turtle should never have while True: as it potentially blocks out events (e.g. keyboard)."

Here is a Python Turtle program that seems to work fine, but which uses the while True: construct.

Could someone please explain why this approach is wrong, what problems is creates and what the correct way is to achieve the same result?

import turtle
import time


def move_snake():
    """
    This function updates the position of the snake's head according to its direction.
    """
    if head.direction == "up":
        head.sety(head.ycor() + 20)

def go_up():
    """
    callback for up key.
    """
    if head.direction != "down":
        head.direction = "up"

# Set up screen
screen = turtle.Screen()
screen.tracer(0)  # Disable animation so we can update screen manually.

# Event handlers
screen.listen()
screen.onkey(go_up, "Up")

# Snake head
head = turtle.Turtle()
head.shape("square")
head.penup()
head.direction = "stopped"  # Cheeky use of instance property to avoid global variable.


while True:
    move_snake()
    screen.update()
    time.sleep(0.2)

turtle.done()
Robin Andrews
  • 3,514
  • 11
  • 43
  • 111
  • It's not wrong. It's a mainloop which makes program possible to run. How would you do this without main loop which polls keyboard? – Javed Mar 07 '20 at 08:11
  • There is already a mainloop running in the background. `turtle.done()` is equivalent to `screen.mainloop()` I believe. The keyboard event listener is set up by `screen.listen()`. – Robin Andrews Mar 07 '20 at 08:13
  • Well I'm sorry then, but I cant see how it's even comes to turtle.done() line, there is no exit from infinite loop. – Javed Mar 07 '20 at 08:19
  • I can see why you would say that. Yet the program works! `turtle.done()` is really only needed to ensure a clean exit (by enabling exit-on-click). I think when a `turtle` or `screen` object is created, a `tkinter` mainloop is implicitly initiated. – Robin Andrews Mar 07 '20 at 08:35
  • The `done()` method doesn't enable exit-on-click, `exitonclick()` does. A `tkinter` mainloop is not implcitly intiated when a `Turtle` or screen object is created -- control has to be turned over to it explicitly. – cdlane Mar 08 '20 at 02:10
  • And yet done() and mainloop() also seem to provide the same funtionality as exitonclick(), with done() being used alone in the example in the docs. https://docs.python.org/3/library/turtle.html#turtle.done – Robin Andrews Mar 08 '20 at 06:40

1 Answers1

1

I can provide a crude example. Run your code above as-is. Start the snake moving. Click on the window's close button. Count the number of lines of error messages you get in the console. It could easily exceed two dozen.

Now try this same experiment with the following code which eliminates the while True::

from turtle import Screen, Turtle

class Head(Turtle):
    def __init__(self):
        super().__init__(shape="square")

        self.penup()

        self.direction = "stopped"

def move_snake():
    if head.direction == "up":
        head.sety(head.ycor() + 20)
        screen.update()

    screen.ontimer(move_snake, 200)

def go_up():
    if head.direction != "down":
        head.direction = "up"

# Snake head
head = Head()

# Set up screen
screen = Screen()
screen.tracer(0)  # Disable animation so we can update screen manually.

# Event handlers
screen.onkey(go_up, "Up")
screen.listen()

move_snake()

screen.mainloop()

Your count of error messages should drop to zero. This is because the window close event happens in the same event loop as turtle motion.

There are other effects that you'll end up chasing later. This is just a simple, easily visible one.

cdlane
  • 40,441
  • 5
  • 32
  • 81