Skip to content Skip to sidebar Skip to footer

How To Handle Clicks On A Turtle And Clicks Off Of A Turtle Separately?

Using the Python 3 'turtle' module, I am trying to handle two different click conditions separately: If a turtle is clicked on, it should call a function. (In the example below,

Solution 1:

I believe the following will do what you describe. As you noted, if there is both a screen button event handler and a turtle button event handler active on the same button, both get triggered! This seems wrong for a beginner friendly programming environment sitting atop tkinter. But there you have it.

My solution below is to have the screen button event handler test if the click was likely over a turtle, and, if so, ignore it. This lets just the turtle button event handler deal with the click:

from turtle import Screen, Turtle
from functools import partial

CURSOR_SIZE = 20

def new_turtle(x, y):
    screen.onscreenclick(None)  # disable event handler inside handler

    # don't respond if the click was on a turtle
    if not any(t.distance(x, y) <= CURSOR_SIZE/2 for t in screen.turtles()):
        t = Turtle(shape='circle', visible=False)
        t.color('black')
        t.penup()
        t.goto(x, y)
        t.showturtle()
        t.onclick(partial(select, t))

    screen.onscreenclick(new_turtle)  # reenable event handler

def select(t, x, y):
    t.color('black' if t.pencolor() == 'red' else 'red')

screen = Screen()

screen.onscreenclick(new_turtle)

screen.mainloop()

One side effect is that clicking on the screen away from turtles gets slightly sluggish as lots of turtles are added to the screen and need to be tested. To get around this, I noticed, on my system at least, that turtle button event handlers are invoked before screen button event handlers. So the trick is to have the turtle button event handler disable the screen button event handler, but eventually reenable it:

from turtle import Screen, Turtle
from functools import partial, update_wrapper

def new_turtle(x, y):
    screen.onscreenclick(None)  # disable this event handler inside handler

    t = Turtle(shape='circle', visible=False)
    t.color('black')
    t.penup()
    t.goto(x, y)
    t.showturtle()
    t.onclick(partial(select, t))

    screen.onscreenclick(new_turtle)  # reenable event handler

def select(t, x, y):
    screen.onscreenclick(None)  # disable screen event handler inside handler
    t.onclick(None)  # disable this event handler inside handler

    t.color('black' if t.pencolor() == 'red' else 'red')

    t.onclick(partial(select, t))  # reenable this event handler
    screen.ontimer(wrapper)  # reenable screen event handler, eventually

screen = Screen()

wrapper = partial(screen.onscreenclick, new_turtle)  # prep wrapper for later use
update_wrapper(wrapper, screen.onscreenclick)

screen.onscreenclick(new_turtle)

screen.mainloop()

This does not require examing the turtles so won't slow down. (Nor be as finicky as you click very close to the turtle.) However if the timing doesn't match up the same, you might have to use the other version.


Post a Comment for "How To Handle Clicks On A Turtle And Clicks Off Of A Turtle Separately?"