Python multiple onclick events - python

I want to be able to have both of my turtle onclick events function, but only one of them functions. I have a function that draws a square at the location the user clicks, and I have a close button that closes the program when you click it. Only one of these functions work at a time.
import turtle
import math
turtle.penup()
def square(x, y):
turtle.up()
turtle.goto(x, y)
turtle.down()
for i in range(4):
turtle.forward(50)
turtle.left(90)
def closebutton(location1):
(x,y) = location1
turtle.up()
turtle.setposition(location1)
turtle.down()
for i in range(2):
turtle.forward(40)
turtle.left(90)
turtle.forward(25)
turtle.left(90)
turtle.up()
turtle.forward(7.5)
turtle.left(90)
turtle.forward(5)
turtle.right(90)
turtle.write("close")
def btnclick(x, y):
if x > 100 and x < 141 and y > -100 and y < -75:
quit()
turtle.onscreenclick(btnclick)
closebutton((100,-100))
turtle.onscreenclick(square)

def square(x, y):
# Check area is Close button
if x > 100 and x < 141 and y > -100 and y < -75:
quit()
turtle.up()
turtle.goto(x, y)
turtle.down()
for i in range(4):
turtle.forward(50)
turtle.left(90)
I think, at square function, we can check that is close button's area

We can solve this the way you attempted, but we have to add the screen click handlers in a specific order and take advantage of the little-used add parameter:
from turtle import Screen, Turtle, mainloop
FONT_SIZE = 20
FONT = ('Arial', FONT_SIZE, 'normal')
def draw_square(x, y):
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
for _ in range(4):
turtle.forward(50)
turtle.left(90)
def button_clicked(x, y):
if 100 < x < 100 + text_width and -FONT_SIZE/2 - 100 < y < FONT_SIZE/2 - 100:
quit()
def make_close_button(x, y):
turtle.penup()
turtle.setposition(x, y - FONT_SIZE / 2)
turtle.write("close", move=True, font=FONT)
width = turtle.xcor() - x # pixel width of text we just wrote
turtle.pendown()
for _ in range(2):
turtle.left(90)
turtle.forward(FONT_SIZE)
turtle.left(90)
turtle.forward(width)
turtle.penup()
turtle.home()
return width
turtle = Turtle()
text_width = make_close_button(100, -100)
screen = Screen()
screen.onscreenclick(button_clicked)
screen.onscreenclick(draw_square, add=True)
mainloop()
But this approach makes it hard to disable the onscreenclick() event inside the event handler code. That is, while one square is still being drawn, you can click elsewhere on the screen and start a second, interfering with the completion of the first. To solve this we might try a completely different approach with a single event handler function that we can disable and reenable as needed:
from turtle import Screen, Turtle, mainloop
FONT_SIZE = 20
FONT = ('Arial', FONT_SIZE, 'normal')
def draw_square():
for _ in range(4):
turtle.forward(50)
turtle.left(90)
def button_clicked(x, y):
screen.onscreenclick(None) # disable event handler inside event handler
if button.distance(x, y) < FONT_SIZE:
quit()
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
draw_square()
screen.onscreenclick(button_clicked)
def make_close_button(x, y):
button = Turtle(visible=False)
button.speed('fastest')
button.penup()
button.setposition(x, y - FONT_SIZE / 2)
button.write("close", move=True, font=FONT)
width = button.xcor() - x # pixel width of text we just wrote
button.pendown()
for _ in range(2):
button.left(90)
button.forward(FONT_SIZE)
button.left(90)
button.forward(width)
button.penup()
button.setposition(x + width / 2, y)
return button
button = make_close_button(100, -100)
turtle = Turtle()
screen = Screen()
screen.onscreenclick(button_clicked)
mainloop()
Here we broke up the functionality a bit to have functions with specific duties. And instead of figuring out if we clicked near the button, we left a turtle behind and just check the distance of the click from that turtle.
Both approaches have their advantages and disadvantages.

Related

i wanna draw a chessboard and here is my code(edited)

