Python 3.8 / Tkinter:canvas 中的项目无法绑定到循环中的函数

Python 3.8 / Tkinter: items in canvas cannot be bound to a function from within a loop

我不明白为什么这段代码对绿点有效很好(当我点击它时,显示的标签没问题)但一点也不对于红色的(标签总是相同的,尽管循环:最新的一个)。

这是一个错误吗?还是我错过了什么?

import tkinter as tk

def show(event, p, tag):
    print(f"{p=}\n{tag=}")


centers = [[50, 50], [90, 50]]

root = tk.Tk()
canvas = tk.Canvas()
canvas.grid(row=0, column=0, sticky='news')

p1 = canvas.create_oval(20,10,30,20, fill='green',tags=('point_green_1', 'draw'))
p2 = canvas.create_oval(50,10,60,20, fill='green',tags=('point_green_2', 'draw'))
canvas.tag_bind(p1, '<Button-1>', lambda event: show(event, p1, ('point_green_1', 'draw')))
canvas.tag_bind(p2, '<Button-1>', lambda event: show(event, p2, ('point_green_2', 'draw')))

for idx, center in enumerate(centers):
    tag= f'point_{idx}'
    p=canvas.create_oval(center[0] - 5, center[1] - 5, center[0] + 5, center[1] + 5, fill='red',
                         tags=(tag, 'draw'))
    canvas.tag_bind(p, '<Button-1>', lambda event: show(event, p, tag))

root.mainloop()

将代码改成这样

import tkinter as tk

def show(event, p, tag):
    print(f"{p}\n{tag}")


centers = [[50, 50], [90, 50]]

root = tk.Tk()
canvas = tk.Canvas()
canvas.grid(row=0, column=0, sticky='news')

p1 = canvas.create_oval(20,10,30,20, fill='green',tags=('point_green_1', 'draw'))
p2 = canvas.create_oval(50,10,60,20, fill='green',tags=('point_green_2', 'draw'))
canvas.tag_bind(p1, '<Button-1>', lambda event: show(event, p1, ('point_green_1', 'draw')))
canvas.tag_bind(p2, '<Button-1>', lambda event: show(event, p2, ('point_green_2', 'draw')))

for idx, center in enumerate(centers):
    tag= f'point_{idx}'
    p=canvas.create_oval(center[0] - 5, center[1] - 5, center[0] + 5, center[1] + 5, fill='red', tags=(tag, 'draw'))
    canvas.tag_bind(p, '<Button-1>', lambda event, p=p, tag=tag: show(event, p, tag))

root.mainloop()

问题出在绑定红色按钮事件的那一行canvas.tag_bind(p, '<Button-1>', lambda event: show(event, p, tag))

在 lambda 中你也必须传递参数 p=p, tag=tag,如果不在回调中传递 tagp 中使用的变量的引用 for loop。这将导致每次您更改这些变量时,它们也会在回调函数中更改,并且会生成您看到的 "error"。相反,如果您在 labmda 中设置变量,您将在回调中传递不会再次被触及的新变量。