By Jack


2011-08-03 00:18:14 8 Comments

Suppose I have the following Button made with Tkinter in Python:

import Tkinter as Tk
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command=action)

The method action is called when I press the button, but what if I wanted to pass some arguments to the method action?

I have tried with the following code:

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

This just invokes the method immediately, and pressing the button does nothing.

13 comments

@Rhett 2019-04-16 13:35:37

I am extremely late, but here is a very simple way of accomplishing it.

import tkinter as tk
def function1(param1, param2):
    print(str(param1) + str(param2))

var1 = "Hello "
var2 = "World!"
def function2():
    function1(var1, var2)

root = tk.Tk()

myButton = tk.Button(root, text="Button", command=function2)
root.mainloop()

You simply wrap the function you want to use in another function and call the second function on the button press.

@dominic thorpe 2018-12-22 11:42:51

The best thing to do is use lambda as follows:

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

@Christoph Frings 2018-06-08 13:53:41

Building on Matt Thompsons answer : a class can be made callable so it can be used instead of a function:

import tkinter as tk

class Callback:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self):
        self.func(*self.args, **self.kwargs)

def default_callback(t):
    print("Button '{}' pressed.".format(t))

root = tk.Tk()

buttons = ["A", "B", "C"]

for i, b in enumerate(buttons):
    tk.Button(root, text=b, command=Callback(default_callback, b)).grid(row=i, column=0)

tk.mainloop()

@Harrison Sills 2018-04-04 20:31:27

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

I believe should fix this

@Matt Thompson 2018-02-14 03:28:51

For posterity: you can also use classes to achieve something similar. For instance:

class Function_Wrapper():
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
    def func(self):
        return self.x + self.y + self.z # execute function

Button can then be simply created by:

instance1 = Function_Wrapper(x, y, z)
button1  = Button(master, text = "press", command = instance1.func)

This approach also allows you to change the function arguments by i.e. setting instance1.x = 3.

@Nae 2017-12-27 18:27:57

Example GUI:

Let's say I have the GUI:

import tkinter as tk

root = tk.Tk()

btn = tk.Button(root, text="Press")
btn.pack()

root.mainloop()

What Happens When a Button Is Pressed

See that when btn is pressed it calls its own function which is very similar to button_press_handle in the following example:

def button_press_handle(callback=None):
    if callback:
        callback() # Where exactly the method assigned to btn['command'] is being callled

with:

button_press_handle(btn['command'])

You can simply think that command option should be set as, the reference to the method we want to be called, similar to callback in button_press_handle.


Calling a Method(Callback) When the Button is Pressed

Without arguments

So if I wanted to print something when the button is pressed I would need to set:

btn['command'] = print # default to print is new line

Pay close attention to the lack of () with the print method which is omitted in the meaning that: "This is the method's name which I want you to call when pressed but don't call it just this very instant." However, I didn't pass any arguments for the print so it printed whatever it prints when called without arguments.

With Argument(s)

Now If I wanted to also pass arguments to the method I want to be called when the button is pressed I could make use of the anonymous functions, which can be created with lambda statement, in this case for print built-in method, like the following:

btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)

Calling Multiple Methods when the Button Is Pressed

Without Arguments

You can also achieve that using lambda statement but it is considered bad practice and thus I won't include it here. The good practice is to define a separate method, multiple_methods, that calls the methods wanted and then set it as the callback to the button press:

def multiple_methods():
    print("Vicariously") # the first inner callback
    print("I") # another inner callback

With Argument(s)

In order to pass argument(s) to method that calls other methods, again make use of lambda statement, but first:

def multiple_methods(*args, **kwargs):
    print(args[0]) # the first inner callback
    print(kwargs['opt1']) # another inner callback

and then set:

btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)

Returning Object(s) From the Callback

Also further note that callback can't really return because it's only called inside button_press_handle with callback() as opposed to return callback(). It does return but not anywhere outside that function. Thus you should rather modify object(s) that are accessible in the current scope.


Complete Example with global Object Modification(s)

Below example will call a method that changes btn's text each time the button is pressed:

import tkinter as tk

i = 0
def text_mod():
    global i, btn           # btn can be omitted but not sure if should be
    txt = ("Vicariously", "I", "live", "as", "the", "whole", "world", "dies")
    btn['text'] = txt[i]    # the global object that is modified
    i = (i + 1) % len(txt)  # another global object that gets modified

root = tk.Tk()

