Hi!

The following Tkinter buttons should call opt_def with opt as 1 or 2 depending on which button is pressed (see code).

from Tkinter import *

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        self.opt1 = Button(frame, text="Opt1", command=self.opt_def(1))
        self.opt1.pack(side=TOP)

        self.opt2 = Button(frame, text="Opt2", command=self.opt_def(2))
        self.opt2.pack(side=BOTTOM)

    def opt_def(self, opt):
        print 'You have choosen option', opt
        

root = Tk()

app = App(root)

root.mainloop()

But when the code is ran (no Tkinter buttons pressed) it returns this:

>>> 
You have choosen option number 1
You have choosen option number 2

Why does this happens and how do I fix it? Sorry about my spelling btw, I'm not american

Dani AI

Generated

You are seeing the prints at startup because command=self.opt_def(1) calls the function immediately while the widget is being created. The command option must be a zero-argument callable that Tkinter can invoke later. Both ’s pass-through methods and ’s lambda wrapper fix this by deferring the call until the button is clicked.

Another concise option is functools.partial, which builds a callable that already has arguments bound:

from functools import partial

self.opt1 = Button(frame, text="Opt1", command=partial(self.opt_def, 1))
self.opt2 = Button(frame, text="Opt2", command=partial(self.opt_def, 2))

This stays readable as the number of parameters grows and works with keyword args too (e.g., partial(self.opt_def, opt=1, verbose=True)).

One common gotcha when generating many buttons in a loop is Python’s late binding of closures. If you do not capture the current loop variable, every button ends up passing the last value. Use a default argument (or partial) to freeze the value at each iteration:

for i in range(1, 4):
    btn = Button(frame, text="Opt%s" % i, command=lambda i=i: self.opt_def(i))
    btn.pack()

If you need access to the event object (mouse position, which widget, modifiers), prefer .bind as showed; command= callbacks do not receive an event. Finally, in Python 3 the module is tkinter and print() is a function, but the callback patterns above are the same.

Recommended Answers

All 4 Replies

The easiest way to do this is with a pass-through function.

from Tkinter import *
 
class App:
 
    def __init__(self, master):
 
        frame = Frame(master)
        frame.pack()
 
        self.opt1 = Button(frame, text="Opt1", command=self.opt_1)
        self.opt1.pack(side=TOP)
 
        self.opt2 = Button(frame, text="Opt2", command=self.opt_2)
        self.opt2.pack(side=BOTTOM)
 
    def opt_def(self, opt):
        print 'You have choosen option', opt
 
    def opt_1(self):
        self.opt_def(1)

    def opt_2(self):
        self.opt_def(2)

root = Tk()
 
app = App(root)
 
root.mainloop()

Another way is to use the event:

# Tkinter, show the button that has been clicked
# using the bind event

import Tkinter as tk

def click(event):
    """
    event.widget.cget("text") gets the label
    of the button clicked
    """
    s = "clicked: " + event.widget.cget("text")
    root.title(s)

root = tk.Tk()
root.geometry("300x50+30+30")

b1 = tk.Button(root, text="button1")
b1.bind("<Button-1>", click)
b1.pack()

b2 = tk.Button(root, text="button2")
b2.bind("<Button-1>", click)
b2.pack()

root.mainloop()

Actually simpler:

# Tkinter, show the button that has been clicked
# using lambda

from Tkinter import *

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        cmd1 = lambda: self.opt_def(1)
        self.opt1 = Button(frame, text="Opt1", command=cmd1)
        self.opt1.pack(side=TOP)

        cmd2 = lambda: self.opt_def(2)
        self.opt2 = Button(frame, text="Opt2", command=cmd2)
        self.opt2.pack(side=BOTTOM)

    def opt_def(self, opt):
        print 'You have choosen option', opt


root = Tk()

app = App(root)

root.mainloop()

Thanks for the replies. I've used lambda.

self.opt2 = Button(frame, text="Opt2", command=lambda: self.function(arguments))

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.