Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the command bound to a Button or event executed when declared?

My code is:

from Tkinter import *

admin = Tk()
def button(an):
    print(an)
    print('het')

b = Button(admin, text='as', command=button('hey'))
b.pack()
mainloop()

The button doesn't work, it prints 'hey' and 'het' once without my command, and then, when I press the button nothing happens.

like image 203
salk Avatar asked Apr 23 '11 21:04

salk


2 Answers

Consider this code:

b = Button(admin, text='as', command=button('hey'))

It does exactly the same as this:

result = button('hey')
b = button(admin, text='as', command=result)

Likewise, if you create a binding like this:

listbox.bind("<<ListboxSelect>>", some_function())

... it's the same as this:

result = some_function()
listbox.bind("<<ListboxSelect>>", result)

The command option takes a reference to a function, which is a fancy way of saying you need to pass it the name of the function. To pass a reference you must use the name only, without using parenthesis or arguments. For example:

b = Button(... command = button)

If you want to pass a parameter such as "hey" you must use a little extra code:

  • You can create an intermediate function that can be called without your argument and which then calls your button function,
  • You can use lambda to create what is referred to as an anonymous function. In every way it's a function except it doesn't have a name. When you call the lambda command it returns a reference to the created function, which means it can be used for the value of the command option to the button.
  • You can use functools.partial

For me, lambda is the simplest since it doesn't require any additional imports like functools.partial does, though some people think that functools.partial is easier to understand.

To create a lambda function that calls your button function with an argument you would do something like this:

lambda: button('hey')

You end up with a function that is functionally equivalent to:

def some_name():
    return button('hey')

As I said earlier, lambda returns a reference to this nameless function. Since a reference is what the command option expects you can use lambda directly in the creation of the button:

b = Button(... command = lambda: button('hey'))

There's a question on this site that has a lot of interesting comments about lambda, in general. See the question Why Python lambdas are useful?. That same discussion has an answer that shows how to use lambdas in a loop when you need to pass in a variable to the callback.

Finally, see the zone.effbot.org article titled Tkinter Callbacks for a nice tutorial. The coverage of lambda is pretty lean, but the information there might still be useful.

like image 196
Bryan Oakley Avatar answered Nov 02 '22 14:11

Bryan Oakley


You need to create a function without parameters that you can use as the command:

b = Button(admin, text='as', command=lambda: button('hey'))

See the "Passing Argument to Callbacks" section of this document.

like image 16
Lukáš Lalinský Avatar answered Nov 02 '22 15:11

Lukáš Lalinský