Skip to main content
 首页 » 编程设计

Python turtle ondrag 事件与复合形状

2025年05月04日7小虾米

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

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

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 特有的?

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

请您参考如下方法:

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

我在 Python bug tracker 中查找了这个问题,幸运的是有人已经确定了这个问题并且其他人已经提交了一个补丁: 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()