Tkinter смешивает привязки разных виджетов, когда виджеты создаются в цикле, но когда виджеты создаются отдельно, привязки правильные. Я не могу понять, почему это так.

Неправильные привязки

Вот рабочий пример для воспроизведения проблемы. Два виджета (изображения, которые должны работать как кнопки, которые выделяются при наведении курсора) создаются в цикле и упаковываются во втором цикле.

import tkinter as tk
import tkinter.ttk as ttk


IMG_DEFAULT = 'add_default.png'
IMG_HOVER = 'add_hover.png'

class Application(tk.Tk):
    def __init__(self, master=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title('WRONG!')
        self.img_default = tk.PhotoImage(file=IMG_DEFAULT)
        self.img_hover = tk.PhotoImage(file=IMG_HOVER)
        self.create_widgets()
        self.pack_widgets()

    def create_widgets(self):
        self.buttons = []
        for i in range(2):
            button = ttk.Label(image=self.img_default)
            button.bind('<Button-1>', ...)  # Do something
            button.bind('<Enter>', lambda e: button.config(image=self.img_hover))
            button.bind('<Leave>', lambda e: button.config(image=self.img_default))
            self.buttons.append(button)

    def pack_widgets(self):
        for button in self.buttons:
            button.pack(padx=100, pady=10)

app = Application()
app.mainloop()

Как видно из изображения ниже, привязки неправильные: когда я наводил курсор на верхнюю кнопку, подсвечивается нижняя кнопка.

enter image description here

Правильная привязка

Когда виджеты создаются вне цикла, привязки клавиш правильные.

import tkinter as tk
import tkinter.ttk as ttk


IMG_DEFAULT = 'add_default.png'
IMG_HOVER = 'add_hover.png'

class Application(tk.Tk):
    def __init__(self, master=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title('RIGHT!')
        self.img_default = tk.PhotoImage(file=IMG_DEFAULT)
        self.img_hover = tk.PhotoImage(file=IMG_HOVER)
        self.create_widgets()
        self.pack_widgets()

    def create_widgets(self):
        button1 = ttk.Label(image=self.img_default)
        button1.bind('<Button-1>', ...)   # Do something
        button1.bind('<Enter>', lambda e: button1.config(image=self.img_hover))
        button1.bind('<Leave>', lambda e: button1.config(image=self.img_default))

        button2 = ttk.Label(image=self.img_default)
        button2.bind('<Button-1>', ...)   # Do something
        button2.bind('<Enter>', lambda e: button2.config(image=self.img_hover))
        button2.bind('<Leave>', lambda e: button2.config(image=self.img_default))

        self.buttons = [button1, button2]

    def pack_widgets(self):
        for button in self.buttons:
            button.pack(padx=100, pady=10)

app = Application()
app.mainloop()

Как видно из изображения, теперь привязки правильные: при наведении курсора на верхнюю кнопку подсвечивается верхняя кнопка, а при наведении курсора на нижнюю она выделяется.

enter image description here

Может ли кто-нибудь объяснить, почему второй случай работает так, как задумано, а первый - нет? В моем приложении у меня есть более общий вариант использования с неизвестным количеством кнопок. Как мне установить правильные привязки?

РЕДАКТИРОВАТЬ

Как указал Брайан Окли в своем комментарии, мой вопрос является дубликатом Tkinter назначает команду кнопки в цикле с лямбдой . Оба решения Генри (https://stackoverflow.com/a/66663554/12646289) и acw1668 предоставляют решение.

Спасибо большое за вашу помощь! Я поддержал комментарии и принял ответ Генри.

2
Jan-WillemL 17 Мар 2021 в 00:05

1 ответ

Лучший ответ

Эта проблема вызвана вашими лямбдами. Когда привязка вызывается, она использует текущее значение кнопки для config. Если бы вы сделали 4 кнопки, все они изменили бы изображение на последней, потому что это последнее значение кнопки. (Я не уверен, хорошо ли я это объяснил, подробнее см. этот ответ). Вам нужно изменить лямбду на

lambda e, b = button: b.config(...)

Это создает ссылку на конкретную кнопку, поэтому при вызове привязки изображение этой кнопки изменяется.

0
Henry 16 Мар 2021 в 21:18