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()
Related
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())
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()
I am trying to move a drawing slowly across the screen as I move another turtle with the arrow keys. The problem is that my code only makes the drawing move across the screen, not allowing me to move the other turtle with the keys. I have tried defining the keys inside of the while true statement and it still came out with the same outcome.
Here is my code:
from turtle import *
setup(500, 500)
Screen()
screen = Screen()
title("Turtle Keys")
turtle1 = Turtle()
turtle2 = Turtle()
def moving_square():
turtle1.fillcolor("Red")
turtle1.begin_fill()
for i in range(4):
turtle1.forward(50)
turtle1.right(90)
turtle1.end_fill()
turtle1.goto(-350, 0)
turtle1.pendown()
turtle1.hideturtle()
def k1():
turtle2.forward(50)
def k2():
turtle2.left(45)
def k3():
turtle2.right(45)
def k4():
turtle2.back(25)
onkey(k1, "Up")
onkey(k2, "Left")
onkey(k3, "Right")
onkey(k4, "Down")
while True:
moving_square()
screen.update()
turtle1.forward(5)
It needs listen() to react on pressed keys.
And you have this information even in documentation for onkey()
from turtle import *
# --- functions ---
def moving_square():
turtle1.fillcolor("Red")
turtle1.begin_fill()
for i in range(4):
turtle1.forward(50)
turtle1.right(90)
turtle1.end_fill()
def k1():
turtle2.forward(50)
def k2():
turtle2.left(45)
def k3():
turtle2.right(45)
def k4():
turtle2.back(25)
# -- main ---
setup(500, 500)
Screen()
screen = Screen()
title("Turtle Keys")
turtle1 = Turtle()
turtle2 = Turtle()
turtle1.goto(-350, 0)
turtle1.pendown()
turtle1.hideturtle()
onkey(k1, "Up")
onkey(k2, "Left")
onkey(k3, "Right")
onkey(k4, "Down")
listen()
while True:
moving_square()
screen.update()
turtle1.forward(5)
For more game-like action, I would go about this a different way. First, by not drawing a red square but by making the first turtle into a red square. Second, by using a timed event instead of a while True: loop:
from turtle import Screen, Turtle
WIDTH, HEIGHT = 500, 500
BLOCK_SIZE = 50
CURSOR_SIZE = 20
def k1():
turtle.forward(50)
def k2():
turtle.left(45)
def k3():
turtle.right(45)
def k4():
turtle.back(25)
def move():
block.forward(1)
if block.xcor() < WIDTH/2 + BLOCK_SIZE/2:
screen.ontimer(move, 75) # milliseconds
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.title("Turtle Keys")
block = Turtle()
block.hideturtle()
block.shape('square')
block.fillcolor('red')
block.shapesize(BLOCK_SIZE / CURSOR_SIZE)
block.penup()
block.setx(-WIDTH/2 - BLOCK_SIZE/2)
block.showturtle()
turtle = Turtle()
turtle.shape('turtle')
turtle.penup()
screen.onkey(k1, 'Up')
screen.onkey(k2, 'Left')
screen.onkey(k3, 'Right')
screen.onkey(k4, 'Down')
screen.listen()
move()
screen.mainloop()
You can increase the speed of the block by adjusting block.forward(1) to higher value. And if you need a trailing line, you can put the pen down.
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()
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()