I am trying to get the turtle to stop moving forward when I press the escape key. When I press escape, nothing happens! Can anyone tell me why? A solution would be greatly appreciated.
import turtle
screen = turtle.Screen()
running = True
def stop():
running = False
print(running)
while running:
turtle.forward(1)
screen.onkey(stop, "Esc")
screen.listen()
I see several problems with your code. The primary one is a missing global statement in stop(). Secondary ones include: mixing the turtle function and object APIs; using key name 'Esc' instead of 'Escape'; putting onkey() and listen() in a loop; and potentially blocking events with your while loop.
I believe this code should do what you want:
from turtle import Screen, Turtle
running = True
def stop():
global running
running = False
def run():
if running:
turtle.forward(1)
screen.ontimer(run)
screen = Screen()
screen.onkey(stop, 'Escape')
screen.listen()
turtle = Turtle()
run()
screen.mainloop()
Related
I am trying to create a turtle crossing game but every time I run the program neither the screen.listen() gets executed nor the screen.exitonclick()
After running the program on clicking on the turtle window it does not close neither the turtle moves forward
import turtle
from turtle import Screen
from player import Player
import time
screen = Screen()
screen.setup(width=600, height=600)
screen.tracer(0)
player = Player()
screen.listen()
screen.onkey(player.go_up(), "Up")
turtle.TurtleScreen._RUNNING = True
game_is_on = True
while game_is_on:
time.sleep(0.1)
screen.update()
screen.exitonclick()
Although I tried adding the ._RUNNING method, yet it does not make any difference
There are a few issues here:
while game_is_on: is an infinite loop since game_is_on is never changed from True to False. Anything after the loop will never run. Avoid using this pattern; the typical way to make a real-time rendering loop in turtle is ontimer.
turtle.TurtleScreen._RUNNING = True messes with an undocumented internal property. Unless you have an absolute need to, you shouldn't touch internal properties in libraries because you may be corrupting the instance's state and the property can disappear after an update. I'm not sure what you're trying to do here, but either figure out a way using the public API or drop this code entirely if it's not really needed (I don't think it is--I've never used it in a turtle program).
Although the code for Player wasn't posted, screen.onkey(player.go_up(), "Up") is likely incorrect. It invokes the go_up() method immediately and sets its return value, probably None, as the onkey handler. You probably meant screen.onkey(player.go_up, "Up") which doesn't call the method immediately, but instead passes it to the handler so it can be called later on by the turtle library, when the key is pressed.
With a little stub for Player, I'd suggest a setup like:
import turtle
class Player:
def __init__(self):
self.turtle = turtle.Turtle()
def go_up(self):
self.turtle.setheading(90)
self.turtle.forward(10)
def tick():
#### the main loop; move/update entities here ####
screen.update()
screen.ontimer(tick, 1000 // 30)
screen = turtle.Screen()
screen.setup(width=600, height=600)
screen.tracer(0)
player = Player()
screen.onkey(player.go_up, "Up")
screen.listen()
tick()
screen.exitonclick()
Now, you don't have any code in tick yet. This is the main update/rendering loop. The player's movement will be jerky because it's directly wired to the keyboard's retrigger mechanism. If this behavior isn't what you want, I suggest reacting to key presses and changing the player's position only inside of tick. See How to bind several key presses together in turtle graphics? for an example of this.
I just discovered the turtle module, and I'm trying to use it. I'd like to run a program that draws a static graphic, and then close the window when the space bar is pressed. This program draws the graphic just fine – but then nothing happens when I press the – and fairly soon the blue wheel is spinning, and the window has become unresponsive in the mind of Windows.
How to do better? How to wait while remaining a "responsive window"?
Python 3.9,
Windows 10
import turtle
from time import sleep
t = turtle.Turtle()
turtle.onkey(turtle.bye, ' ')
t.forward(150)
t.rt(108)
while True:
sleep(0.2)
You're missing a call to the listen() method so your key press won't be heard. Also, don't reinvent the event loop -- neither while True: nor sleep() belong in an event-driven world like turtle:
from turtle import Screen, Turtle
turtle = Turtle()
turtle.forward(150)
turtle.right(108)
screen = Screen()
screen.onkey(screen.bye, ' ')
screen.listen()
screen.mainloop()
I'm trying to use python 3 turtle graphics to do something like presentation software: draw something, pause for a keystroke so the presenter can explain, then draw the next thing.
Here is one solution I've tried (that doesn't work):
import turtle
import time
paused = False
def unpause():
print("unpause() called")
global paused
paused = False
def pause():
global paused
paused = True
while paused:
time.sleep(0.1)
t = turtle.Turtle()
# set up listener
t.screen.listen()
t.screen.onkeypress(unpause)
# draw something
t.hideturtle()
t.pensize(5)
t.speed(1)
t.pu()
t.goto(-300,-300)
t.pd()
t.goto(-300, 300)
# pause until key is pressed
pause()
# draw some more
t.pu()
t.goto(300,-300)
t.pd()
t.goto(300, 300)
t.screen.mainloop()
The problem is that the sleep call loop totally blocks the keypress from being detected, even when I use a while loop of very short (100ms) sleeps.
If I hit a key while the first line is drawing, I see "unpause() called" in my console, so I know that the key binding is active.
Why doesn't the keypress get detected? I don't know about the internals, but I thought that the keystroke would be recorded in a buffer somewhere, and during the break between sleep calls, the listener would read the buffer and unset the paused global variable. This is not happening.
Is there some other way I could implement this?
This is on a Debian Linux system.
Turtle graphics is based on tkinter, which is an event-driven GUI framework, so you can't do things like you would in a regular procedurally-driven program — see #Bryan Oakley's answer to the question Tkinter — executing functions over time for a more detailed explanation (although the turtle module hides most of these details). Anyway, this fact means you shouldn't call time.sleep() in an tight loop like that because everything has to happen without interfering with running of the mainloop().
The "trick" to avoid calling time.sleep() is to schedule periodic checks of the global variable using the turtle.ontimer() function — so your program will work if the first part of it is changed as shown below:
import turtle
paused = False
def unpause():
print("unpause() called")
global paused
paused = False
def pause():
global paused
paused = True
pausing() # Start watching for global to be changed.
def pausing():
if paused:
turtle.ontimer(pausing, 250) # Check again after delay.
# else quit checking.
t = turtle.Turtle()
# set up listener
t.screen.onkeypress(unpause) # Reversed order of
t.screen.listen() # these two statements.
# draw something
t.hideturtle()
t.pensize(5)
t.speed(1)
t.pu()
t.goto(-300,-300)
t.pd()
t.goto(-300, 300)
# pause until key is pressed
pause()
# draw some more
t.pu()
t.goto(300,-300)
t.pd()
t.goto(300, 300)
t.screen.mainloop()
Taking the ideas your suggestions have given me (thanks martineau and kederrac!) I was able to come up with a solution. It involves wrapping each of my drawing tasks in a function, then using a dispatch function that either waits for a keypress with an ontimer loop, or calls the next drawing function.
This proof-of-concept code uses entirely too many globals, but it shows the technique:
import turtle
t = turtle.Turtle()
paused = False
current_task = 0
def unpause():
global paused
paused = False
def turtle_setup():
global t
t.screen.onkeypress(unpause)
t.screen.listen()
t.hideturtle()
t.pensize(5)
t.speed(1)
def draw_task_finished():
global paused, current_task, drawing_tasks
current_task += 1
paused = True
if current_task < len(drawing_tasks):
draw_task_after_keypress()
def draw_task_after_keypress():
global paused, current_task
if paused:
turtle.ontimer(draw_task_after_keypress, 100)
else:
drawing_tasks[current_task]()
def draw_thing_one():
global t
t.pu()
t.goto(-300,-300)
t.pd()
t.goto(-300, 300)
draw_task_finished()
def draw_thing_two():
global t
t.pu()
t.goto(300,-300)
t.pd()
t.goto(300, 300)
draw_task_finished()
drawing_tasks = [draw_thing_one, draw_thing_two]
turtle_setup()
drawing_tasks[0]()
t.screen.mainloop()
You Can Use turtle.done()function.
Just make a input() function, and if input is entered, the program will run.
I tried this with basic approach.
I've just started working on a version of Snake using Turtle, and I've encountered an issue. I want the snake to move indefinitely, but also to allow the user to move the snake with the keyboard. I got the snake to move from user input, but I can't figure out how to get the snake to keep moving in the same direction while there is no input, whilst preventing it from ignoring user input:
while True:
win.onkey(up,"Up")
win.onkey(right,"Right")
win.onkey(down,"Down")
win.onkey(left,"Left")
win.listen()
#moves the snake one unit in the same direction it is currently facing
movesnake()
I'm new to Turtle, and this is my guess at how to solve this issue - which obviously doesn't work. Any help would be appreciated. I'm conscious Pygame might make this easier but since I've already started with Turtle, I would prefer to get a Turtle solution, if possible.
An event-driven environment like turtle should never have while True: as it potentially blocks out events (e.g. keyboard). Use an ontimer() event instead.
Generally, onkey() and listen() don't belong in a loop -- for most programs they only need to be called once.
Here's a skeletal example of an autonomous turtle being redirected by user input:
from turtle import Screen, Turtle
def right():
snake.setheading(0)
def up():
snake.setheading(90)
def left():
snake.setheading(180)
def down():
snake.setheading(270)
def movesnake():
snake.forward(1)
screen.ontimer(movesnake, 100)
snake = Turtle("turtle")
screen = Screen()
screen.onkey(right, "Right")
screen.onkey(up, "Up")
screen.onkey(left, "Left")
screen.onkey(down, "Down")
screen.listen()
movesnake()
screen.mainloop()
from turtle import *
def PleaseStop():
SomeWord = input("Which word?")
Screen().onkey(PleaseStop,"a")
Screen().listen()
Pressing "a" will make the program ask "Which word?" forever.
No way to stop it besides closing the program. How do I get onkey to call the function only once?
You need to remove the event bindings by calling onkey with None as first parameter:
import turtle
def OnKeyA():
print 'Key "a" was pressed'
turtle.Screen().onkey(None, 'a')
turtle.Screen().onkey(OnKeyA, 'a')
turtle.Screen().listen()
turtle.mainloop()
Doesn't solve the problem. Print is fine either way, input() is
repeating itself forever, even with none.
I believe #Acorn was heading in the right direction with this but the example provided is incomplete. Here's what I feel is a more complete solution:
from turtle import Turtle, Screen, mainloop
def OnKeyA():
screen.onkey(None, 'a')
some_word = raw_input("Which word? ")
turtle.write(some_word, font=('Arial', 18, 'normal'))
screen = Screen()
turtle = Turtle()
screen.onkey(OnKeyA, 'a')
print("Click on turtle window to make it active, then type 'a'")
screen.listen()
mainloop()
Note that this approach is awkward, clicking the turtle graphics window to make it active, hitting 'a', going back to the console window to type your word. If/when you move to Python 3, you can use the turtle function textinput() to prompt for text from the user without having to use input() from the console.