tkinter creating buttons in for loop passing command arguments
tkinter создает кнопки в цикле передачи аргументов команды
Я пытаюсь создавать кнопки в tkinter в for цикле. И с каждым циклом передаю i значение count в качестве аргумента в значении команды. Поэтому, когда функция вызывается из command значения, я могу сказать, какая кнопка была нажата, и действовать соответствующим образом.
Проблема в том, что, скажем, длина равна 3, это создаст 3 кнопки с заголовками Game 1 через Game 3, но при нажатии любой из кнопок печатное значение всегда 2, последняя итерация. Похоже, что кнопки создаются как отдельные объекты, но i значение в аргументах команды, похоже, одинаковое. Вот код:
defcreateGameURLs(self): self.button = [] for i inrange(3): self.button.append(Button(self, text='Game '+str(i+1), command=lambda: self.open_this(i))) self.button[i].grid(column=4, row=i+1, sticky=W)
defopen_this(self, myNum): print(myNum)
Есть ли способ заставить текущее i значение на каждой итерации привязываться к этой конкретной кнопке?
Измените свой лямбда на lambda i=i: self.open_this(i).
Это может показаться волшебным, но вот что происходит. Когда вы используете эту лямбду для определения своей функции, вызов open_this не получает значение переменной i во время определения функции. Вместо этого он выполняет замыкание, которое похоже на заметку самому себе, в которой говорится: "Я должен посмотреть, каково значение переменной i на момент вызова". Конечно, функция вызывается после завершения цикла, поэтому в это время i всегда будет равно последнему значению из цикла.
Использование i=i трюка заставляет вашу функцию сохранять текущее значение i на момент определения вашего лямбда-выражения, вместо того, чтобы ждать поиска значения i позже.
Ответ 2
Вот как работают замыкания в python. Я сам однажды столкнулся с этой проблемой. Вы могли бы использовать functools.partial для этого.
for i inrange(3): self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))
Ответ 3
Просто прикрепите область действия кнопок к лямбда-функции следующим образом:
btn["command"] = lambda btn=btn: click(btn) где click(btn) - функция, которая передает саму кнопку. Это создаст область привязки от кнопки к самой функции.
Характеристики:
Настройка размера сетки
Адаптивное изменение размера
Переключить активное состояние
#Python2 #from Tkinter import * #import Tkinter as tkinter #Python3 from tkinter import * import tkinter
defmain(height=5,width=5): for x inrange(width): for y inrange(height): btn = tkinter.Button(frame, bg=default_color) btn.grid(column=x, row=y, sticky=N+S+E+W) btn["command"] = lambda btn=btn: click(btn)
for x inrange(width): Grid.columnconfigure(frame, x, weight=1)
for y inrange(height): Grid.rowconfigure(frame, y, weight=1)
It's because the value for the name i changes and isn't captured by lambda:. (You can try that theory out by adding i = 1234 after the loop and seeing what happens.)
You'll need to write a function to wrap that i as a local name, then return a lambda in that function that captures i .
Another option is functools.partial, which does effectively the same thing:
command=functools.partial(button_click, i)
All in all, you can also simplify things a bit by using just range to get numbers from 0 to 10 and divmod to get the row and column in one function call: