When creating a turtle onclick event my clicks lags 1 click behind.
In my example I have two buttons that adds or subtracts 1 from a value, when I click one of the buttons it does the action of the last button I pushed, not the one I just clicked.
Example:
My number is 3.
Start program.
Click "+" -> nothing happens.
Click "+" -> number becomes 4.
Click "-" -> number becomes 5.
Click somewhere blank -> number becomes 4.
Click "+" -> nothing happens.
Click somewhere blank -> number becomes 5.
Example code.
import turtle
num = 3
turtle.setup(400,500)
wn = turtle.Screen()
wn.title("None")
wn.bgcolor("black")
# Functions for what happens clicking the minus or plus button
def num_minus(xcor, ycor):
global num
if num > 1:
num = num-1
num_disp.clear()
num_disp.write("{}".format(num), align="center", font=("Arial", 26, "normal"))
print(num)
def num_plus(xcor, ycor):
global num
if num < 9:
num = num+1
num_disp.clear()
num_disp.write("{}".format(num), align="center", font=("Arial", 26, "normal"))
print(num)
# Creating minus button, left of number
minus_btn = turtle.Turtle()
minus_btn.penup()
minus_btn.color("gray")
minus_btn.shape("square")
minus_btn.goto(-30, 0)
minus_btn.onclick(num_minus)
# Creatig plus button, right of number
plus_btn = turtle.Turtle()
plus_btn.penup()
plus_btn.color("gray")
plus_btn.shape("square")
plus_btn.goto(30, 0)
plus_btn.onclick(num_plus)
# Displays the number
num_disp = turtle.Turtle()
num_disp.penup()
num_disp.color("red")
num_disp.goto(0, -20)
num_disp.write("{}".format(num), align="center", font=("Arial", 26, "normal"))
num_disp.ht()
wn.listen()
wn.mainloop()
Why are my clicks 1 click behind, and how do I fix this?
I was not able to reproduce the errant behavior you describe. Regardless, here's some possibilities to consider:
On some systems, the turtle window does not come up as the key window and you need to click on it once (anywhere) to have it receive events. You should be able to click between the turtle window and another window on your screen to see how the windows visually change as they gain, and lose, key status. If your window needs to be clicked first to receive events, then you may be miscounting that click as a button click.
Your event handlers are open to receive a new event while they are still processing a previous event. They should be disabled until they are ready for a new event to keep events from interrupting one another and messing with your count.
You instruct the turtle window to listen() to keyboard events which are not used in this interface, so don't turn that on.
Below is my rework of your code to address some of the above and restyle it -- see if it fixes any of your issues or not:
from turtle import Screen, Turtle
FONT_SIZE = 26
FONT = ('Arial', FONT_SIZE, 'normal')
number = 3
# Functions for what happens when clicking the minus or plus button
def number_minus(x, y):
global number
minus_button.onclick(None) # disable handler inside handler
if number > 1:
number -= 1
number_display.clear()
number_display.write(number, align='center', font=FONT)
minus_button.onclick(number_minus) # reenable handler on exit
def number_plus(x, y):
global number
plus_button.onclick(None)
if number < 9:
number += 1
number_display.clear()
number_display.write(number, align='center', font=FONT)
plus_button.onclick(number_plus)
screen = Screen()
screen.setup(400, 500)
screen.bgcolor('black')
# Displays the number
number_display = Turtle()
number_display.hideturtle()
number_display.color('red')
number_display.penup()
number_display.sety(-FONT_SIZE/2)
number_display.write(number, align='center', font=FONT)
# Creating minus button, left of number
minus_button = Turtle()
minus_button.shape('triangle')
minus_button.color('gray')
minus_button.left(30)
minus_button.penup()
minus_button.setx(-30)
minus_button.onclick(number_minus)
# Creatig plus button, right of number
plus_button = Turtle()
plus_button.shape('triangle')
plus_button.color('gray')
plus_button.right(30)
plus_button.penup()
plus_button.setx(30)
plus_button.onclick(number_plus)
screen.mainloop()
Related
When I start my game it first shows the title screen and when I press Enter it goes to the character selection screen. This is intended, however at anytime if I press Enter, whether after the title screen or not, it will go to character selection.
Is there a way I can make it so that the onkeypress only works when I am on the title screen?
I have tried to move the onkeypress into my function that opens the title screen and I have also tried to make an if statement that says "if it is on the title screen then my onkeypress", however neither solution worked.
How can I do this correctly? I am working in replit.
import turtle as trtl
wn = trtl.Screen()
wn.setup(1280,720)
#the turtles
why = trtl.Turtle()
why2 = trtl.Turtle()
why3 = trtl.Turtle()
wn.bgcolor("dim grey")
#characters
closed = "mouthclosed.gif"
opened = "mouthopen.gif"
thumb = "thumb.gif"
cclosed = "mouthclosedc.gif"
copened = "mouthopenc.gif"
thumbc = "thumbc.gif"
#backgrounds
bikini = "bikini.gif"
wn.addshape(closed)
wn.addshape(opened)
wn.addshape(cclosed)
wn.addshape(copened)
wn.addshape(thumb)
wn.addshape(thumbc)
def title():
why.pu()
why2.pu()
why3.pu()
why.goto(0,300)
why.color("rebecca purple")
why.write("mouth pop game !", align="center",font=("Courier", 40, "bold"))
why.goto(0,250)
why.color("royal blue")
why.write("Press enter to start", align="center",font=("Courier", 40, "bold"))
title()
def character_select():
why.clear()
why.goto(0,300)
why.color("royal blue")
why.write("Choose your Character", align="center",font=("Courier", 40, "bold"))
why.goto(-200,0)
why.shape(thumb)
why2.goto(200,0)
why2.shape(thumbc)
def godspeed():
why.shape(opened)
def ohdear():
why.shape(closed)
def bikinimc():
wn.bgpic(bikini)
wn.onkeypress(character_select,"Return")
wn.onkeypress(godspeed,"a")
wn.onkeypress(ohdear, "s")
wn.onkeypress(bikinimc,"c")
wn.listen()
wn.mainloop()
I tried to solve it by adding a variable called a that equals 1 at the beginning and it adds one to a at the end of character_select:
if a == 1:
wn.onkeypress(character_select, "Return")
The check whether a is equal to 1 needs to be done every time the Enter key is pressed.
Your attempt
if a == 1:
wn.onkeypress(character_select, "Return")
was not effective, because this is only executed once during the program.
Instead, move the check inside the character_select function. Either as a positive check:
def character_select():
global a
if a == 1:
# ... code for going to character selection screen ...
a += 1
or as a negative check with an "early return":
def character_select():
global a
if a != 1:
return # do NOT go to character selection screen
# ... code for going to character selection screen ...
a += 1
Notice that the code for going to the character selection screen must not be indented to be inside the if statement in this case.
Also notice that in order to assign to the a variable from inside character_select you need to mark it as global.
So I am writing code for a a Stealth Game in python, I am using the pygame module for it but this problem may not even concern it. I have stages for my menu where I am taking an OOP approach (I am new to OOP so please don't hate so much on my code!) where buttons are generated for a menu where there is a play and quit button. Then Easy, medium, hard and veteran buttons are loaded on top of the old buttons (where the previous buttons lose functionality) and then the 1, 2 , 3 and 4 buttons for levels are loaded again on top of the previous ones if that makes sense. However, the way I wrote my code, I need for the variable stage where stage 1 is the play and quit, 2 is the difficulty and 3 is the level number to be passed out the method and class it would need to be in. I don't know how to do this without declaring the variable with a global scope which defeats the purpose of OOP I think. So how do I? Because otherwise the code just takes input and goes through all the buttons to the end.
Here is my code, you would need images I guess of at least the text files.
#Stealth Assassin
import pygame #Imports the pygame module inclulding many in built functions that aids in game design
import time #Imports the time module for which I can implement delays into my program
pygame.init() #Runs pygame
clock = pygame.time.Clock() #Intialises the variable to control the game clock (FPS)
gameDisplay = pygame.display.set_mode((1920,1080),pygame.FULLSCREEN) #Variable which will set the resolution of the game window and put the window into fullscreen mode
pygame.display.set_caption("Stealth Assassin") #Sets the title of the pygame window for the game
class DisplayImage: #This class contains methods required to load images into the game
def __init__(self, filename, xpos, ypos): #Method used to allow the class to intialise attributes
self.filename = filename #Defines filename as the filename attribute passed through
self.xpos = xpos #Defines the x axis positioning of the image as the attribute passed through
self.ypos = ypos #Defines the y axis positioning of the image as the attribute passed through
def LoadImage(self): #This method will load images into the game displaying them
image = pygame.image.load(self.filename+".png") #Image is loaded into the program
gameDisplay.blit(image, (self.xpos,self.ypos)) #Image is displayed to coordinates which were attributes that were defined prior
stage = 1 #Sets the menu as stage 1 which is the play and quit buttons
class Button: #This class contains methods for buttons including display and functionality
def __init__(self, buttonname, buttonx, buttony, buttonwidth, buttonheight, textfile, textx, texty, stage): #Methods used to allow classes to intialise attributes
self.buttonname = buttonname #Defines the name of the button as the attribute passed through
self.buttonx = buttonx #Defines the x axis positioning of the button as the attribute passed through
self.buttony = buttony #Defines the y axis positioning of the button as the attribute passed through
self.buttonwidth = buttonwidth #Defines the width of the button as the attribute passed through
self.buttonheight = buttonheight #Defines the height of the button as the attribute passed through
self.textfile = textfile #Sets the name of the textfile to be called
self.textx = textx #Defines the x axis positioning of the text as the attribute passed through
self.texty = texty #Defines the y axis positioning of the text as the attribute passed through
self.stage = stage #Sets the stage of the menu which has 3 states of play/quit, difficulty and level choice
def createbutton(self): #Method which creates a button for the menu
buttonname = pygame.draw.rect(gameDisplay, (0,0,0), [self.buttonx, self.buttony, self.buttonwidth, self.buttonheight]) #Draws a rectangular button which is black and given the size and coordinates which were attributes
text = pygame.image.load(self.textfile+".png") #Loads the text file into the program
gameDisplay.blit(text, (self.textx,self.texty)) #Displays the text given coordinates
def quitbutton(self): #Method which quits the program if the quit button is clicked
if self.buttonx+self.buttonwidth > mouse[0] > self.buttonx and self.buttony+self.buttonheight > mouse[1] > self.buttony and pressed[0] == 1: #If the button is clicked (regarding its dimensions)
pygame.quit() #Exits pygame
quit() #Quits program
def buttonaction(self): #Method which takes action for the particular button
if self.buttonx+self.buttonwidth > mouse[0] > self.buttonx and self.buttony+self.buttonheight > mouse[1] > self.buttony and pressed[0] == 1: #If the button is clicked (regarding its dimensions)
if self.stage == 1: #If the play/quit buttons are active
EasyButton.createbutton() #Creates and displays the easy button through the button class and its method
MediumButton.createbutton() #Creates and displays the medium button through the button class and its method
HardButton.createbutton() #Creates and displays the hard button through the button class and its method
VeteranButton.createbutton() #Creates and displays the veteran button through the button class and its method
if self.stage == 2: #If the difficulty buttons are active
OneButton.createbutton() #Creates and displays the one button through the button class and its method
TwoButton.createbutton() #Creates and displays the two button through the button class and its method
ThreeButton.createbutton() #Creates and displays the three button through the button class and its method
FourButton.createbutton() #Creates and displays the four button through the button class and its method
if self.buttonname == 'easybutton':
difficulty = 'easy'
if self.buttonname == 'mediumbutton':
difficulty = 'medium'
if self.buttonname == 'hardbutton':
difficulty = 'hard'
if self.buttonname == 'veteranbutton':
difficulty = 'veteran'
print(difficulty)
time.sleep(0.5)
PlayButton = Button('playbutton',133,477,756,223,'PlayText',387,545,1) #Creates play button
QuitButton = Button('quitbutton',133,731,756,223,'QuitText',387,806,None) #Creates quit button
EasyButton = Button('easybutton',127,477,362,223,'EasyText',214,548,2) #Creates easy button
MediumButton = Button('mediumbutton',533,477,362,223,'MediumText',560,548,2) #Creates medium button
HardButton = Button('hardbutton',127,727,362,223,'HardText',214,806,2) #Creates hard button
VeteranButton = Button('veteranbutton',533,727,362,223,'VeteranText',537,806,2) #Creates veteran button
OneButton = Button('onebutton',127,477,362,223,'OneText',287,550,3) #Creates the level 1 button
TwoButton = Button('twobutton',533,477,362,223,'TwoText',693,550,3) #Creates the level 2 button
ThreeButton = Button('threebutton',127,727,362,223,'ThreeText',285,810,3) #Creates the level 3 button
FourButton = Button('fourbutton',533,727,362,223,'FourText',685,810,3) #Creates the level 4 button
PlayButton.createbutton() #Creates the play button through the button class and its method
QuitButton.createbutton() #Creates the play button through the button class and its method
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
mouse = pygame.mouse.get_pos() #Gets the x and y coordinates of the mouse cursor
pressed = pygame.mouse.get_pressed() #Checks if the mouse has been pressed
PlayButton.buttonaction() #Checks if the playbutton needs action
QuitButton.quitbutton() #Checks if the quitbutton needs action
EasyButton.buttonaction() #Checks if the easybutton needs action
MediumButton.buttonaction() #Checks if the mediumbutton needs action
HardButton.buttonaction() #Checks if the hardbutton needs action
VeteranButton.buttonaction() #Checks if the veteranbutton needs action
OneButton.buttonaction() #Checks if the onebutton needs action
TwoButton.buttonaction() #Checks if the twobutton needs action
ThreeButton.buttonaction() #Checks if the threebutton needs action
FourButton.buttonaction() #Checks if the fourbutton needs action
pressed = [0,0,0]
pygame.display.update()
clock.tick(5)
One of the principals of object oriented design is to keep all the aspects of what the object is modelling/implementing inside the object, but no more. What is the purpose of this "Button" - to be seen on-screen, to accept user input, to signal the state to the main program.
So in terms of this, your button class is mostly OK. However, it's including things inside the button definition that really are not the job of the button, for example:
def quitbutton(self):
#If the button is clicked (regarding its dimensions)
if self.buttonx+self.buttonwidth > mouse[0] > self.buttonx and self.buttony+self.buttonheight > mouse[1] > self.buttony and pressed[0] == 1:
pygame.quit() #Exits pygame
quit() #Quits program
A generic button class should not be responsible for quitting the program, it just needs to click and un-click. Similarly for the code that switches between the Play+Quit and the difficulty level button-sets - this is not the button's job.
I think a better button removes clutter to be simpler:
class Button:
def __init__(self, buttonname, buttonx, buttony, buttonwidth, buttonheight, textfile, textx, texty ):
self.buttonname = buttonname # Name of the button
self.buttonx = buttonx # X-axis position
self.buttony = buttony # Y-axis position
self.buttonwidth = buttonwidth # Width of the button
self.buttonheight= buttonheight # Height of the button
self.text_image = pygame.image.load( textfile+".png" ) # Button Label
self.textx = textx # X-axis positioning of the text
self.texty = texty # Y-axis positioning of the text
def drawButton( self, screen ):
""" Paint the button to the screen """
# Black rectangle the size of the button
pygame.draw.rect( screen, (0,0,0), [self.buttonx, self.buttony, self.buttonwidth, self.buttonheight])
# Overlay the button text onto the background
screen.blit( text_image, ( self.textx, self.texty ) )
def mouseIsOver( self, mouse_position ):
""" Returns true if the mouse is within this buttons area """
inside = self.buttonx+self.buttonwidth > mouse_position[0] > self.buttonx and self.buttony+self.buttonheight > mouse_position[1] > self.buttony
return inside
Then in your main program:
PlayButton = Button('playbutton',133,477,756,223,'rock1_64',387,545,1) #Creates play button
QuitButton = Button('quitbutton',133,731,756,223,'rock1_64',387,806,None) #Creates quit button
EasyButton = Button('easybutton',127,477,362,223,'rock1_64',214,548,2) #Creates easy button
MediumButton = Button('mediumbutton',533,477,362,223,'rock1_64',560,548,2)
...
all_buttons = [ PlayButton, QuitButton, EasyButton, MediumButton ]
# Main Loop
while True:
# Handle user-input
for event in pygame.event.get():
if event.type == pygame.QUIT:
break
if event.type == pygame.MOUSEBUTTONUP:
# The mouse button was clicked, was it inside a button?
click_location = pygame.mouse.get_pos()
for b in all_buttons:
if ( b.mouseIsOver( click_location ) ):
print( "Button [%s] pressed" % ( b.buttonname ) )
# Re-paint screen
gameDisplay.fill( BACKGROUND_COLOUR )
for b in all_buttons:
b.drawButton( gameDisplay )
pygame.display.flip()
clock.tick_busy_loop( 60 ) # Limit FPS
pygame.quit()
quit()
Or failing that, the Button class could post and event to the PyGame queue:
(EDIT: There was a bug in this, it needed to create the event to post, FIXED).
BUTTON_CLICK_EVENT = pygame.USEREVENT + 1
...
class Button ( ... ):
def checkClick( self, mouse_pos ):
""" If the mouse-click is inside our rectangle, post a message to the queue """
if ( self.buttonx+self.buttonwidth > mouse_position[0] > self.buttonx and self.buttony+self.buttonheight > mouse_position[1] > self.buttony ):
pygame.event.post( pygame.event.Event( BUTTON_CLICK_EVENT, { "button_name" : self.buttonname } ) )
...
# Main loop
if event.type == BUTTON_CLICK_EVENT:
print("Clicked "+ event.button_name )
Doing this allows the separation of the button logic from the rest of the program, and prevents needing global variables. Obviously you have a need to switch between banks of buttons, but this is not a problem for the Button object, it should go somewhere else. Perhaps in a ButtonGroup or suchlike.
I'm doing an assignment for school and would like to know how to collect user input directly from the turtle window rather than from the console. Is there the capacity for buttons in turtle or, if not, click events for certain areas so an area of the screen can act as a button?
User input can be obtained using the two graphic input methods that the turtle module provides:
textinput(title, prompt)
numinput(title, prompt, default=None, minval=None, maxval=None)
These are designed to help prevent some of the errors that console style input needs to trap. These were introduced in Python 3 and are not available in Python 2 turtle. However there are tkinter equivalents you can invoke from Python 2 when running turtle -- search SO for examples.
If you want a graphic button, my recommendation is you throw a turtle, or two, at the problem to become the button. Size, shape and color the turtle appropriately and then add an onclick() handler onto the turtle, not the screen, so that you can then click on your button to invoke an action:
from turtle import Turtle, mainloop
BUTTON_SIZE = 60
CURSOR_SIZE = 20
FONT_SIZE = 18
FONT = ('Arial', FONT_SIZE, 'bold')
STATES = (('red', 'OFF'), ('green', 'ON'))
INITIAL_STATE = STATES[0]
def toggle_power(x, y):
color, state = STATES[button.fillcolor() == 'red']
button.fillcolor(color)
marker.undo()
marker.write(state, align='center', font=FONT)
color, state = INITIAL_STATE
button = Turtle('circle')
button.shapesize(BUTTON_SIZE / CURSOR_SIZE, outline=2)
button.color('black', color)
button.penup()
# button.goto(-200, 200) # move the button into position
marker = Turtle(visible=False)
marker.penup()
marker.goto(button.xcor(), button.ycor() - BUTTON_SIZE/2 - FONT_SIZE - 2)
marker.write(state, align='center', font=FONT)
button.onclick(toggle_power)
mainloop()
The above button logic should work in either Python 3 or Python 2.
x = screen.textinput(title="level", prompt="Which level do you want to play? Type beginner, intermediate, pro")
I am attempting to finish a program for a class, and I am doing fairly well. It is a simple turtle-graphics game in python, where you attempt to avoid poison dots and get navigate to a square. However, my program starts immediately, before the user clicks on the screen. How can I fix this? Thanks!
My code:
# This game involves avoiding red poison blobs while attempting to navigate to
# a square. If you hit the blob, you begin to speed up, making it more difficult
# not to hit more. Additionally, you lose a point. If you reach the square you
# get a point.
import turtle
import math
import random
# screen
wn = turtle.Screen()
wn.bgcolor("black")
wn.tracer(3)
# Draw border
pen1 = turtle.Turtle()
pen1.color("white")
pen1.penup()
pen1.setposition(-275,-275)
pen1.pendown()
pen1.pensize(5)
for side in range(4):
pen1.forward(550)
pen1.left(90)
pen1.hideturtle()
# player
player = turtle.Turtle()
player.color("dark green")
player.shape("turtle")
player.penup()
# poisonBlob
maxpoisonBlob = 15
poisonBlob = []
for a in range(maxpoisonBlob):
poisonBlob.append(turtle.Turtle())
poisonBlob[a].color("dark red")
poisonBlob[a].shape("circle")
poisonBlob[a].shapesize(4, 4, 4)
poisonBlob[a].penup()
poisonBlob[a].speed(0)
poisonBlob[a].setposition(random.randint(-255, 255), random.randint(-255, 255))
maxfood = 1
food = []
for a in range(maxfood):
food.append(turtle.Turtle())
food[a].color("light blue")
food[a].shape("square")
food[a].penup()
food[a].speed(0)
food[a].setposition(random.randint(-240, 240), random.randint(-240, 240))
# speed variable
speed = 6.5
def turnleft():
player.left(30)
def turnright():
player.right(30)
def increasespeed():
global speed
speed += 1
def touchPoison(t1, t2):
d = math.sqrt(math.pow(t1.xcor()-t2.xcor(),2) + math.pow(t1.ycor()-t2.ycor(),2))
if d < 50:
return True
else:
return False
def touchfood(z1, z2):
d = math.sqrt(math.pow(z1.xcor()-z2.xcor(),2) + math.pow(z1.ycor()-z2.ycor(),2))
if d < 20:
return True
else:
return False
turtle.listen()
turtle.onkey(turnleft, "Left")
turtle.onkey(turnright, "Right")
def main():
print("Help your turtle navigate red poison blobs while attempting to navigate to the\n"
"food! If you hit the poison, you begin to speed up, making it more difficult\n"
"not to hit more. Additionally, you lose a point. If you reach the square you\n"
"get a point. To navigate, click the screen, and then use the right and left\n"
"arrow keys. Quickly, your turtle is running away!")
score = 0
while True:
player.forward(speed)
# turtle boundary
if player.xcor() > 260 or player.xcor() < -260:
player.right(180)
# turtle boundary
if player.ycor() > 260 or player.ycor() < -260:
player.right(180)
# move poison
for a in range(maxpoisonBlob):
poisonBlob[a].forward(3)
# Poison boundaries
if poisonBlob[a].xcor() > 220 or poisonBlob[a].xcor() < -220:
poisonBlob[a].right(180)
# Poision boundaries
if poisonBlob[a].ycor() > 220 or poisonBlob[a].ycor() < -220:
poisonBlob[a].right(180)
# Poison touching
if touchPoison(player, poisonBlob[a]):
increasespeed()
poisonBlob[a].setposition(random.randint(-230, 230), random.randint(-230, 230))
poisonBlob[a].right(random.randint(0,360))
score -= 1
#Draw score
pen1.undo()
pen1.penup()
pen1.hideturtle()
pen1.setposition(-260, 280)
scorestring = "Score: %s" %score
pen1.write(scorestring, font=("Calibri", 12))
for a in range(maxfood):
#Positive Point Checking
if touchfood(player, food[a]):
food[a].setposition(random.randint(-230, 230), random.randint(-230, 230))
score += 1
#Draw Score
pen1.undo()
pen1.penup()
pen1.hideturtle()
pen1.setposition(-260, 280)
scorestring = "Score: %s" %score
pen1.write(scorestring, font=("Calibri", 12))
if __name__ == "__main__":
main()
Generally we try not to directly answer homework questions, but I will give you some guidance.
I'm not familiar with the library you're using, but the basic idea is that you need to wait until the user clicks the screen before continuing. My understanding is that you want to do so after the print() function in main, so that the user has to read the message and then click to continue.
A quick search turned up this link: Turtle in python- Trying to get the turtle to move to the mouse click position and print its coordinates
This goes into detail of the onscreenclick() event. Judging by your onkey() methods you are already familiar with binding to events, so you should be able to use this knowledge to bind to the onscreenclick() event and then you just need to break your code up a bit so that it doesn't execute your game until the onscreenclick() event has happened.
If that isn't clear enough, help me understand where your confusion is and I'll assist further. Thanks!
You could begin by defining a function where you call your "if" statement and then use turtle.onscreenclick() by inserting your newly defined function in it like so:
def Game(x, y):
if __name__ == "__main__":
main()
turtle.onscreenclick(Game)
Make sure to insert all this at the end!
Now, adding the (x, y) in the parenthesis after the function name in the function definition assigns the spot the user clicks on in the graphics window its corresponding (x, y) coordinates, which in turn activates the function due to the action of clicking on the screen. I did this successfully, so I can assure you it works! I hope this helps! :)
Criteria I'm trying to meet:
The screen resets when the user presses the SPACEBAR, meaning the drawn lines go away and the unnamed turtle returns to the center but it doesn’t return to the default turtle color and shape!
""" A simple drawing program
Click and drag the center turtle to draw
Click the colorful buttons to change the
turtle's color,
and then draw different shapes.
Press the SPACEBAR key to reset the
drawing.
"""
from turtle import *
turtle_1 = Turtle()
turtle_2 = Turtle()
turtle_3 = Turtle()
def callback_1(x,y):
color("red")
shape("circle")
circle(100)
def callback_2(x,y):
color("blue")
shape("square")
circle(100,steps=4)
def callback_3(x,y):
color("green")
shape("triangle")
circle(100,steps=3)
def place_turtles():
turtle_1.color("red")
turtle_1.shape("circle")
turtle_1.penup()
turtle_1.goto(-200,-200)
turtle_2.color("blue")
turtle_2.shape("square")
turtle_2.penup()
turtle_2.goto(0,-200)
turtle_3.color("green")
turtle_3.shape("triangle")
turtle_3.penup()
turtle_3.goto(200,-200)
def start_over():
resetscreen()
place_turtles()
listen()
onkey(start_over, "space")
ondrag(goto)
place_turtles()
This code allows the user to drag the turtle, press buttons, and reset the screen when they press SPACEBAR. For some reason, though, resetting the screen also resets the color of the turtle. How can I prevent this from happening?
Basically what I want to happen is if, say, the user clicks on the blue square button, then resets the screen to hide the shape drawn by the button, all of the turtles return to their original positions, but the unnamed turtle does not change its previous color and shape. Let me know if I need to elaborate further.
With a "better late than never" attitude, I believe I've reworked your code to get the behavior you desired. I also reworked your drag logic to give you better drawing capability:
from turtle import Turtle, Screen, getturtle
def callback_1(x, y):
anonymous.color(*turtle_1.color())
anonymous.shape(turtle_1.shape())
anonymous.circle(100)
def callback_2(x, y):
anonymous.color(*turtle_2.color())
anonymous.shape(turtle_2.shape())
anonymous.circle(100, steps=4)
def callback_3(x, y):
anonymous.color(*turtle_3.color())
anonymous.shape(turtle_3.shape())
anonymous.circle(100, steps=3)
def setup_turtles():
turtle_1.onclick(callback_1)
turtle_1.color("red")
turtle_1.penup()
turtle_1.goto(-200, -200)
turtle_2.onclick(callback_2)
turtle_2.color("blue")
turtle_2.penup()
turtle_2.goto(0, -200)
turtle_3.onclick(callback_3)
turtle_3.color("green")
turtle_3.penup()
turtle_3.goto(200, -200)
def start_over():
colors = anonymous.color()
anonymous.reset()
anonymous.color(*colors)
def drag_handler(x, y):
anonymous.ondrag(None) # disable event inside event handler
anonymous.goto(x, y)
anonymous.ondrag(drag_handler) # reenable event on event handler exit
anonymous = getturtle()
anonymous.ondrag(drag_handler)
turtle_1 = Turtle(shape="circle")
turtle_2 = Turtle(shape="square")
turtle_3 = Turtle(shape="triangle")
setup_turtles()
screen = Screen()
screen.onkey(start_over, "space")
screen.listen()
screen.mainloop()
Many of the changes are just for personal style reasons. The two key changes are: just reset the anonymous turtle itself, not the screen; rather than use goto as an event handler, wrap it in a function that turns off drag events during the goto call.