Search code examples
pythonpython-3.xwhile-loopturtle-graphicspython-turtle

Python Turtle becomes unresponsive and hangs after using a while loop


I'm trying to make a program that is essentially an etchasketck, with some minor tweaks for my school project(the required use of a main function, wasd as movement controls, and q to quit the program and p to pick up or drop down the turtle pen). I was testing this code in trinket.io and it was working fine. You can see it working there with this link: https://trinket.io/python/99fd3ec305. However, when I go to run it from pycharm, cmd, or the python IDLE, it always leaves the turtle hanging and unresponsive. I get no errors, just the turtle to pop up for a few seconds, then it hangs, and I'm unable to do anything. Here's my code:

import sys
import turtle

arg_len = len(sys.argv)

# store length of arguments

if arg_len < 3:
    print("Too less arguments, using default values..")
    WIDTH = 200
    HEIGHT = 200
else:
    WIDTH = int(sys.argv[1])
    HEIGHT = int(sys.argv[2])

screen = turtle.Screen()
screen.setup(WIDTH, HEIGHT)
# create a turtle instance
t = turtle.Turtle()
t.speed(0)
# slow down the turtle
# declare flags
move = False
exit_flag = False


def up():
    global move
    move = True
    t.setheading(90)


def down():
    global move
    move = True
    t.setheading(270)


def left():
    global move
    move = True
    t.setheading(180)


def right():
    global move
    move = True
    t.setheading(0)


# toggle pen up and down
def toggle_pen():
    if t.isdown():
        t.penup()
    else:
        t.pendown()


# set exit flag
def quit_program():
    global exit_flag
    exit_flag = True


def check_border():
    if t.xcor() == WIDTH / 2:
        t.penup()
        t.setx(-WIDTH / 2)
    elif t.xcor() == -WIDTH / 2:
        t.penup()
        t.setx(WIDTH / 2)
    if t.ycor() == HEIGHT / 2:
        t.penup()
        t.sety(-HEIGHT / 2)
    elif t.ycor() == -HEIGHT / 2:
        t.penup()
        t.sety(HEIGHT / 2)


def listen_keys():
    screen.listen()
    screen.onkey(up, "w")
    screen.onkey(down, "s")
    screen.onkey(left, "a")
    screen.onkey(right, "d")
    screen.onkey(toggle_pen, "p")
    screen.onkey(quit_program, "q")


# main loop
def main():
    listen_keys()
    while not exit_flag:
        global move
        if move:
            t.forward(0.5)
            screen.update()
        check_border()
    else:
        t.done()

main()

I'm using python 3.10, and I mainly use pycharm for the running.

I was trying to get the turtle to move indefinitly without user input after the first user input, I was going to achieve this with the while loop, but it just leaves my program unresponsive. Can anyone tell me what's wrong that I'm not seeing?


Solution

  • You've effectively put a while True: loop in an event-driven program where one should never be used. If I were writing this for the command line, with the constraints listed, I might go about it this way (dropping command line processing for example simplicity):

    from turtle import Screen, Turtle
    
    WIDTH, HEIGHT = 200, 200
    
    def up():
        turtle.setheading(90)
        start_motion()
    
    def down():
        turtle.setheading(270)
        start_motion()
    
    def left():
        turtle.setheading(180)
        start_motion()
    
    def right():
        turtle.setheading(0)
        start_motion()
    
    def toggle_pen():
        if turtle.isdown():
            turtle.penup()
        else:
            turtle.pendown()
    
    def quit_program():
        screen.bye()
    
    def check_border():
        x, y = turtle.position()
    
        if x >= WIDTH / 2:
            turtle.penup()
            turtle.setx(-WIDTH / 2)
        elif x <= -WIDTH / 2:
            turtle.penup()
            turtle.setx(WIDTH / 2)
    
        if y >= HEIGHT / 2:
            turtle.penup()
            turtle.sety(-HEIGHT / 2)
        elif y <= -HEIGHT / 2:
            turtle.penup()
            turtle.sety(HEIGHT / 2)
    
    def main():
        screen.onkey(up, 'w')
        screen.onkey(down, 's')
        screen.onkey(left, 'a')
        screen.onkey(right, 'd')
    
        screen.onkey(toggle_pen, 'p')
        screen.onkey(quit_program, 'q')
    
        screen.listen()
        screen.mainloop()
    
    def move():
        turtle.forward(1)
    
        check_border()
    
        screen.ontimer(move, 25)
    
    moving = False
    
    def start_motion():
        global moving
    
        if not moving:
            moving = True
            move()
    
    screen = Screen()
    screen.setup(WIDTH, HEIGHT)
    
    turtle = Turtle()
    turtle.speed('fastest')
    
    main()
    

    See if this still works with trinket.io, IDLE and PyCharm, or if it can be made to do so.