Canvas 项 event_generate

Canvas item event_generate

我会尝试 运行 我的命令 nothing()item 的嵌套 class 中,即使用户在项目附近单击也是如此,但它变成了无限循环。

所以我的想法是将鼠标移动到项目上,单击项目并返回到起始位置。为了保护一些行,我将它运行k 解决了这里这段代码的问题:

item = canvas.find_closest(canvas.canvasx(event.x),
                           canvas.canvasy(event.y))[0]

canvas.event_generate('<Motion>', warp=True, x=x, y=y)
canvas.event_generate('<Button-1>',
                      x=root.winfo_pointerx(),
                      y=root.winfo_pointery())
canvas.after(20,canvas.event_generate('<ButtonRelease-1>',
                                      x=root.winfo_pointerx(),
                                      y=root.winfo_pointery()))

我想保持结构如下,使用 tk.Frame 的子 class,网格我的 canvas 并定义用这段代码完成的项目这里:

class my_figure(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master

        # DownFrame
        self.button = tk.Button(self, text='add', command=self.add)
        self.button.grid(column=0, row=0)
        self.body = tk.Frame(self, relief='sunken')
        self.Display = tk.Canvas(self.body)
        self.Display.bind("<Button-1>", self.click)
        
        self.body.grid(column=0, row=1, sticky='nswe')
        self.Display.grid(column=0, row=0,
                          sticky='nswe')

    class item(object):
        def __init__(self,canvas, x1, y1,x2, y2):
            rec = canvas.create_rectangle(x1,y1,x2,y2)
            canvas.tag_bind(rec,'<Button-1>',self.nothing)
        
        def nothing(self, event):
            print('nothing')

其余代码都是垃圾,只是为了展示一些东西。 可在此处找到可执行示例:

import tkinter as tk

root = tk.Tk()


class my_figure(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master

        # DownFrame
        self.button = tk.Button(self, text='add', command=self.add)
        self.button.grid(column=0, row=0)
        self.body = tk.Frame(self, relief='sunken')
        self.Display = tk.Canvas(self.body)
        self.Display.bind("<Button-1>", self.click)
        
        self.body.grid(column=0, row=1, sticky='nswe')
        self.Display.grid(column=0, row=0,
                          sticky='nswe')

        self.x = tk.IntVar()
        self.x2 = tk.IntVar()
        self.y = tk.IntVar()
        self.y2 = tk.IntVar()
        self.x.set(10)
        self.x2.set(50)
        self.y.set(10)
        self.y2.set(10)
        
    def click(self, event):
        canvas = event.widget
        item = canvas.find_closest(canvas.canvasx(event.x),
                                   canvas.canvasy(event.y))[0]
        
        self.coord = (canvas.coords(item))
        x = self.coord[0]
        y = self.coord[1]
        print(item)
        print(self.coord)
        
        
        canvas.event_generate('<Motion>', warp=True, x=x, y=y)
        canvas.event_generate('<Button-1>',
                              x=root.winfo_pointerx(),
                              y=root.winfo_pointery())
        canvas.after(20,canvas.event_generate('<ButtonRelease-1>',
                                              x=root.winfo_pointerx(),
                                              y=root.winfo_pointery()))
    
        
    def add(self):
        x = self.item(self.Display,self.x.get(),self.y.get(),self.x2.get(),self.y2.get())
        
        self.old_x = self.x.get()
        self.old_x2 = self.x2.get()
        self.old_y = self.y.get()
        self.old_y2 = self.y2.get()
        self.x.set(self.old_x+40)
        self.x2.set(self.old_x2+40)
        self.y.set(self.old_y+40)
        self.y2.set(self.old_y2+80)

    
    class item(object):
        def __init__(self,canvas, x1, y1,x2, y2):
            rec = canvas.create_rectangle(x1,y1,x2,y2)
            canvas.tag_bind(rec,'<Button-1>',self.nothing)
            
        def nothing(self, event):
            print('nothing')


figure = my_figure(root)
figure.grid()
root.mainloop()

您可能过去做过一些事情并想分享经验,或者您有比这更好的想法,我们欢迎一切。

您可能对我的参考文献感兴趣:

https://wiki.tcl-lang.org/page/event+generate

http://mars.cs.utu.fi/BioInfer/files/doc/private/Tkinter.Canvas-class.html

https://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.event_generate-method

http://tcl.tk/man/tcl8.5/TkCmd/event.htm#M9

问题在这里:

canvas.event_generate('<Button-1>',
                              x=root.winfo_pointerx(),
                              y=root.winfo_pointery())

当生成一个 <Button-1> 事件时,<Button-1> 也会调用 click 函数 again.Then 它会无限调用它。

一个简单的解决方案是检查自定义状态变量(也许您需要做一些修改):

import tkinter as tk

root = tk.Tk()


class my_figure(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master

        # DownFrame
        self.button = tk.Button(self, text='add', command=self.add)
        self.button.grid(column=0, row=0)
        self.body = tk.Frame(self, relief='sunken')
        self.Display = tk.Canvas(self.body)
        self.Display.bind("<Button-1>", self.click)

        self.body.grid(column=0, row=1, sticky='nswe')
        self.Display.grid(column=0, row=0,
                          sticky='nswe')

        self.x = tk.IntVar()
        self.x2 = tk.IntVar()
        self.y = tk.IntVar()
        self.y2 = tk.IntVar()
        self.x.set(10)
        self.x2.set(50)
        self.y.set(10)
        self.y2.set(10)
        
        # add here
        self.check = True

    def click(self, event):
        canvas = event.widget
        item = canvas.find_closest(canvas.canvasx(event.x),
                                   canvas.canvasy(event.y))[0]

        self.coord = (canvas.coords(item))
        x = self.coord[0]
        y = self.coord[1]
        print(item)
        print(self.coord)

        canvas.event_generate('<Motion>', warp=True, x=x, y=y)
        if self.check:
            # when call it twice, pass it.
            self.check = False
            self.check = canvas.event_generate('<Button-1>',
                                  x=root.winfo_pointerx(),
                                  y=root.winfo_pointery())
        canvas.after(20, canvas.event_generate('<ButtonRelease-1>',
                                               x=root.winfo_pointerx(),
                                               y=root.winfo_pointery()))
        # restore.
        self.check = True

    def add(self):
        x = self.item(self.Display, self.x.get(), self.y.get(), self.x2.get(), self.y2.get())

        self.old_x = self.x.get()
        self.old_x2 = self.x2.get()
        self.old_y = self.y.get()
        self.old_y2 = self.y2.get()
        self.x.set(self.old_x + 40)
        self.x2.set(self.old_x2 + 40)
        self.y.set(self.old_y + 40)
        self.y2.set(self.old_y2 + 80)

    class item(object):
        def __init__(self, canvas, x1, y1, x2, y2):
            rec = canvas.create_rectangle(x1, y1, x2, y2)
            canvas.tag_bind(rec, '<Button-1>', self.nothing)

        def nothing(self, event):
            print('nothing')


figure = my_figure(root)
figure.grid()
root.mainloop()

问题的根源是您需要在 item 的实例上调用与 canvas 上的矩形相关的方法,并且您需要能够做到这一点仅基于矩形 ID。您将需要创建一种将矩形 ID 转换为 item class.

实例的方法

最简单的解决方案是:

  1. item 的实例保存在字典中,其中键是矩形 id
  2. 提供一个接口,用于根据矩形 id 获取 item 的实例。

保存每个实例非常简单:创建一个 class 字典变量,然后将实例添加到 __init__ 中的字典中。这是一个简单的例子:

class Item(object):
    _instances = {}
    def __init__(self,canvas, x1, y1,x2, y2):
        self.item_id = canvas.create_rectangle(x1,y1,x2,y2)
        self._instances[self.item_id] = self

因此,编写一个 class 方法可以 return 任何 canvas 项目 id 的 Item 的实例是微不足道的:

    @classmethod
    def get_item(cls, item_id):
        return cls._instances.get(item_id, None)

这是一个完整的工作示例,当您通过调用实例上的方法单击它时,它会更改最近元素的背景。我稍微调整了 item 以记住 canvas,并且我正在使用 PEP8 命名准则。

import tkinter as tk
import random

root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200, bg="bisque")
canvas.pack(fill="both", expand=True)

class Item(object):
    _instances = {}
    def __init__(self,canvas, x1, y1,x2, y2):
        self.canvas = canvas
        self.item_id = canvas.create_rectangle(x1,y1,x2,y2)
        self._instances[self.item_id] = self

    @classmethod
    def get_item(cls, item_id):
        return cls._instances.get(item_id, None)

    def nothing(self):
        self.canvas.itemconfigure(self.item_id, fill="red")

for i in range(20):
    x1 = random.randint(0, 175)
    y1 = random.randint(0, 175)
    x2 = x1 + random.randint(10,20)
    y2 = y1 + random.randint(10,20)
    Item(canvas, x1, y1, x2, y2)

def click_closest(event):
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)

    item_id = canvas.find_closest(x, y)[0]
    item = Item.get_item(item_id)
    item.nothing()

canvas.bind("<1>", click_closest)
root.mainloop()

这不一定是解决问题的最佳或最有效的方法,但它给出了如何将 python 对象与 canvas 项相关联的一般概念。