How to handle clicks on a turtle and clicks off of a turtle separately?

余生颓废 提交于 2020-06-17 15:48:28

问题


Using the Python 3 "turtle" module, I am trying to handle two different click conditions separately:

  1. If a turtle is clicked on, it should call a function. (In the example below, it should switch from black to red, or vice versa.)
  2. If a click is NOT on a (visible) turtle, a different function should be called (to create a turtle at that point).

Right now I can make this work using two different mouse buttons, like so:

#!/usr/local/bin/python3

import turtle

sc = turtle.Screen()

def new_turtle(x, y):
  t = turtle.RawTurtle(sc, shape='circle', visible=False)
  t.penup()
  t.speed(0)
  t.goto(x, y)
  t.color('black')
  t.showturtle()
  t.onclick(selector(t), 2)

def deselector(t):
  def deselect(x, y):
    t.color('black')
    t.onclick(selector(t), 2)
  return deselect

def selector(t):
  def select(x, y):
    t.color('red')
    t.onclick(deselector(t), 2)
  return select

sc.onscreenclick(new_turtle, 1)

turtle.mainloop()

However, I want to use the second mouse button for other things.

If the above code is changed to use mouse button 1 for everything, the turtles do change color when clicked like they're supposed to, but the onscreenclick handler is also called so that a new turtle is created almost right above the turtle that changes color.

Is there any way to only call the onscreenclick handler if the click is not on a turtle?


回答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.



来源:https://stackoverflow.com/questions/62336296/how-to-handle-clicks-on-a-turtle-and-clicks-off-of-a-turtle-separately

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!