Python;tkinter;画布对象与事件

2 投票
1 回答
6299 浏览
提问于 2025-04-17 23:06

我有一个类,里面包含了一些鼠标事件:

class graphic_object(object):

   def mouse_click(self,event):
       #do something

   def mouse_move(self,event):
       #do something

   def mouse_unpressed(self,event):
       #do something

这个类的实例并不是屏幕上的真实图形对象,但它们有自己的图形表现,形状是圆形的。正如我所说的,它们会监听鼠标事件。图形表现和事件处理都是由一个叫做 tkinter.Canvas 的对象来管理的,这个对象就像它们的视觉容器。
当我创建这个类的一个实例时:

graphic1 = graphic_object(a,b,c,d)   # init method takes coordinates of the circle as arguments; a,b,c,d - numbers

一切都按预期工作,对象会以我希望的方式对鼠标事件做出反应。但是当我创建两个实例时:

graphic1 = graphic_object(a,b,c,d)
graphic2 = graphic_object(e,f,g,h)

只有最后创建的对象会对鼠标事件做出反应。

这是我用来检查鼠标是否在圆形上方的条件:

if d < self.radius:

这里 d 是鼠标位置和圆心之间的距离,而 radius 是圆的半径。
在调试器中,我发现 self.center 总是指向最后创建的对象的圆心,所以条件总是作用于第二个圆形。那么,我该如何让两个对象都能对鼠标事件做出反应呢?

事件处理:

C = Canvas()
C.bind("<Button-1>" ,self.mouse_click)
C.bind("<B1-Motion>",self.mouse_move)
C.bind("<ButtonRelease-1>",self.mouse_unpressed)

1 个回答

3

看起来你在鼠标绑定的时候,依赖了一个事先计算好的全局变量(d)。其实这样做并不合适。你在绑定的第一步应该是获取当前鼠标的位置,然后再计算出d

另外一个选择是把绑定放在每个画布对象上,使用画布的tag_bind方法。你可以参考这个问题的例子:如何在Tkinter中将事件绑定到画布上的项目?

你在这个回答的评论中提到,你有时候才能接收到鼠标点击。你的代码细节不够,无法判断你具体在做什么,但我可以肯定的是,画布通常不会出现这种问题。

我无法调试你的代码,因为你只展示了一些片段,不过这里有一个工作示例,试图说明如何使用tag_bind。我对你的代码做了一些调整,比如我添加了一个名称参数,这样我可以打印出你点击的是哪个圆圈。当我测试这个时,每次点击似乎都能正确识别到对应的圆圈。

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.canvas = tk.Canvas(self, width=400,  height=400, 
                                background="bisque")
        self.canvas.pack(fill="both", expand=True)

        graphic1 = GraphicObject(10,10,100,100, name="graphic1")
        graphic2 = GraphicObject(110,110,200,200, name="graphic2")

        graphic1.draw(self.canvas)
        graphic2.draw(self.canvas)

class GraphicObject(object):
    def __init__(self, x0,y0,x1,y1, name=None):
        self.coords = (x0,y0,x1,y1)
        self.name = name

    def draw(self, canvas, outline="black", fill="white"):
        item = canvas.create_oval(self.coords, outline=outline, fill=fill)
        canvas.tag_bind(item, "<1>", self.mouse_click)

    def mouse_click(self, event):
        print "I got a mouse click (%s)" % self.name

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

撰写回答