Tkinter assign button command in a for loop with lambda
The problem is your lambda
expression in the for
loop. It is using the name
variable, but the name
variable gets reassigned each iteration of the for
loop, so in the end, all of the buttons get the last value that name
was assigned to in the for loop. To avoid this you can use default keyword parameters in your lambda
expression like so:
user_button = Tkinter.Button(self.root, text=name,
command=lambda name=name: self.a(name))
This binds the current value of the name
variable to the lambda's name
keyword argument each time through the loop, producing the desired effect.
tkinter button created in loop command not working
Change the lambda to:lambda i=i, j=j: show(i, j)
This will capture the values of i and j at the time the lambda is created. What's happening instead is it is looking up i and j in the scope of the lambda, but at the time the button is clicked, by which time the loop in the list comprehension is complete and i and j are both 8.
Tkinter Button Commands in For Loop
It is my understanding that e.g. i
in a lambda
within a loop like this is referring to the variable i
itself, not the value of the variable on each iteration, so that when the command
callback is called, it will use the value of i
at that moment, which as you noticed is the value on the last iteration.
One way to solve this is with a partial
. partial
will, in effect "freeze" the arguments at their current state in the loop and use those when calling the callback.
Try using a partial
instead of a lambda
like this:
from functools import partial
buttonDictionary = {}
for i in range(0,len(currentVisitors)):
buttonDictionary[i] = Button(bottomFrame, text=currentVisitors[i], command=partial(signOut, topFrame, bottomFrame, currentVisitors[i]))
buttonDictionary[i].pack()
Another way I have seen this done, but haven't tried, is to assign i
to a new variable in your lambda
each time:
command=lambda i=i: signOut(topFrame, bottomFrame, currentVisitors[i])
I have gotten burned bad more than once when I first started with Python by using lambdas in loops (including a case very similar to this trying to assign a callback to dynamically generated buttons in a loop). I even created a snippet that would expand to think_about_it('are you sure you want to use a lambda?')
whenever I typed lambda
just to remind me of the pain I caused myself with that...
tkinter button different command for loop in dictionary not working
Replace
self.widgetdata[i] = Button(self, text=i, command=lambda: self.edit(i))
by
self.widgetdata[i] = Button(self, text=i, command=lambda i=i: self.edit(i))
Explanation: the body of the lambda function is executed when clicking the Button, so it uses the current value of i
at execution time (i.e. the index of the last created Button), not at definition time. Creating an aliased argument, forces the creation of a local variable for each loop step, each having a different value, so refering to a different Button.
Creating Tkinter buttons in a loop and setting the command for each button to take in the indices of the loop
You need to create a local variable in lambda
when using a loop
button = Button(root, text="0", height=2, width=10, command=lambda x_p=i,y_p=j: self.if_clicked_square(x_p,y_p))
button.place(x=25 + (100 * j), y=100 + (100 * i))
how to give different command to buttons created in a loop in tkinter?
Lambdas don't immediately bind the values they use. They are closures and look up the values needed when invoked. What this means is that it will only act on the last quick_button
since that is what quick_button
equals after your loop is finished. On top of that, you are trying to reference quick_button
before it exists!
What if I told you that you could exploit this late binding behavior which is causing your problem to actually help solve your problem?
We can store a list of your buttons (buttons
) and store the index of the button as a local variable within your lambda. You will then use that index to access the button that was pressed and pass that to your press_button
function.
It would look something like this:
import tkinter
def press_button(button):
button.configure(bg="red")
root = tkinter.Tk()
buttons = [] # <-- Added list of buttons
for i in range(4):
quick_button = tkinter.Button(root, text=str(i) ,font=("courier", 30),
command=lambda i=i: press_button(buttons[i]))
quick_button.grid(row=i, pady=3, padx=3)
buttons.append(quick_button)
root.mainloop()
Notice the lambda i=i
part. Without the i=i
, you would experience the same behavior as you are experiencing now... When the lambda is called, it will look up the value of i
and see that it's 3
no matter what button you press. Adding the i=i
to the lambda binds it locally within the lambda.
Now, each button behaves "independently" from the rest:
Using lambda function in command of Button widget in tkinter
The problem with the code you sent is that the value of j
is changing with the loop, so as the loop ends, all of your buttons and lables take the value of j as 3 (thats because when your loop ends, j has the value "3") so that means all of your lables and buttons are using the last label.
An easy fix would be to manually set label[j]
and entry[j]
to some other variable, then apply the command.
Something like this :lambda x=label[j], y=entry[j]: x.set(y.get())
Here I first set label[j]
to x
and entry[j]
to y
and then change the values inside lambda.
import tkinter as tk
main = tk.Tk()
label = [None]*3
entry = [None]*3
read = [None]*3
for j in range(3):
label[j] = tk.StringVar()
tk.Label(main, textvariable = label[j], relief = 'raised', width = 7).place(x = 5, y = 40+30*j)
entry[j] = tk.Entry(main, width=8)
entry[j].place(x=80, y=40 + 30 * j)
read[j] = tk.Button(main, text="READ", pady=0, padx=10, command= lambda x=label[j], y=entry[j]: x.set(y.get()))
read[j].place(x=150, y=40 + 30 * j)
main.mainloop()
Related Topics
Why Are Multiple Instances of Tk Discouraged
Import Multiple CSV Files into Pandas and Concatenate into One Dataframe
How to Get Posted Json in Flask
List Comprehension Without [ ] in Python
What Happens When Using Mutual or Circular (Cyclic) Imports in Python
What Is the Purpose of the Return Statement? How Is It Different from Printing
How to Concatenate Two Lists in Python
Best Way to Strip Punctuation from a String
If/Else in a List Comprehension
How to Make a Class Json Serializable
Renaming Column Names in Pandas
Why Can't Python Parse This Json Data
How to Replace Multiple Substrings of a String
A Weighted Version of Random.Choice