I have been writing program about dragging turtles to different designated position.
However, turtle.ondrag(turtle.goto)
nor at[i].ondrag(at[i].goto)
does not work.
I've been amended with the solution from here Python turtle.ondrag not working @ Jellominer, but that's seems not working for my case (with multiple turtles). The code snippet at below.
import turtle as t
def handler(x, y, i):
global at
curTurtle = at[i]
curTurtle.ondrag(None) # disable handler inside handler
curTurtle.setheading(curTurtle.towards(x, y)) # turn toward cursor
curTurtle.goto(x, y) # move toward cursor
print(x, y, i)
curTurtle.ondrag(handler)
at = []
for i in range(3):
at.append(t.Turtle())
# Just to seperated turtles with color
at[0].color('grey')
at[0].goto(100, 100)
at[1].color('blue')
at[1].goto(0, 100)
at[2].color('green')
at[2].goto(100, 0)
for i in range(3):
at[i].ondrag(lambda x, y: handler(x, y, i))
t.listen()
t.mainloop()
Only the turtle with the largest index could be dragged, as seen from the print(x, y, i)
. It's there anyways to let the drag other turtle move as well?
CodePudding user response:
This where classes come in handy. The easiest way to solve this is to subclass the Turtle object and create the handler as an instance method, then when you construct the turtle you can assign it's ondrag listener to it's own version of the handler.
You might also want to bump the screen tracer a tiny bit so you don't get the lagging effect.
import turtle as t
class T(t.Turtle):
def dragging(self, x, y):
self.ondrag(None)
self.setheading(self.towards(x,y))
self.goto(x,y)
self.ondrag(self.dragging)
at = []
for i in range(3):
turtle = T()
turtle.ondrag(turtle.dragging)
at.append(turtle)
# Just to seperated turtles with color
at[0].color('grey')
at[0].goto(100, 100)
at[1].color('blue')
at[1].goto(0, 100)
at[2].color('green')
at[2].goto(100, 0)
# t.Screen().tracer(3) # uncomment to remove lagging.
t.listen()
t.mainloop()
CodePudding user response:
There are two problems with your code, the first is how the lambda
is setup. The second is that you use two different ways to set the ondrag
:
at[i].ondrag(lambda x, y: handler(x, y, i))
curTurtle.ondrag(handler)
So even if your lambda
was correct, you'd wipe it out on the first drag event. Here's a lambda
approach:
from turtle import Screen, Turtle
def handler(x, y, turtle):
turtle.ondrag(None) # disable handler inside handler
turtle.setheading(turtle.towards(x, y)) # turn toward cursor
turtle.goto(x, y) # move toward cursor
turtle.ondrag(lambda x, y, turtle=turtle: handler(x, y, turtle))
screen = Screen()
turtles = []
for _ in range(3):
turtle = Turtle()
turtle.ondrag(lambda x, y, turtle=turtle: handler(x, y, turtle))
turtles.append(turtle)
# Separate turtles by color
turtles[0].color('grey')
turtles[0].goto(100, 100)
turtles[1].color('blue')
turtles[1].sety(100)
turtles[2].color('green')
turtles[2].setx(100)
screen.listen()
screen.mainloop()
But I'm partial to functools.partial()
in these situations as it doesn't lead to the same confusion as lambda
:
from turtle import Screen, Turtle
from functools import partial
def handler(turtle, x, y):
turtle.ondrag(None) # disable handler inside handler
turtle.setheading(turtle.towards(x, y)) # turn toward cursor
turtle.goto(x, y) # move toward cursor
turtle.ondrag(partial(handler, turtle)) # reenable handler
screen = Screen()
turtles = []
for _ in range(3):
turtle = Turtle()
turtle.ondrag(partial(handler, turtle))
turtles.append(turtle)
# ...