Есть ли способ изменить изображение итеративно созданной кнопки при нажатии на нее?

Кнопки создаются в более крупном цикле, повторяющем i, а затем j со следующим кодом:

btn[i][j] = Button(lbl_frm, width=24, height=24, image=unchecked_img,
                                       command=lamda:change_btn_img(btn[i][j]),relief=SOLID)

                global state
                state = "unchecked"

                btn[i][j].place(relx=.5, rely=.5, anchor='c')

с функцией изменения конфигурации кнопки:

def change_btn_img(btn):
    global state
    if state == "checked":
        btn.configure(image=unchecked_img)
        state = "unchecked"

    elif state == "unchecked":
        btn.configure(image=checked_img)
        state = "checked"

Однако это не работает, так как если я нажму любую кнопку, это только изменит изображение btn[i][j], где i и j были значениями, достигнутыми на последней итерации цикла. Кнопки используются для формирования сетки, и в этом случае любой щелчок изменяет последний элемент в последней строке. Есть ли способ сделать так, чтобы i и j, используемые при объявлении команды при создании кнопки, были привязаны к этой конкретной кнопке?


person Dillon    schedule 10.02.2021    source источник
comment
привет, интересно, возможно, это может быть интересно stackoverflow.com/questions/44210557/   -  person IronMan    schedule 10.02.2021
comment
распространенная проблема с лямбдой в цикле: command=lamda x=i, y=j:change_btn_img(btn[x][y])   -  person furas    schedule 10.02.2021


Ответы (1)


Это общая проблема с lambda в loop.

Вы должны присвоить i, j аргументам в lambda (т.е. x, y), и он будет копировать значения из i, j вместо использования ссылок на i, j

 command=lamda x=i, y=j:change_btn_img(btn[x][y]) 

Кстати:

В Python вы можете создавать собственные атрибуты в классе, поэтому, возможно, вам следует оставить state в Button

 btn[i][j].state = "unchecked"

и тогда не надо global и у каждой кнопки свой state


Или, может быть, вы должны создать свой собственный Widget - что-то вроде этого:

class MyButton(tkinter.Button):

    def __init__(self, parent, *args, **kwars):
        super().__init__(parent, width=24, height=24, image=unchecked_img, command=self.change, relief=tkitner.SOLID)
        self.state = "unchecked"
        
    def change(self):
        if self.state == "checked":
            self.configure(image=unchecked_img)
            self.state = "unchecked"
        elif self.state == "unchecked":
            self.configure(image=checked_img)
            self.state = "checked"
            

а позже вы могли бы просто использовать

btn[i][j] = MyButton(lbl_frm)

btn[i][j].place(relx=.5, rely=.5, anchor='c')
person furas    schedule 10.02.2021
comment
Теперь работает отлично, большое спасибо. Спасибо за ваше предложение. Теперь я реализовал класс кнопок. - person Dillon; 10.02.2021
comment
вы можете пометить мой ответ как принятый и через несколько минут вы можете проголосовать за него. - person furas; 10.02.2021