Home > Software design >  Python multiple turtle.ondrag not working
Python multiple turtle.ondrag not working

Time:11-05

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)

# ...
  • Related