Note that my problem is the opposite of this: Creating functions in a loop in that I have many buttons and one function, not many functions.
I create 10 numbered buttons from a for
loop, then try to bind each one to a function that will print the button's number; See code below:
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
# creating buttons and adding them to dictionary
self.buttons = {}
for number in range(1, 11):
self.buttons.update({'button' + str(number): tk.Button(self, height=1, width=4, bg="grey", text=number)})
# example of a pair in the dictionary: 'button2': <Tkinter.Button instance at 0x101f9ce18>
""" bind all the buttons to callback, each button is
named something like 'button3',so I take the number off
the end of its name and feed that as an argument to Callback"""
for button in self.buttons:
self.buttons[button].bind('<Button-1>', lambda event: self.Callback(event, button[6:]))
self.buttons[button].pack(side='left')
def Callback(self, event, num):
print(num)
All the buttons appear on the window no problem, but when I click any of them, the console prints ' 10
', as opposed to the button's number. It seems the function is only remembering the last argument it was given.
First lets correct your code to give the desired answer.
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.buttons = {}
for number in range(1, 11):
self.buttons.update({'button' + str(number): tk.Button(self, height=1, width=4, bg="grey", text=number)})
for button in self.buttons:
self.buttons[button].bind('<Button-1>', lambda event, : self.Callback(event, num))
self.buttons[button].pack(side='left') #\____________/
def Callback(self, event, num):
print(num)
Window().mainloop()
Explanation :
The trick lies in the way lambda functions work.
When you write lambda event: self.Callback(event, button[6:])
, it doesn't get the value of button[6:]
at that instance and store it. Instead, it makes a closure , which is sort of like a note to itself saying " I should look for what the value of the variable button(the iterator) is at the time that I am called ".
Now when the loop is over and every widget is ready and set up, and you call it, it will look for the value of button at that time, which is ofcourse the last value of the iteration (here, button10
).
num=button[6:]
causes the function to store the current value of the counter(here button ) at the time your lambda is defined, instead of waiting to look up the value of button later.
Credits: BrenBarn
Just to add, you can do what you are doing right now in much less code using the command
attribute of Button
widget. Here is an example.
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
for number in range(1, 11):
tk.Button(self, height=1, width=4, bg="grey", text=number, command=lambda num=number: self.Callback(num)).pack(side='left')
def Callback(self, num):
print(num)
Window().mainloop()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.