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

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())

Related

How to draw square in Turtle module without filling color?

I am trying to draw a square in turtle without any thing filled in it. I would manually draw it with a loop, but I am trying to write a program where if the square is touched, something would happen. If I drew it with a loop, even if I touch the border, it would not work, because the actual turtle isn't there.
We can do this by dedicating one turtle as the button. We need to change its shape, size and fill color and assign it a button event handler:
from turtle import Screen, Turtle
def thanks(x, y):
marker.write("Thank you!", align='center', font=('Arial', 24, 'bold'))
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.sety(-200)
button = Turtle()
button.shape('square')
button.fillcolor('white')
button.shapesize(3)
button.onclick(thanks)
screen = Screen()
screen.mainloop()
Click on the square in the middle of the screen and it will thank you for doing so.
I am trying to draw a square in turtle without any thing filled in it.
The above solution does use a filled turtle, it just happens to be white fill. We can't draw within the square that is this button. If that's the need, then we can draw the square with a loop, but move our onclick() from the turtle to the screen. We'll need to make the onclick() itself determine if it's in the right place:
from turtle import Screen, Turtle
BUTTON_POSITION = (100, 100)
BUTTON_SIZE = 60
def thanks(x, y):
bx, by = BUTTON_POSITION
if bx < x < bx + BUTTON_SIZE and by < y < by + BUTTON_SIZE:
turtle.write("Thank you!", align='center', font=('Arial', 24, 'bold'))
turtle = Turtle()
turtle.hideturtle()
# draw the button
turtle.penup()
turtle.goto(BUTTON_POSITION)
turtle.pendown()
for _ in range(4):
turtle.forward(BUTTON_SIZE)
turtle.left(90)
# decorate our button a bit
turtle.penup()
turtle.forward(BUTTON_SIZE/2)
turtle.left(90)
turtle.forward(BUTTON_SIZE/2)
turtle.dot()
# position turtle for writing later
turtle.goto(0, -200)
screen = Screen()
screen.onclick(thanks)
screen.mainloop()

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()

python turtle creating flickering images when using tracer(0,0)

I am trying to make a program that will repeatedly draw two lines. my code is
import turtle
screen = turtle.Screen()
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
screen.tracer(0,0)
while True:
screen.clear()
t.penup()
t.goto(1,12)
t.pendown()
t.goto(4,67)
t.penup()
t.goto(50, 3)
t.pendown()
t.goto(4, 73)
screen.update()
I expect this to show two lines in turtle which do not flicker. however it is drawing one line and that line is flickering. the lines do need to be redrawn every frame so i can do some other stuff with the lines. Why is this happening?
Short answer: don't do screen.clear(), instead do t.clear().
When you clear the screen, you reset a number of its properties to the default values, including the tracer() setting. You simply want to clear whatever the turtle drew in the last iteration so clear the turtle instead.
In the long term, you don't want while True: in an event-driven environment like turtle. I would write this code more like:
from turtle import Screen, Turtle
def one_step():
turtle.clear()
turtle.penup()
turtle.goto(1, 12)
turtle.pendown()
turtle.goto(4, 67)
turtle.penup()
turtle.goto(50, 3)
turtle.pendown()
turtle.goto(4, 73)
screen.update()
screen.ontimer(one_step, 50)
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
one_step()
screen.mainloop()

How can I make my turtle move to my cursor?

I am trying to move the turtle to my cursor to draw something so that every time I click, the turtle will go there and draw something.
I have already tried onscreenclick(), onclick, and many combinations of both, I feel like I am doing something wrong, but I don't know what.
from turtle import*
import random
turtle = Turtle()
turtle.speed(0)
col = ["red","green","blue","orange","purple","pink","yellow"]
a = random.randint(0,4)
siz = random.randint(100,300)
def draw():
for i in range(75):
turtle.color(col[a])
turtle.forward(siz)
turtle.left(175)
TurtleScreen.onclick(turtle.goto)
Any help would be great, thank you for your time ( If you help me! ;)
It's not so much what method you're invoking, but on what object you're invoking it:
TurtleScreen.onclick(turtle.goto)
TurtleScreen is a class, you need to call it on a screen instance. And since you want to call draw in addition to turtle.goto you need to define your own function that calls both:
screen = Screen()
screen.onclick(my_event_handler)
Here's a rework of your code with the above fixes and other tweaks:
from turtle import Screen, Turtle, mainloop
from random import choice, randint
COLORS = ["red", "green", "blue", "orange", "purple", "pink", "yellow"]
def draw():
size = randint(100, 300)
# make turtle position center of drawing
turtle.setheading(0)
turtle.setx(turtle.xcor() - size/2)
turtle.pendown()
for _ in range(75):
turtle.color(choice(COLORS))
turtle.forward(size)
turtle.left(175)
turtle.penup()
def event_handler(x, y):
screen.onclick(None) # disable event handler inside event handler
turtle.goto(x, y)
draw()
screen.onclick(event_handler)
turtle = Turtle()
turtle.speed('fastest')
turtle.penup()
screen = Screen()
screen.onclick(event_handler)
mainloop()

Python multiple onclick events

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.

Categories