it has no error now but i want to draw a chess board but this thing just randomly output something else:)))))
i think there is something wrong with the fill function tho
import turtle
screen = turtle.Screen()
screen.setup(800,800)
screen.bgcolor('pink')
screen.colormode(255)
screen.title("chessboard")
turtle = turtle.Turtle()
def draw(length,angle):
for i in range (4):
turtle.forward(length)
turtle.right(angle)
length = 30
angle = 90
coor = -120
num = 64
def board(length):
draw(length,angle)
turtle.forward(length)
def limit():
for q in range (8):
board(length)
def pos(coor,length):
for w in range(8):
turtle.penup()
turtle.setpos(coor,coor+30*w)
turtle.pendown()
limit()
def fill():
for q in range (8):
for w in range(8):
if (q+w)%2==0:
turtle.fillcolor('#000000')
else:
turtle.fillcolor('#FFFFFF')
def repeat():
for h in range (8):
draw(length,angle)
turtle.forward(length)
def color():
turtle.begin_fill()
repeat()
pos(coor,length)
fill()
turtle.end_fill()
turtle.speed(0)
turtle.penup()
turtle.goto(coor, coor)
turtle.pendown()
turtle.hideturtle()
screen.update()
color()
it has no error now but i want to draw a chess board but this thing just randomly output something else:)))))
i think there is something wrong with the fill function tho
I'm just learning python as well, so I found your problem being an interesting thing to try to do myself.
Please see below a solution that works, using some ideas from your code and my own implementation too.
If anything of the above helps you fix your own code, or have any questions I might be able to answer, please let me know :)
import turtle
# draw a square at given pos
def draw_square(t, fwd, angle):
for _ in range(4):
t.forward(fwd)
t.right(angle)
def changePos(t, startPos, endPos, penUp):
if penUp:
t.pu()
t.goto(startPos, endPos)
if penUp:
t.pd()
# Let's skip the drawing all together?
turtle.tracer(0, 0)
# taking input for the side of the square
squareSize = 100
angle = 90
boardSquares = 8
startPos = -(squareSize * (boardSquares/2)), (squareSize * (boardSquares/2)) #start from top-left
print("Start position: ", startPos)
screen = turtle.Screen()
screen.setup(850, 850)
screen.bgcolor('pink')
screen.title("chessboard")
# create the white squares
squares = turtle.Turtle()
squares.hideturtle()
squares.fillcolor("white")
squares.pensize(1)
squares.pencolor("red")
changePos(squares, startPos[0], startPos[1], True)
for r in range(8):
for c in range(8):
if (r + c) % 2 == 0:
squares.fillcolor('#000000')
else:
squares.fillcolor('#FFFFFF')
changePos(squares, startPos[0] + (squareSize * c), startPos[1] - (squareSize * r), True)
squares.begin_fill()
draw_square(squares, squareSize, angle)
squares.end_fill()
# let's draw the board edge
board = turtle.Turtle()
board.hideturtle()
changePos(board, startPos[0], startPos[1], True)
board.pensize(5)
board.pencolor("brown")
draw_square(board, boardSquares * squareSize, angle)
turtle.update()
turtle.exitonclick()

How can I make a turtle start from the endpoint of another?

For instance: I made a square using a turtle, and another turtle should start from the endpoint of the square turtle, not from the beginning.
hexagonTurtle = turtle.Turtle()
def hexagon(hexagonTurtle, size):
hexagonTurtle.pendown()
hexagonTurtle.pencolor('yellow')
hexagonTurtle.pensize(10)
hexagonTurtle.left(90)
for i in range(6):
hexagonTurtle.forward(size)
hexagonTurtle.right(60)
hexagonTurtle.left(60)
hexagonTurtle.back(size)
hexagonTurtle.left(60)
hexagonTurtle.back(size)
hexagonTurtle.right(210)
pentagonTurtle = turtle.Turtle()
def pentagon (pentagonTurtle, size):
pentagonTurtle.penup()
pentagonTurtle.forward(430)
pentagonTurtle.pendown()
pentagonTurtle.pencolor('green')
pentagonTurtle.pensize(10)
pentagonTurtle.left(90)
for i in range(5):
pentagonTurtle.forward(size)
pentagonTurtle.right(72)
hexagon(hexagonTurtle, 120)
pentagon(pentagonTurtle, 120)
Rather then create a second turtle from scratch and adjust it to match your first turtle, another approach you can use, given the design of your program, is to simply clone your first turtle:
hexagonTurtle = turtle.Turtle()
hexagon(hexagonTurtle, 120)
pentagonTurtle = hexagonTurtle.clone()
pentagon(pentagonTurtle, 120)
It will start out with the same position and orientation as the turtle of which it is a clone. The complete example:
from turtle import Screen, Turtle
def hexagon(turtle, size):
turtle.pencolor('yellow')
turtle.pensize(10)
turtle.left(90)
for _ in range(6):
turtle.forward(size)
turtle.right(60)
turtle.left(60)
turtle.backward(size)
turtle.left(60)
turtle.backward(size)
turtle.right(210)
def pentagon(turtle, size):
turtle.pencolor('green')
turtle.pensize(10)
turtle.penup()
turtle.forward(430)
turtle.pendown()
turtle.left(90)
for _ in range(5):
turtle.forward(size)
turtle.right(72)
screen = Screen()
screen.setup(1000, 500)
screen.setworldcoordinates(-100, -200, 900, 300) # for demonstration purposes only
hexagonTurtle = Turtle()
hexagon(hexagonTurtle, 120)
pentagonTurtle = hexagonTurtle.clone()
pentagon(pentagonTurtle, 120)
screen.exitonclick()
You can use turtle.position()/turtle.pos() to get the position of a turtle, and turtle.goto()/turtle.setpos/turtle.setposition to set the position of a turtle. Similarly, use turtle.heading() to get where a turtle is facing and turtle.setheading() to set where a turtle is facing.
pentagonTurtle.penup()
pentagonTurtle.goto(hexagonTurtle.pos())
pentagonTurtle.setheading(hexagonTurtle.heading())

