I am making a game in python called pong.
Can I get 2 different turtles in turtle graphics to respond simultaneously to keybindings?
Here is the code:
import turtle
class paddle(turtle.Turtle):
def __init__(self, x_cord, keybindings):
super().__init__("square")
self.color("white")
self.penup()
self.goto(x_cord, 0)
self.turtlesize(stretch_wid=5, stretch_len=1, outline=1)
self.screen = turtle.Screen()
self.screen.bgcolor("black")
self.screen.tracer(0)
self.screen.listen()
self.screen.update()
def up():
self.goto(self.xcor(), self.ycor() + 10)
self.screen.update()
def down():
self.goto(self.xcor(), self.ycor() - 10)
self.screen.update()
self.screen.onkey(up, keybindings[0])
self.screen.onkey(down, keybindings[1])
paddle_1 = paddle(-350, ["Up", "Down"])
paddle_2 = paddle(350, ["w", "s"])
food.screen.exitonclick()
This was once a problem I struggled with for a long time, and came to the conclusion that it's not possible (please prove me wrong, as I'm interested in the solution if there is one).
I've analyzed this great answer that explains how to bind two arrow keys for diagonal movement, but it only works one step at a time, just like how your code allows for simultaneous movement of the turtles as long as making them move one step at a time.
Anyways, that situation pushed me further into embracing the versatile Pygame python package.
Related
How do you make two turtles draw at once? I know how to make turtles draw and how to make two or more but I don't know how you can make them draw at the same time.
Please help!
Here's a minimalist example using timer events:
import turtle
t1 = turtle.Turtle(shape="turtle")
t2 = turtle.Turtle(shape="turtle")
t1.setheading(45)
t2.setheading(-135)
def move_t1():
t1.forward(1)
turtle.ontimer(move_t1, 10)
def move_t2():
t2.forward(1)
turtle.ontimer(move_t2, 10)
turtle.ontimer(move_t1, 10)
turtle.ontimer(move_t2, 10)
turtle.exitonclick()
New to ktinker and I've met a problem that I can't find a solution to.
My goal is to animate a shape and allow it to move using a function inside a while loop, and the function generates the shape while the while loop deletes and refreshes the canvas.
My code look something like this:
def shape():
global a
a = screen.create_rectangle(x,100,x+50,200,fill = 'white')
while True:
shape(x,y)
x+=10
screen.update()
screen.delete(a)
time.sleep(0.03)
the code successfully creates a rectangle and it moves, but the code isn't deleting the rectangles. However, the code works fine and deletes the rectangles if I'm not using a function.
The proper way to animate objects on a tk.Canvas is different from pygame and other GUI frameworks.
tk.Canvas does not require a "blitting" process; the items do not need to be deleted, and recreated each frame.
The proper approach is to move existing items, either using the tk.Canvas.move method, or the tk.Canvas.coord method. The first moves the item by a provided δx and δy, whereas the second re-positions the item to the new coordinates passed to it.
Here is an example with tk.Canvas.move:
import tkinter as tk
def shape(x):
return screen.create_rectangle(x, 100, x+50 , 200, fill='white')
def animate(rect, dx=0, dy=0):
screen.move(rect, dx, dy)
screen.after(100, animate, rect, dx)
if __name__ == '__main__':
root = tk.Tk()
screen = tk.Canvas(root, width=400, height=400, bg='cyan')
screen.pack()
rect = shape(50)
animate(rect, dx=10)
root.mainloop()
Notice that we make use of the tk.mainloop provided by the framework, instead of a clumsy while True loop. The tk.after method is the correct approach to call a function (here animate) at regular intervals.
We also avoid the use of time.sleep which always results to problems and blocking the GUI.
Try updating the screen after you delete the shape.
def shape():
global a
a = screen.create_rectangle(x,100,x+50,200,fill = 'white')
while True:
shape(x,y)
x+=10
screen.update()
screen.delete(a)
screen.update()
time.sleep(0.03)
I'm trying Turtle for the first time and running into some trouble. I'm using ipyturtle, a widget that lets you use Turtle inline on a Jupyter notebook. It seems to be missing some commands. For example:
from ipyturtle import Turtle
t = Turtle()
t
size = 10
angle = 20
t.reset()
for a in range(10):
for i in range(100):
t.right(1)
t.forward(i/size)
t.home()
t.right(a*angle)
draws the first line, then throws the error:
AttributeError: 'Turtle' object has no attribute 'home'
It also seems to be missing goto(), speed(), among other key commands. Am I doing something wrong? If it is missing commands, how can you tell? I've used Python a fair amount in an engineering context but am unfamiliar with Github. I'd really appreciate an explanation of how someone navigating the page I linked above might sniff out a list of available commands.
I've tried running the following very similar block of code on Repl.it and it works fine:
from turtle import Turtle
t = Turtle()
size = 15
angle = 20
for a in range(1, 19):
for i in range(100):
t.right(1)
t.forward(i/size)
t.home()
t.right(a*angle)
Thanks in advance for your help!
Looking at the ipyturtle code, these are the turle methods supported:
position(self)
forward(self, length)
back(self, length)
heading(self)
goto(self, x, y=None)
setpos(self, x, y=None)
setposition(self, x, y=None)
left(self, degree=None)
right(self, degree=None)
penup(self)
pendown(self)
isdown(self)
hideturtle(self)
showturtle(self)
isvisible(self)
reset(self)
pencolor(self,r=-1,g=-1,b=-1)
So you're right about home() and speed() but goto() does appear to be there. There also appears to be only one name for each command, not the wealth of aliases available in Python turtle (e.g. forward(), fd()).
The t.home() call can be replaced by:
t.goto(0, 0)
t.setheading(0)
But in your example, you immediately do right() afterward so we can combine that into the setheading(). I believe the following should work in ipyturtle, Repl.it and standard Python:
from turtle import Turtle
size = 10
angle = 20
t = Turtle()
for a in range(1, 19):
for i in range(100):
t.right(1)
t.forward(i / size)
t.goto(0, 0)
t.setheading(-a * angle)
I'm working on testing something for my teacher, he wants to see how the program below could possibly run faster if we simulated the simultaneous (i know it can't be perfectly simultaneous, this is just an experiment for the sake of learning/practicing) movement of multiple turtles. I've tried using modules like multiprocessing, threading, and even some crazy stupid attempt to time and delay (I'm in high school and I just learned about classes in python because of a previous question I asked I think last week)
So after many failed attempts I'm asking if someone has a few ideas of what else to try, or a direction to go in to simulate simultaneous movement of the turtles
import turtle
from turtle import Turtle
turtle.getscreen().delay(0)
class MyTurtle(Turtle):
def petal(self):
for i in range(90):
self.fd(1)
self.rt(1)
self.rt(90)
for i in range(90):
self.fd(1)
self.rt(1)
def stem(self):
self.pencolor('green')
self.fd(250)
def flowerhead(self):
for i in range(9):
self.pencolor('red')
self.begin_fill()
self.petal()
self.lt(230)
self.end_fill()
def stempetal(self):
self.seth(90)
self.rt(15)
self.fillcolor('green')
self.begin_fill()
self.petal()
self.end_fill()
tony = MyTurtle(shape='turtle')
todd = MyTurtle(shape='turtle')
tina = MyTurtle(shape='turtle')
tiny = MyTurtle(shape='turtle')
tweeny = MyTurtle(shape='turtle')
def flower1():
todd.speed('fastest')
todd.fillcolor('blue')
todd.flowerhead()
todd.seth(270)
todd.stem()
todd.stempetal()
def flower2():
tony.speed('fastest')
tony.setpos(80, -15)
tony.pencolor('green')
tony.goto(0, -200)
tony.fillcolor('purple')
tony.goto(80,-15)
tony.rt(40)
tony.flowerhead()
def flower3():
tina.speed('fastest')
tina.setpos(-80, -15)
tina.pencolor('green')
tina.goto(0, -200)
tina.fillcolor('teal')
tina.goto(-80,-15)
tina.lt(40)
tina.flowerhead()
def flower4():
tiny.speed('fastest')
tiny.setpos(160, -25)
tiny.pencolor('green')
tiny.goto(0, -200)
tiny.fillcolor('black')
tiny.goto(160, -25)
tiny.flowerhead()
def flower5():
tweeny.speed('fastest')
tweeny.setpos(-160, -25)
tweeny.pencolor('green')
tweeny.goto(0, -200)
tweeny.fillcolor('pink')
tweeny.goto(-160,-25)
tweeny.lt(40)
tweeny.flowerhead()
flower2()
tony.hideturtle()
flower4()
tiny.hideturtle()
flower3()
tina.hideturtle()
flower5()
tweeny.hideturtle()
flower1()
todd.hideturtle()
thank you for your time
The solution is to disable updating the position of each turtle, and then force the whole screen to update once the new position is computed.
import turtle
# our two turtle instances
first, second = turtle.Turtle(), turtle.Turtle()
first.tracer(False) # disable updating view on screen for this turtle!
second.tracer(False)
# make one move - note this will not appear on screen.
first.forward(50)
second.left(20)
# when you are ready to see the whole screen update
turtle.update()
To do what you want, you will have to essentially make it so that every new action is done before a turtle.update(). You cannot keep it to a serial execution as you are doing now - in other words, you can't run flower1, then flower2, in sequence.
Here's an example of a pair of turtles that will generate a random pattern on the screen at the same time:
import turtle
import random
# our two turtle instances
turtles = [turtle.Turtle(), turtle.Turtle()]
for turtle_object in turtles:
turtle_object.tracer(False)
for _ in range(10000): # make ten thousand moves.
for t in turtles:
# list the possible moves available
possible_moves = [t.forward, t.back, t.right, t.left]
# give it a random value
random_value = random.randint(0, 100)
# make a random move
random.choice(possible_moves)(random_value)
# update the whole screen now that the new positions have been calculated
turtle.update()
The trick here is to note that every new position for each turtle is calculated, then the screen as a whole is told to update, and only then do you move on to the next move. Every move must be as granular as possible.
You've asked for two different things, 'run faster' and 'simulate simultaneous movement'. I believe we can do both (separately) but I don't believe that tracer() and update() are the answer in this situation as they'd just be a band-aid to cover over the real issue.
wants to see how the program below could possibly run faster
If you want it to run faster, fix the bottleneck which is the petal() function. Replace it with something that uses turtle's built-in circle() function which is faster. For example:
def petal(self):
self.circle(-60, 90)
self.rt(90)
self.circle(-60, 90)
This speeds up your code by a factor of 25X with no other changes.
simulate simultaneous movement of the turtles
This can be done with turtle's own ontimer() event hander and some careful programming. Surprisingly, we use your original petal() logic as it breaks up the graphics into minute steps between which we can switch off processing to another timed event:
from random import randint
from turtle import Turtle, Screen
class MyTurtle(Turtle):
def petals(self, size=30, count=8, speed=100):
if size == 30:
self.begin_fill()
if size > 0: # drawing leading edge of petal
self.fd(3)
self.rt(3)
screen.ontimer(lambda: self.petals(size - 1, count, speed), speed)
return
if size == 0: # switch to other edge of petal
self.rt(90)
if size > -30: # drawing trailing edge of petal
self.fd(3)
self.rt(3)
screen.ontimer(lambda: self.petals(size - 1, count, speed), speed)
return
self.end_fill() # finish this petal
self.lt(230) # prepare for the next petal
if count > 0: # drawing the next petal
screen.ontimer(lambda: self.petals(count=count - 1, speed=speed), speed)
return
self.hideturtle() # finished drawing
def stem(self):
self.pencolor('green')
self.fd(250)
def flowerhead(self):
self.pencolor('red')
self.petals(speed=randint(50, 250))
def flower2():
tony.color('green', 'purple')
tony.penup()
tony.goto(0, -200)
tony.pendown()
tony.showturtle()
tony.goto(80, -15)
tony.rt(40)
tony.flowerhead()
def flower3():
tina.color('green', 'turquoise')
tina.penup()
tina.goto(0, -200)
tina.pendown()
tina.showturtle()
tina.goto(-80, -15)
tina.lt(40)
tina.flowerhead()
def flower5():
tweeny.color('green', 'pink')
tweeny.penup()
tweeny.goto(0, -200)
tweeny.pendown()
tweeny.showturtle()
tweeny.goto(-160, -25)
tweeny.lt(40)
tweeny.flowerhead()
tony = MyTurtle(shape='turtle', visible=False)
tina = MyTurtle(shape='turtle', visible=False)
tweeny = MyTurtle(shape='turtle', visible=False)
screen = Screen()
screen.ontimer(flower2, 100)
screen.ontimer(flower3, 120)
screen.ontimer(flower5, 100)
screen.mainloop()
RUNNING IMAGE
It won't be any faster, as it's just a simulation. (Well, it does go a little faster as I made petal drawing slightly cruder in return for speed.) If you look closely, you can see the turtles are (intentionally) moving at their own individual speed.
Hopefully I'll be able to explain this well. I'm currently using helper functions to draw a six-pointed star in the turtle graphics window of python. First, we had to create a function to draw a triangle. Here is my code:
import turtle
wn = turtle.Screen()
tess = turtle.Turtle()
tess.speed(30)
def triangle(sz):
for i in range(3):
tess.fd(sz)
tess.lt(120)
Then, we had to use the triangle function to draw a six-pointed star. Here is my code:
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd(80)
tess.rt(90)
tess.fd(120)
tess.pd()
tess.rt(180)
triangle(sz)
Now, for me, this all runs smoothly. But the parameters for our test run of those two functions was that sz = 120 (so in the shell we'd type sixPtdStar(120) and it would run. But then we had to draw a row of stars with a new function, and then a BOX outline by those rows of stars, in another function. Here is my code:
def rowOfStars(numInRow,sz):
for i in range(numInRow):
sixPtdStar(sz)
tess.pu()
tess.lt(90)
tess.fd(80)
tess.lt(90)
def sqrOfRows(numInRow, sz):
for i in range(4):
rowOfStars(numInRow, sz)
tess.rt(90)
While this accomplishes the task, it only does so if the sz = 120. And for our test run on the rowOfStars function, the parameters are supposed to be (6, 72) and for the test run on the sqrOfRows function, our parameters are supposed to be (6, 36).
So my issue is this. How can I make this work no matter what sz equals? When I run it as is (with (6, 72) for rowOfStars or (6, 36) for sqrOfRows), the pen moves too far because the triangles aren't as big anymore.
Please let me know if more info is needed! Thanks! (I'm using Python 3.5.2)
Anywhere you use a unit that has a dimension:
tess.fd(80)
tess.fd(120) # probably should be tess.fd(sz)
tess.fd(80)
you need to scale it by what ever logic you used to get from 120 (sz) to 80. However, as #wptreanor mentioned, that logic is slightly flawed as the points on your star are uneven:
Also, your rowOfStars() routine doesn't really draw a row of stars (math is off and the pen is in the wrong state at times.) Simply fixing the scaling won't fix this. Finally, your sqrOfRows() routine won't work until rowOfStars() is fixed, and to make it useful, you need to adjust the starting position on the screen to make room for the drawing.
Below is my rework of your code to address some of these issues. It uses a slightly different calculation of how to position from finishing the lower to starting the upper triangle so the numbers are slightly different:
from turtle import Turtle, Screen
WIDTH_RATIO = 2 * 3**0.5 / 3 # ratio of widest point in star to edge of triangle
def triangle(size):
for i in range(3):
tess.fd(size)
tess.lt(120)
def sixPtdStar(size):
triangle(size)
tess.lt(30)
tess.pu()
tess.fd(size * WIDTH_RATIO)
tess.lt(150)
tess.pd()
triangle(size)
def rowOfStars(numInRow, size):
for i in range(numInRow):
sixPtdStar(size)
tess.pu()
tess.lt(90)
tess.fd(size * WIDTH_RATIO / 2)
tess.lt(90)
tess.pd()
def sqrOfRows(numInRow, size):
tess.pu()
halfSize = numInRow * size / 2
tess.goto(-halfSize, halfSize) # center on screen
tess.pd()
for i in range(4):
rowOfStars(numInRow, size)
tess.rt(90)
screen = Screen()
tess = Turtle()
tess.speed("fastest") # numbers > 10 are all equivalent, safer to use symbols
sqrOfRows(6, 36)
screen.exitonclick()
The problem is in your sixPtdStar() function.
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd(80) # here
tess.rt(90)
tess.fd(120) # and here
tess.pd()
tess.rt(180)
triangle(sz)
If your function takes a size as a parameter, all functions involving movement (such as forward() or goto()) need to be scaled by the size as well. The following code should work:
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd((2.0/3.0)*sz) #formerly 80
tess.rt(90)
tess.fd(sz) #formerly 120
tess.pd()
tess.rt(180)
triangle(sz)
This will ensure that all forward movements are proportional to the size of the object you create. You will need to make similar tweaks to your rowOfStars() function. I've also noticed that your six pointed star isn't fully symmetrical. You could resolve that by replacing tess.fd((2.0/3.0)*sz) with tess.fd((7.0/12.0)*sz).