This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
How can I identify buttons, created in a loop?
(6 answers)
Closed 4 years ago.
I'm having a problem where I create a for loop of buttons and I want each one of other to have different column, row and text.
I noticed only the text and the column (because i used lambda on it) are actually the updated values given to the function.
(btw the row and column are for something else, not the button - in a different function).
This is my code, I can't seem to understand how to get the most updated value for a button since all of my buttons are getting the values of the first button except for the text name and their column
for order in self.orders:
if counter2 >= 2 and counter2 % 2 == 0:
x += 1
print x, counter2
bt = Button(window, command=lambda i=counter2: self.display_order(self.orders[order], window, x, i))
bt.configure(text='order ' + str(counter2+1), fg='black', bg='steel blue', width=20)
bt.grid(sticky='W', row=0, column=counter2, columnspan=1) # increase row number for every button
counter2 += 1
x has also a problem with late binding. You've done it right for counter2. Use the following change (by adding x=x as additional lambda argument):
bt = Button(window, command=lambda i=counter2, x=x: self.display_order(self.orders[order], window, x, i))
Related
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 1 year ago.
I want to create multiple clickable labels in a for-loop. The labels are structured grid-like with a row and col attribute. If I click the label, the row and col of the clicked label should be printed with the print_it() function.
The problem is if I click any label, the output is always the last defined row/col (2,2) in this case. How can I fix it, so the correct row/col gets printed?
Here is a code example to reproduce:
from tkinter import *
def print_it(row, col):
print(row, col)
root = Tk()
sizex = 700
sizey = 400
posx = 0
posy = 0
root.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))
canvas_area = Canvas(root)
canvas_area.grid(row=1, column=1)
labels = []
for row in range(3):
for col in range(3):
labels.append(Label(canvas_area, text=f"Row, Col: {row},{col}", bd=0))
labels[-1].grid(row=row, column=col)
labels[-1].bind(
f"<Button-1>",
lambda e: print_it(row, col),
)
root.mainloop()
I actually found another post which let me solve this problem: Python Tkinter: Bind function to list of variables in a for-loop
The correct definition of the bind function is this:
labels[-1].bind(
"<Button-1>",
lambda event, row=row, col=col: print_it(row, col),
)
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 1 year ago.
def run_game(self):
root = Tk()
for i in range(0, 6):
for j in range(0, len(self.board[i])):
button = Button(root, text="0", height=2, width=10, command=lambda: self.if_clicked_square(i,j))
button.place(x=25 + (100 * j), y=100 + (100 * i))
# button.pack()
self.buttonsList[i].append([button])
root.mainloop()
The above function is creating a GUI board consisting of 28 buttons using Tkinter. I used nested for loops to create every button and place it on the board and also in a list of buttons. The problem arises when setting the command for each button. I want to use the function if_clicked_square(i,j)for the command. The function uses the i and j indices to operate. The problem is that the command for every single button after the loop is done is if_clicked_square(5,2), which happen to be the last indices of the loop. I want every button to have a unique command based on the indices of the for loop. For example, one of the buttons should have the command if_clicked_square(0,0), one should have the command if_clicked_square(1,0), and so forth for all 28 buttons. Can I get any tips to accomplish this?
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))
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 months ago.
I need to create multiple buttons with different names (each new name is equal to the name of the previous button + the iterating value at that moment.) Please help me out, here is my code.
buttons = [0]*len(gg.allStudents)
for j in range(len(gg.allStudents)):
buttons[j] = tk.Button(wind, text=gg.allStudents[j].name, height = 2, width = 20, command=lambda: plotMarks(j))
buttons[j].pack()
The looping conditions that I have used our correct. The only help I need is to find a way to store each new button with a new name into the 'buttons' list.
Your issue is not what you think it is. It can be easily solved by changing:
command=lambda: plotMarks(j)
to
command=lambda j=j: plotMarks(j).
The reason this works is because, in your version, you are sticking the variable j in all of your commands, and they will all use whatever the final value of j is. In the second version you are sticking the current value of j in your command.
To understand this better all we have to do is expand the lambdas.
def add2(n):
return n+2
#equivalent of your current version
j = 6
def currentLambdaEquivalent():
global j
print(add2(j))
currentLambdaEquivalent() #8
#equivalent of second version
def alternateLambdaEquivalent(j):
print(add2(j))
alternateLambdaEquivalent(2) #4
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 5 years ago.
I'm unsure where I'm going wrong with the below - I'm sure it's something basic but I'm still a bit unsure what the issue is. I'm trying to have a button change the range of the car when clicked, but it will only set the last one.
def draw:
car_column = 0
for car in boarding_cars:
tk.Label(transport_frame, text="{}".format(car.name)).grid(row=0, column=car_column)
tk.Button(transport_frame, text=car.range, command= lambda: change_range(car)).grid(row=1, column=car_column)
car_column += 1
def change_range(car):
print car.name
if car.range == "mid":
car.range = "close"
elif car.range == "close:
car.range = "mid"
I understand it's just setting everything as the last one in the list, but I'm unsure how to stop it doing that. Any help would be appreciated.
This is a common problem when people don't understand that lambda is late-binding. You need to use partial instead for this.
from functools import partial
tk.Button(transport_frame, text=car.range, command= partial(change_range, car)).grid(row=1, column=car_column)
This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 9 years ago.
I am having some trouble with the button widgets.
Here's my code. Basically, what I would like to do is to print "0,0" when I press the first button, print "0,1" when I press the second button and so on. But what happens is that it always prints "1,1", which are last values in my for loops. How could I fix that?
Thanks a lot for helping
from tkinter import *
def show(x,y):
print(x,y)
root = Tk()
a = 0
for i in range(2):
for j in range(2):
Button(root, text=a, command=lambda:show(i,j)).grid(column=j, row=i)
a += 1
root.mainloop()
It is because of the closure property of Python. To fix this, change
Button(root, text=a, command=lambda:show(i,j)).grid(column=j, row=i)
to
def inner(i=i, j=j):
Button(root, text=a, command=lambda:show(i,j)).grid(column=j, row=i)
inner()
We are just wrapping the values of i and j with a function. Since the function show is not executed immediately, it just accepts the values of the two variables i and j. So, all the buttons now, will have the same i and j and they will get the values which are at the end of the loop.
Now, by defining a new function, we are getting a default parameter of the same names i and j and we get the values of i and j at the particular moment. So, the current values will be retained.