Python turtle ondrag 事件与复合形状

Python turtle ondrag event vs. compound shapes

当我注册一个多边形或只有一个组件的复合形状时,我可以使用该形状创建一个海龟光标,添加一个拖动事件处理程序,然后将其拖动到屏幕上。

但是当我用第二个组件注册复合形状时,我不能再拖动它了:

from turtle import Turtle, Screen, Shape

def simple_polygon(turtle):

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    screen.register_shape("simple_polygon", turtle.get_poly())

    turtle.reset()

def compound_single(turtle):

    shape = Shape("compound")

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "blue", "blue")  # component #1
    screen.register_shape("compound_single", shape)

    turtle.reset()

def compound_double(turtle):

    shape = Shape("compound")

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "green", "green")  # component #1

    turtle.penup()
    turtle.left(90)
    turtle.forward(25)
    turtle.right(90)
    turtle.pendown()

    turtle.begin_poly()
    turtle.circle(25)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "yellow", "yellow")  # component #2
    screen.register_shape("compound_double", shape)

    turtle.reset()

def drag_handler(turtle, x, y):
    turtle.ondrag(None)  # disable ondrag event inside drag_handler
    turtle.goto(x, y)
    turtle.ondrag(lambda x, y, turtle=turtle: drag_handler(turtle, x, y))

screen = Screen()

magic_marker = Turtle()
simple_polygon(magic_marker)
compound_single(magic_marker)
compound_double(magic_marker)
magic_marker.hideturtle()

red = Turtle(shape="simple_polygon")
red.color("red")
red.penup()
red.goto(150, 150)
red.ondrag(lambda x, y: drag_handler(red, x, y))

blue = Turtle(shape="compound_single")
blue.penup()
blue.goto(-150, -150)
blue.ondrag(lambda x, y: drag_handler(blue, x, y))

mostly_green = Turtle(shape="compound_double")
mostly_green.penup()
mostly_green.goto(150, -150)
mostly_green.ondrag(lambda x, y: drag_handler(mostly_green, x, y))

screen.mainloop()

你会发现生成的三个形状中只有两个可以拖动。注释掉这一行:

shape.addcomponent(turtle.get_poly(), "yellow", "yellow")  # component #2

第三个圆圈将全绿并且可以拖动。

我在 turtle 文档中找不到关于具有多个组件的复合形状就拖动而言不是有效光标的任何提及。第二个组件是否完全在第一个组件内、重叠或外部没有任何区别。

查看 turtle 代码,我看不出有什么区别,这让我相信这个问题出在 tkinter 基础上,而没有在 turtle 中正确记录。这个问题是 Unix 还是 OSX 特定的?

我错过了什么吗?为什么我不能拖动由多个组件构建的光标?

我认为发生的事情是,当您拖动一个对象时,整个 canvas 都会移动,看起来就像在移动一样。

同样的事情发生在 Canvas Tkinter 中。 当您编写单击某个对象时发生的事情时,整个 canvas 都在等待单击。所以这意味着如果两个对象有一个 onclick 事件,整个事情就不会起作用。

希望对您有所帮助!!

我最近也遇到了这个问题,很高兴找到你的问题,cdlane,以确认我不仅仅是想象的事情——Python 的 turtle 模块中可能确实存在错误。

我在 Python 错误跟踪器中查找了这个问题,幸运的是有人已经确定了这个问题并且其他人已经提交了补丁: https://bugs.python.org/issue16428

虽然在撰写本文时该补丁尚未合并到 Python 中,但我尝试修改我安装的 Python 版本以合并该补丁,并且成功了。

如果您不想对已安装的 Python 进行更改,您可以直接在您的代码中猴子修补 turtle 对象以合并补丁,如下所示:

[请注意,为了提高性能,我稍微修改了您的代码,添加了 tracer(0) 和 update();然而,补丁在没有这些添加的情况下仍然有效。]

from turtle import Turtle, Screen, Shape, tracer, update

#### Monkey patch Turtle object to allow compound shapes to be dragged. ####
####### Based on patch by Ingrid: https://bugs.python.org/issue16428 #######

def onclick(self, fun, btn=1, add=None):
    if (self.turtle._type == 'compound'):
        for i in self.turtle._item:
            self.screen._onclick(i, fun, btn, add)
    else:
        self.screen._onclick(self.turtle._item, fun, btn, add)
    self._update()

Turtle.onclick = onclick

def onrelease(self, fun, btn=1, add=None):
    if (self.turtle._type == 'compound'):
        for i in self.turtle._item:
            self.screen._onrelease(i, fun, btn, add)
    else:
        self.screen._onrelease(self.turtle._item, fun, btn, add)
    self._update()

Turtle.onrelease = onrelease

def ondrag(self, fun, btn=1, add=None):
    if (self.turtle._type == 'compound'):
        for i in self.turtle._item:
            self.screen._ondrag(i, fun, btn, add)
    else:
        self.screen._ondrag(self.turtle._item, fun, btn, add)

Turtle.ondrag = ondrag

############################ End Monkey patch. #############################

def simple_polygon(turtle):

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    screen.register_shape("simple_polygon", turtle.get_poly())

    turtle.reset()

def compound_single(turtle):

    shape = Shape("compound")

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "blue", "blue")  # component #1
    screen.register_shape("compound_single", shape)

    turtle.reset()

def compound_double(turtle):

    shape = Shape("compound")

    turtle.begin_poly()
    turtle.circle(50)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "green", "green")  # component #1

    turtle.penup()
    turtle.left(90)
    turtle.forward(25)
    turtle.right(90)
    turtle.pendown()

    turtle.begin_poly()
    turtle.circle(25)
    turtle.end_poly()
    shape.addcomponent(turtle.get_poly(), "yellow", "yellow")  # component #2
    screen.register_shape("compound_double", shape)

    turtle.reset()

def drag_handler(turtle, x, y):
    turtle.ondrag(None)  # disable ondrag event inside drag_handler
    turtle.goto(x, y)
    update()
    turtle.ondrag(lambda x, y, turtle=turtle: drag_handler(turtle, x, y))

screen = Screen()
tracer(0)

magic_marker = Turtle()
simple_polygon(magic_marker)
compound_single(magic_marker)
compound_double(magic_marker)
magic_marker.hideturtle()

red = Turtle(shape="simple_polygon")
red.color("red")
red.penup()
red.goto(150, 150)
red.ondrag(lambda x, y: drag_handler(red, x, y))

blue = Turtle(shape="compound_single")
blue.penup()
blue.goto(-150, -150)
blue.ondrag(lambda x, y: drag_handler(blue, x, y))

mostly_green = Turtle(shape="compound_double")
mostly_green.penup()
mostly_green.goto(150, -150)
mostly_green.ondrag(lambda x, y: drag_handler(mostly_green, x, y))
update()
screen.mainloop()