Draw then erase square

I'm trying to draw a square using python graphics then erase it after 3 seconds. I have the code below:
import threading as th
import turtle
import random
thread_count = 0
def draw_square():
turtle.goto(random.randint(-200,200), random.randint(-200,200))
for i in range(4):
turtle.forward(100)
turtle.left(90)
def erase_square(x,y):
turtle.pencolor('white')
turtle.fillcolor('white')
turtle.goto(x,y)
turtle.begin_fill()
for i in range(4):
turtle.forward(target_size)
turtle.left(90)
turtle.end_fill()
def square_timer():
global thread_count
if thread_count <= 10:
print("Thread count", thread_count)
draw_square()
T = th.Timer(3, square_timer)
T.start()
The square_update() function will draw 10 squares then stop. I'm trying to make it draw one square then clear it using the erase_square() by painting white over it, then another and repeat the process and stop when it reaches 10. I can't figure how to make the erase function go to the random location of the drawn square and 'erase' it. How can I make it draw and erase the square?
The most straightforward way to clear a drawing is with the clear() method rather than trying to draw white over the image:
from turtle import Screen, Turtle
from random import randint
def draw_square():
turtle.goto(randint(-200, 200), randint(-200, 200))
turtle.pendown()
for _ in range(4):
turtle.forward(100)
turtle.left(90)
turtle.penup()
def erase_square():
turtle.clear()
screen.ontimer(square_update) # no time, ASAP
def square_update():
draw_square()
screen.ontimer(erase_square, 3000) # 3 seconds in milliseconds
screen = Screen()
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest')
turtle.penup()
square_update()
screen.exitonclick()
You can use the undo() method as #JonathanDrukker suggests, but you have to undo each step. Even for a simple shape like a square, this takes a little thought, but with a complex shape, this is difficult. The number of operations added to the undo buffer don't always match the calls that you make to turtle. But we can get that count from turtle itself, as long as we don't do any other turtle actions between drawing and erasing:
def draw_square():
turtle.goto(randint(-200, 200), randint(-200, 200))
count = turtle.undobufferentries()
turtle.pendown()
for _ in range(4):
turtle.forward(100)
turtle.left(90)
turtle.penup()
return turtle.undobufferentries() - count
def erase_square(undo_count):
for _ in range(undo_count):
turtle.undo()
screen.ontimer(square_update)
def square_update():
undo_count = draw_square()
screen.ontimer(lambda: erase_square(undo_count), 3000)
But what I'd do is make the turtle itself the square and then just move it rather than erase and (re)draw the square:
from turtle import Screen, Turtle
from random import randint
CURSOR_SIZE = 20
def draw_square():
turtle.goto(randint(-200, 200), randint(-200, 200))
def square_update():
turtle.hideturtle()
draw_square()
turtle.showturtle()
screen.ontimer(square_update, 3000)
screen = Screen()
turtle = Turtle()
turtle.hideturtle()
turtle.shape('square')
turtle.shapesize(100 / CURSOR_SIZE)
turtle.fillcolor('white')
turtle.penup()
square_update()
screen.exitonclick()

how can I change between modes with my button

