Guilmort
Guilmort

Reputation: 41

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

I cannot understand why this small piece of code works well for green points (when I click on it, displayed tags are OK) but not at all for red ones (tags are always the same although the loop : the latest one in).

Is it a bug? Or do I miss something?

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()

Upvotes: 0

Views: 231

Answers (1)

Mat.C
Mat.C

Reputation: 1429

Change the code as this

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()

The problem is in the line where you bind the red button event canvas.tag_bind(p, '<Button-1>', lambda event: show(event, p, tag))

at lambda you have to pass the args p=p, tag=tag too, if not in the callback are passed the refereces of the tag and p variables that are used in the for loop. This will cause that each time that you change that variables they will change in the callback functions too and it will generate the "error" that you are seeing. Instead, if you set the varibles inside labmda you will pass at the callback new variables that will not be touched again.

Upvotes: 2

Related Questions