btn = tk.Button(root, text="My Button")
btn['command'] = text_mod

btn.pack(fill='both', expand=True)

root.mainloop()

Mirror

@Mitsakos 2019-04-20 17:15:10

Kudos for the Tool reference as well!

@Nae 2017-12-10 21:48:05

One simple way would be to configure button with lambda like the following syntax:

button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)

@David W. Beck 2017-11-10 10:09:16

JasonPy - a few things...

if you stick a button in a loop it will be created over and over and over again... which is probably not what you want. (maybe it is)...

The reason it always gets the last index is lambda events run when you click them - not when the program starts. I'm not sure 100% what you are doing but maybe try storing the value when it's made then call it later with the lambda button.

eg: (don't use this code, just an example)

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

then you can say....

button... command: lambda: value_store[1]

hope this helps!

@Jayce 2017-06-22 17:33:25

Use a lambda to pass the entry data to the command function if you have more actions to carry out, like this (I've tried to make it generic, so just adapt):

event1 = Entry(master)
button1 = Button(master, text="OK", command=lambda: test_event(event1.get()))

def test_event(event_text):
    if not event_text:
        print("Nothing entered")
    else:
        print(str(event_text))
        #  do stuff

This will pass the information in the event to the button function. There may be more Pythonesque ways of writing this, but it works for me.

@berna1111 2016-10-17 19:59:48

The reason it invokes the method immediately and pressing the button does nothing is that action(somenumber) is evaluated and its return value is attributed as the command for the button. So if action prints something to tell you it has run and returns None, you just run action to evaluate its return value and given None as the command for the button.

To have buttons to call functions with different arguments you can use global variables, although I can't recommend it:

import Tkinter as Tk

frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
frame.grid(row=2,column=2)
frame.pack(fill=Tk.X, padx=5, pady=5)
def action():
    global output
    global variable
    output.insert(Tk.END,variable.get())
button = Tk.Button(master=frame, text='press', command=action)
button.pack()
variable = Tk.Entry(master=frame)
variable.pack()
output = Tk.Text(master=frame)
output.pack()

if __name__ == '__main__':
    Tk.mainloop()

What I would do is make a class whose objects would contain every variable required and methods to change those as needed:

import Tkinter as Tk
class Window:
    def __init__(self):
        self.frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
        self.frame.grid(row=2,column=2)
        self.frame.pack(fill=Tk.X, padx=5, pady=5)

        self.button = Tk.Button(master=self.frame, text='press', command=self.action)
        self.button.pack()

        self.variable = Tk.Entry(master=self.frame)
        self.variable.pack()

        self.output = Tk.Text(master=self.frame)
        self.output.pack()

    def action(self):
        self.output.insert(Tk.END,self.variable.get())

if __name__ == '__main__':
    window = Window()
    Tk.mainloop()

@Voo 2011-08-03 03:07:50

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))

@agf 2011-08-03 20:29:18

You're still writing wrapper methods, you're just doing it inline.

@Voo 2011-08-03 22:13:06

Sure but I hoped it was obvious from context how that was meant. Also the advantages still stand: Shorter and you can more easily use it on methods you don't have direct control over, or still want to be able to simply call directly. I find the inner method variant only useful for more complex scenarios (eg for decorators; although there a class is imo clearer too)

@eyquem 2013-03-01 00:05:42

@Voo What is the non-wrapper solution to which agf seems to allude to, please ?

@Daniel 2014-06-25 15:02:27

there is no non-wrapper solution.

@Scrontch 2014-07-18 13:46:15

This doesn't work if someNumber is in fact a variable that changes values inside a loop that creates many buttons. Then each button will call action() with the last value that has been assigned to someNumber and not the value it had when the button was created. The solution using partial works in this case.

@Voo 2014-07-18 14:10:44

@Scrontch Well yeah obviously that's how variable scoping works in python and pretty much every other language (so it works and if you think about it, it's the only logical way to do things). Several solutions, partial certainly works although old school python people will probably stick to the default parameter assignment for the lambda.

@Tommy 2014-07-20 16:56:30

This worked great for me. However, can you also explain why the OPs statement of "This just invokes the method immediately, and pressing the button does nothing" happens?

@WeRelic 2014-08-26 23:09:35