I created 2 drawing modes but they draw at the same time but i want them to work seperateley so i created a button but dont know how to define the mode change for the button to work
how can I define my mode change so I can change between different drawing modes eg freehandmode, circlemode so they dont work at the same time
from turtle import Screen, Turtle
beni=Screen()
beni.setup(400, 400, 10, 10)
beni.setworldcoordinates(-300, -300, 300, 300)
def mode_change(x, y):
#freehandmode
def freehandmode(x, y):
t.ondrag(None)
t.setheading(t.towards(x, y))
t.goto(x, y)
t.ondrag(freehandmode)
#circlemode
def draw_circle(x, y):
beni.onclick(None)
center = turtle.position()
turtle.setposition(x, y)
turtle.setheading(turtle.towards(center) - 90)
turtle.pendown()
turtle.circle(turtle.distance(center))
turtle.penup()
turtle.clearstamps()
beni.onclick(pick_center)
def pick_center(x, y):
beni.onclick(None)
turtle.setposition(x, y)
turtle.stamp()
beni.onclick(draw_circle)
turtle = Turtle()
turtle.hideturtle()
turtle.shape('circle')
turtle.shapesize(0.5)
turtle.penup()
beni.onclick(pick_center)
#freehand turtle
t = Turtle('circle')
t.shapesize(1)
t.speed('fastest')
t.ondrag(freehandmode)
#modechange turtle
modechange = Turtle('circle')
modechange.pu()
modechange.shapesize(0.55, 1.45)
modechange.color('black')
modechange.setpos(0, 290)
modechange.onclick(mode_change)
#modebutton
modebutton = Turtle()
modebutton.shapesize(0.25)
modebutton.pu()
modebutton.setpos(-20, 300)
modebutton.setheading(270)
modebutton.pd()
modebutton.fd(20)
modebutton.setheading(0)
modebutton.fd(45)
modebutton.setheading(90)
modebutton.fd(20)
modebutton.setheading(180)
modebutton.fd(45)
modebutton.pu()
modebutton.setpos(-10, 263)
modebutton.setheading(0)
modebutton.color('green')
modebutton.write('Modi')
modebutton.hideturtle()
beni.mainloop()
It's mostly a matter of accounting -- you need to explicitly list steps necessary to disable one turtle and enable the other every time you switch modes. And you need to keep track of the current mode. Here's my rework and simplification of your (actually, largely my) code to do this:
from turtle import Screen, Turtle, mainloop
def mode_change(x, y):
global mode
if mode == 'freehand':
# disable freehand
freehand.ondrag(None)
freehand.hideturtle()
# enable circle
screen.ontimer(lambda: screen.onclick(pick_center)) # so screen doesn't inherit *this* click
mode = 'circle'
else:
# disable circle
screen.onclick(None)
# enable freehand
freehand.ondrag(freehandmode)
freehand.showturtle()
mode = 'freehand'
# freehand mode
def freehandmode(x, y):
freehand.ondrag(None)
freehand.setheading(freehand.towards(x, y))
freehand.goto(x, y)
freehand.ondrag(freehandmode)
# circle mode
def draw_circle(x, y):
screen.onclick(None)
center = circle.position()
circle.setposition(x, y)
circle.setheading(circle.towards(center) - 90)
circle.pendown()
circle.circle(circle.distance(center))
circle.penup()
circle.clearstamps()
screen.onclick(pick_center)
def pick_center(x, y):
screen.onclick(None)
circle.setposition(x, y)
circle.stamp()
screen.onclick(draw_circle)
screen = Screen()
screen.setup(400, 400)
mode = 'freehand'
circle = Turtle('circle', visible=False)
circle.shapesize(0.5)
circle.speed('fastest')
circle.penup()
freehand = Turtle('circle')
freehand.speed('fastest')
freehand.ondrag(freehandmode)
# modechange turtle
modechange = Turtle('square')
modechange.penup()
modechange.setposition(0, 155)
modechange.pencolor('green')
modechange.write('Modi', align='center')
modechange.shapesize(2.25, 1.0)
modechange.setposition(0, 190)
modechange.onclick(mode_change)
mainloop()

Creating a turtle program that does commands based upon the button pressed

I haven't been able to find anything online for this but I need to create a program that:
If the left button is pressed, the turtle should move to that location and draw a small square.
If the right button is pressed, the turtle should move that that location and draw a small circle.
If the middle button is pressed, the turtle should change to a different random color.
You should also change the color if the use presses the space bar.
Any suggestions on how to start?
Here is some code I have tried so far:
def k2(x,y): turtle.penup() turtle.setposition(x,y) turtle.pendown() turtle.circle(radius)
This is the top from turtle
import * setup(500, 500) Screen() title("Turtle Keys") move = Turtle() showturtle()
This is the bottom
onkey(k1, "Up") onkey(k2, "Left") onkey(k3, "Right") onkey(k4, "Down") listen() mainloop()
Below is my guess as to what you and your code snippets describe. Note that I changed the circle and square functions from the left and right keyboard buttons to the left and right mouse buttons which seemed to make more sense in the context of, "move to that location":
import turtle
import random
colors = ["red", "orange", "yellow", "green", "blue", "violet"]
radius = 10
width = 20
LEFT, MIDDLE, RIGHT = 1, 2, 3
def k1(x=None, y=None): # dummy arguments so can be a click or key
turtle.color(random.choice(colors))
def k2(x, y):
turtle.penup()
turtle.setposition(x, y)
turtle.pendown()
for _ in range(4):
turtle.forward(width)
turtle.right(90)
def k3(x, y):
turtle.penup()
turtle.setposition(x, y)
turtle.pendown()
turtle.circle(radius)
turtle.setup(500, 500)
turtle.Screen().title("Turtle Keys")
turtle.onkey(k1, " ")
turtle.onscreenclick(k2, btn=LEFT)
turtle.onscreenclick(k1, btn=MIDDLE)
turtle.onscreenclick(k3, btn=RIGHT)
turtle.listen()
turtle.mainloop()

Categories