How to Pass Arguments to a Button Command in Tkinter

How to pass arguments to a Button command in Tkinter?

I personally prefer to use lambdas in such a scenario, because imo it's clearer and simpler and also doesn't force you to write lots of wrapper methods if you don't have control over the called method, but that's certainly a matter of taste.

That's how you'd do it with a lambda (note there's also some implementation of currying in the functional module, so you can use that too):

button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))

How can I pass arguments to Tkinter button's callback command?

This should work:

...
btnaText='ButtonA'
btna = Button(root, text = btnaText, command = lambda: sayHi(btnaText))
btna.pack()

For more information take a look at Tkinter Callbacks

Pass button text to command as arg with tkinter button

You can use default value of argument on the lambda:

for folder in os.listdir("Servers"):
btns.append(
tk.Button(
master,
text=folder,
command=lambda f=folder: handleButton(f)
)
)

How to pass a variable to a function via a Button?

The common way to do it when working with tkinter is to use lambda functions. The tricky part is getting the return value back because the return value of command= functions is ignored. Folks often use global variables to workaround that, but that tactic won't work if the function is defined in another module. Fortunately tkinter has several type-specific container Variable subclasses that can be passed as arguments to such functions that will permit values to be retrieved from them.

Here's runnable code showing one way of using them to do this:

script.py

from tkinter import filedialog

def load_file(input_path):
file_name = filedialog.askopenfilename(filetypes=[('EXE files', '*.exe')])
input_path.set(file_name)

def simulation(input_path, input_par): # this works fine
# the function runs an EXE file ang gives me two lists lst1 and lst2
return lst1, lst2

main.py

from script import load_file, simulation
import tkinter as tk

root = tk.Tk()
frame_file = tk.Frame(root)
frame_file.pack()

input_path = tk.StringVar()
input_var = 42
button = tk.Button(frame_file, text='Open file', command=lambda: load_file(input_path))
button.grid(row=1, column=1)
button2 = tk.Button(frame_file, text='Run simulation',
command=lambda: simulation(input_path.get(), input_var))
button2.grid(row=1, column=2)

root.mainloop()

Update

Here's a slightly more complicated but much more flexible version, that would work with many more types of return values, including user-defined classes (not just the four tkinter supports — bool, float, int, string). Supporting more types of returned data automatically, also means that called functions don't need to be modified.

It does this by defining a CallBack class whose instances "wrap" the functions to be called and save their return values in a tkinter StringVar widget so they can be retrieved later as needed by calling a method get_result() that instances of the class have. To work with more datatypes it uses Python's built-in pickle module to temporarily store the data.

script2.py

from tkinter import filedialog

def load_file():
file_name = filedialog.askopenfilename(filetypes=[('EXE files', '*.exe')])
return file_name

def simulation(input_path, input_par): # this works fine
# the function runs an EXE file and gives me two lists lst1 and lst2
print(f'simulation({input_path=}, {input_par=}) called')
list1, list2 = ['param', input_par], list('spam')
return list1, list2

main2.py

from script2 import load_file, simulation
import pickle
import tkinter as tk
from tkinter import messagebox


class CallBack:
def __init__(self, func):
self.func = func
self.var = tk.StringVar()
def get_result(self):
pickled_result = self.var.get()
return pickle.loads(pickled_result.encode('latin1')) # Unpickle and return it.
def __call__(self):
res = self.func()
pickled_result = pickle.dumps(res).decode('latin1') # Pickle result.
self.var.set(pickled_result) # And save it in tkinter StringVar.


root = tk.Tk()
frame_file = tk.Frame(root)
frame_file.pack()

input_par = 42
callback1 = CallBack(load_file) # Wrap function from other module.

button = tk.Button(frame_file, text='Open file', command=callback1)
button.grid(row=1, column=1)

def run_sim():
""" Run simulation function and display value returned. """
# Wrap other function in other module to allow results to be retrieved.
callback2 = CallBack(lambda: simulation(callback1.get_result(), input_par))
callback2() # Call the function and save its return value.
results = callback2.get_result() # Get function's return value.
messagebox.showinfo(title='Simulation Results', message=str(results))

button2 = tk.Button(frame_file, text='Run simulation', command=run_sim)
button2.grid(row=1, column=2)

root.mainloop()

Tkinter : How to pass a button's text as an argument to a function when button is clicked

Just change your button to:

redbutton = Button(root, text = num, fg ='red',height = 3, width = 5,command=lambda num=num: numberClick(num))

This should fix the issue, this will store the value of num in lambda rather than just looping and using the last value of num.

Tkinter button command argument

Ok there is a lot going on here so I have a couple of things that I think will help you.

You need to move your def out of the while loop for all the functions. They should be defined only once in the beginning of the file.

Also, you are assigning variables to the Button object after you call the grid method. That method returns None so you shouldn't do that because you are assigning variables None instead of the actual button object as you intend to. You should assign the variables the button object alone and then call varname.grid() later.

Finally, to address your question: when you write command=beer() you are once again calling the function beer and assigning its return value to the command parameter. When you are using Tkinter you must assign only the function name to the command parameter such as command=beer. However, if you have to pass it arguments you can use lambda. For example: command=lambda: beer(arg1, arg2).

P.S.
When comparing strings you should say

if selection1 == "Beer":

not

if selection1 is "Beer":

is tests for identity not equality and you want to test equality.

EDIT: You also should unindent the try at the top of your file.

Also because selection1 is a local variable in the function beer it won't work, you need to declare it a global

def beer():
global selection1
selection.append('Beer')
selection1 = 'Beer'

Furthermore, you need to destroy the window or the if statement in the while loop won't run.

def beer(window):
global selection1
selection.append('Beer')
selection1 = 'Beer'
window.destroy()

and then you need to pass the welcomeGUI Tk instance to the function like so

beerButton = Button(welcomeGUI, text='Beer', font='Times 16 bold',command=lambda: beer(welcomeGUI)).grid(row=6,column=1)

One last thing. I would remove the while loop all together and have a button on the beer window to call back the main welcome window because putting two mainloops in the while loop is not going to be a good thing.

How to pass two or more arguments to a Button command?

The lambda in your question should work:

tk.Button(self.board, command=lambda i=i, j=j: self.on_click(i,j))

This binds the value i and j as the default values for the lambda parameters.

Personally I prefer lambda over partial, mainly because I don't have to import the functools module. If you wish to use partial, it should look like your first example:

tk.Button(self, command=partial(self.on_click, i, j))

In both cases, on_click will be passed the correct value for i and j based on their values when the button is created.

Here is an example based on your code, but with some unnecessary code removed for clarity:

import tkinter as tk
from functools import partial


class Board(tk.Frame):
def __init__(self, parent, method):
tk.Frame.__init__(self, parent, bd=2, relief="sunken")
for i in range(10):
for j in range(10):
if method == "lambda":
button = tk.Button(self, command=lambda i=i, j=j: self.on_click(i,j))
else:
button = tk.Button(self, command=partial(self.on_click, i, j))
button.grid(row=i, column=j)

def on_click(self, i, j):
print("X: {}, Y:{}".format(j, i))

root = tk.Tk()
board1 = Board(root, method="lambda")
board2 = Board(root, method="partial")
board1.pack(side="top", fill="both", expand=True)
board2.pack(side="top", fill="both", expand=True)
root.mainloop()


Related Topics



Leave a reply



Submit