OT: Went out of my way to upvote this, thank you. @Tommy, Just taking a guess, but I think it is because Python handles functions in two stages. callables and actual calls(still new so I don't know the terms). By passing args to a callable, you take the arg from being a reference to a type object to being a call to that object with the specified parameters.

@gboffi 2014-11-10 00:34:58

@Scrontch I wonder how many novice Tkinter users never felt in the trap you mentioned! At any rate one can capture the current value using the idiom callback=lambda x=x: f(x) as in fs = [lambda x=x: x*2 for x in range(5)] ; print [f() for f in fs]

@Klamer Schutte 2015-02-18 22:14:51

@Voo what do you mean above with "although old school python people will probably stick to the default parameter assignment for the lambda"? I did not get lambda to work and thus now use partial.

@erm3nda 2015-08-21 19:41:03

My personal case is that command=something() does action even if no click is made on button. So strange behavior. Using lambda function constructor is so much better, also allows you to extend the function and does what i expect, run on click :P

@Voo 2015-08-22 06:56:55

@erm3nda Leave the parens off - you don't want to call that function.

@erm3nda 2015-08-22 07:14:01

@Voo: Ask Python devs to remove it from the Python functions and you will know why is in there. Lambda is nothing new, double iteration over a function, literally function constructor. Use it with care ;)

@Voo 2015-08-22 18:01:13

@erm3nda I have no idea what you're trying to tell me. Your parameter should be command=something without the parens otherwise you assign the result of your function to the parameter which you almost certainly don't want.

@erm3nda 2015-08-22 18:08:24

I got the same problem than the asker. Even if in theory it should work, doesn't. Every function defined into a button does fire at load, and not when you press button. Did u try in your machine that piece of code?

@z33k 2018-01-20 00:15:10

More on the issue pointed by @Scrontch

@elig 2019-01-14 13:59:00

This trick actually works, but sadly not with .bind() ; @erm3nda It shouldn't work in "theory". That's how Python is supposed to work. You put a function name with '()' - it gets called immediately !

@Voo 2019-01-14 17:47:51

@elig Not if you put a lambda: in front of it (that's how you designate an anonymous function that takes no arguments and returns the result of the function call), which is why the shown code works just fine.

@MarrekNožka 2013-03-19 20:49:12

Python's ability to provide default values for function arguments gives us a way out.

def fce(x=myX, y=myY):
    myFunction(x,y)
button = Tk.Button(mainWin, text='press', command=fce)

See: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

For more buttons you can create a function which returns a function:

def fce(myX, myY):
    def wrapper(x=myX, y=myY):
        pass
        pass
        pass
        return x+y
    return wrapper

button1 = Tk.Button(mainWin, text='press 1', command=fce(1,2))
button2 = Tk.Button(mainWin, text='press 2', command=fce(3,4))
button3 = Tk.Button(mainWin, text='press 3', command=fce(9,8))

@Bryan Oakley 2013-03-19 23:09:22

This does not solve the problem. What if you are creating three buttons that all call the same function but need to pass different arguments?

@MarrekNožka 2014-12-03 21:41:15

You can create a function Which returns a function.

@Tadhg McDonald-Jensen 2016-02-25 19:42:59

I know this isn't active any more but I linked to here from stackoverflow.com/questions/35616411/…, this works the exact same way as using lambda expressions, you can define a function for each button in the same way as making a lambda expression for each button.

@Tadhg McDonald-Jensen 2016-02-25 19:51:50

putting the first code example in a loop that keeps changing myX and myY works perfectly thank you very much.

@Dologan 2014-03-10 00:57:19

This can also be done by using partial from the standard library functools, like this:

from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)

@Klamer Schutte 2015-02-18 22:11:39

Or even shorter: button = Tk.Button(master=frame, text='press', command=partial(action, arg))

Related Questions

Sponsored Content

56 Answered Questions

[SOLVED] Calling an external command in Python

31 Answered Questions

[SOLVED] How do I parse command line arguments in Bash?

28 Answered Questions

[SOLVED] How to create an HTML button that acts like a link?

44 Answered Questions

[SOLVED] How to merge two dictionaries in a single expression?

32 Answered Questions

[SOLVED] "Least Astonishment" and the Mutable Default Argument

42 Answered Questions

[SOLVED] How do I check whether a file exists without exceptions?

29 Answered Questions

16 Answered Questions

[SOLVED] How can I pass arguments to a batch file?

24 Answered Questions

[SOLVED] How do I pass a variable by reference?

14 Answered Questions

[SOLVED] <button> vs. <input type="button" />. Which to use?

Sponsored Content