Categories
python tkinter

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

97

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.

1

  • 11

    @Mike-SMT That’s exactly why. I want to reward people for posting good answers to common questions – especially if the questions are easy. Many people post half-baked, unmotivated answers to easy questions. I want people to realize that you don’t have to be a programming expert to write outstanding answers.

    – Aran-Fey

    Oct 27, 2018 at 7:20

15

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.

    13

    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 (a 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