Tkinter Button Command switches when new Button is used [duplicate] - python

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed last year.
I face an issue which is that my first button is using the second button's command. I have faced this logic error multiple times when trying to create buttons programmatically with different functions, is there a way to resolve this or is this a limitation to Tkinter? The gif below illustrates my issue.
import tkinter as tk
root = tk.Tk()
root.geometry("400x400")
def print_when_clicked(message):
print(message)
array = ["hi", "bye"]
for i in array:
tk.Button(root, text=i, command=lambda:print_when_clicked(i)).pack()

You have fallen in to one of the classic Python traps. The issue is that the lambda captures the i variable, NOT the value of i. So, after the loop finishes, i is bound to "bye", and both functions use it.
The trick to work around this is to use a fake default argument:
for i in array:
tk.Button(root, text=i, command=lambda i=i:print_when_clicked(i)).pack()

Related

Tkinter buttons calling functions without pressing them [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed last year.
I've had this problem for a while. When I start a program, any tkinter buttons will call their functions without being pressed or anything. and after that, I cannot use the button anymore because it only works once for some reason. Can anybody help?
This is not the actual code, but it is an example:
from tkinter import *
root = Tk()
def function():
print("activated")
button = Button(root, text="this is a button", command=function)
button.pack()
root.mainloop()
Apparently adding parentheses at the end of the command inside the button code was the problem. credit to #jasonharper for this one.

Python gui button (tkinter) function is being run when program is executed and not onclick [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 2 years ago.
i'm currently learning python/tkinter and I have two buttons working correctly but my final button is assigned to a function, which is supposed to run on click but it instead runs when the program is executed and then will not run when the button is clicked. I have looked at a couple of other threads and cannot work out why mine wont work.
Thanks a lot
def onclickSelectSheet():
test()
buttonSelectSheet = Button(topFrame, text="Select an information sheet", command=onclickSelectSheet(), bg = '#CDCDCD')
buttonSelectSheet.place(relx=0.2, rely=0.1, relwidth=0.6, relheight=0.4)
To execute a Button-command on click, you need to set the command option as follows:
Button(..., command=lambda: onclickSelectSheet(),...) # with parenthesis
OR
Button(..., command=onclickSelectSheet,...) # without parenthesis
Using parenthesis () without lambda executes the function as soon as the Button widget is defined

How do I pass a index of a list to a callback of a button? [duplicate]

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 years ago.
I am writing a GUI using tkinter, and I have several lines consisting of a Entry widget inline with a Button widget. When I press the button, the callback opens the askdirectory dialog, and the directory choosen is written in the Entry widget, from the same line as the button clicked.
To get to a more elegant solution than just copy/paste 12 times the code, I wrote the following:
for i in range (0,12):
e[i] = Entry(master)
e[i].insert(0, "<Choose a directory...>")
e[i].config(width=70)
e[i].grid(row=i+2, column=0, sticky=NW, columnspan=2)
b[i] = Button(master, text="Choose directory", command=lambda: self.getDir(e[i]))
b[i].grid(row=i+2, column=2, sticky=N)
And the callback function:
def getDir(self, e):
'''Retrieve directory and write it on the right "Entry" widget'''
dirName = askdirectory(title='Choose a directory:')
e.delete(0, END)
e.insert(0, dirName)
When I run the script, I get the "list index out of range" error.
From my understanding, it is because i - which is used later on the code -, is not the index anymore, only in the moment I am building the buttons, right?
So, what should I do to make the callback understand which Button was clicked and to which Entry it should write?
I can only guess since you didn't post the error message:
could simply be the lenght of e and b! e and b need to be lists or tuples with at least the lenght of 12...or if you just want to process your code 12 times you dont need the [i] at all.
I would have posted this as a comment but I don't have enough points to do so :(

Tkinter Function attached to Button executed immediately [duplicate]

This question already has answers here:
How can I pass arguments to Tkinter button's callback command?
(2 answers)
Closed 6 years ago.
What I need is to attach a function to a button that is called with a parameter. When I write the code as below however, the code is executed once when the button is created and then no more. Also, the code works fine if I get rid of the parameter and parentheses when I declare the function as an attribute of the button. How can I call the function with a parameter only when the button is pressed?
from Tkinter import *
root =Tk()
def function(parameter):
print parameter
button = Button(root, text="Button", function=function('Test'))
button.pack()
root.mainloop()
The solution is to pass the function as a lambda:
from Tkinter import *
root =Tk()
def callback(parameter):
print parameter
button = Button(root, text="Button", command=lambda: callback(1))
button.pack()
root.mainloop()
Also, as #nbro already correctly pointed out, the button attribute is command, not function.

How to determine which button is pressed out of Button grid in Python TKinter? [duplicate]

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 months ago.
I am writing a GUI with Python TKinter where I have a grid of some 24 buttons which I have created using a loop (not individually). Is there any way in which I can get the Text of the button that I pressed.
Since it is in loop, a callback function even with lambda is not helping me. I don't wish to write separate code for, what happens when each different button is pressed. I just need to know the text of the corresponding button so that I could initiate another generic function which works with that text only.
ps: I am able to do the same task but with List and curselection() and don't want it this way.
self.num = 11
for r in range(0,5):
for c in range(0,3):
R = r; C = c
resNum = "Ch0"+str(self.num);
self.button1_rex = tk.Button(self.frame, text = resNum,font=("Helvetica", 14), justify = CENTER,width = 20, command = self.new_window)
self.button1_rex.grid(row=R, column=C, sticky=W)
self.num = self.num+1
self.new_window is the function that opens a new window and needs to do other functionalities based on the button number (like "Ch011" etc)
The simplest way is just to, when you are constructing the button, bind the name to the command, either using functools.partial or a lambda.
Using functools.partial:
self.button1_rex = tk.Button(..., command=functools.partial(self.new_window, resNum))
Using a lambda:
self.button1_rex = tk.Button(..., command=lambda r=resNum: self.new_window(r))
For more infomation about the lambda, see What is a lambda (function)? and Python tkinter creating buttons ... arguments.

Categories