Tkinter expanded event handler

vegaseat 1 Tallied Votes 1K Views Share

If you bind a Tkinter widget to an event, normally only the event information is passed to the function responding to the specific event like a mouse click. Python allows you to expand the event handler to include any number of values to be passed on. Here is a typical example ...

# expanding the event handler for a Tkinter button
# to pass other values with the event

try:
    # Python2
    import Tkinter as tk
except ImportError:
    # Python3
    import tkinter as tk

class MyApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("expanded event handler")
        # use width x height + x_offset + y_offset (no spaces!)
        self.geometry("280x200+50+50")
        self['bg'] = 'green'
        self.createWidgets()

    def createWidgets(self):
        """
        create a series of buttons
        and assign an expanded event handler
        """
        for k in range(1, 7):
            btn_text = "button%d" % k
            btn = tk.Button(self, text=btn_text)
            btn.pack(pady=5)
            # expanded event handler now contains the button label
            # could be other values too
            def handler(event, self=self, label=btn_text):
                return self.btn_clicked(event, label)
            # respond to left mouse click
            btn.bind('<1>', handler)

    def btn_clicked(self, event, label):
        """action code when button is clicked"""
        s = "you clicked button: " + label
        self.title(s)
        

app = MyApp()
app.mainloop()
bvdet 75 Junior Poster

Excellent snippet vegaseat. The expanded event handler solves a common problem.

Here's another working example using the default argument trick:

from Tkinter import *
from itertools import cycle

class SpinLabel(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack(fill=BOTH, expand=1)
        self.master.title("Spin Characters")
        self.textList = ["/", "--", "\\", "--", "|"]
        buttonFrame = Frame(self)
        self.btnList = []
        for i, name in enumerate(["Start", "Stop", "Exit"]):
            btn = Button(buttonFrame, text=name, padx=5, pady=5,
                         bd=4, bg='#ff0', fg="#00f",
                         activebackground = "#00f",
                         activeforeground = "#ff0",
                         font=("Arial", 12, "bold"),
                         relief='raised')
            btn.pack(side="left", fill=BOTH, expand=1)
                     
            def handler(event, i=i):
                return self.manage_spin(i)
            btn.bind("<ButtonRelease-1>", handler)
            self.btnList.append(btn)
        buttonFrame.pack(fill=BOTH, expand=1)
        
        self.spin = Label(self, font=("Courier New", 24, 'bold'),fg='#000')
        self.spin.pack(side="top", fill=BOTH, expand=1)
        self.spinChrs = cycle(self.textList)

    def manage_spin(self, idx):
        if idx == 0:
            self.btnList[0].configure(state=DISABLED)
            self.spinning = True
            self.loopchrs()
        elif idx == 1:
            self.btnList[0].configure(state=NORMAL)
            self.spinning = False
            self.spin.config(text="")
        elif idx == 2:
            self.master.destroy()

    def loopchrs(self):
        if self.spinning:
            self.spin.config(text=self.spinChrs.next())
            self.spin.update_idletasks()
            self.spin.after(100, self.loopchrs)

if __name__ == "__main__":
    app = SpinLabel()
    app.mainloop()

One more example:

import Tkinter
import random
"""Start with a grid of buttons with blank labels. When clicked, change the
text to a random number between 1 and 100."""

textFont3 = ("Arial", 12, "bold")

class App(Tkinter.Tk):
    def __init__(self, cols, rows):
        Tkinter.Tk.__init__(self)
        self.title("Grid of Buttons")
        self.cols = cols
        self.rows = rows
        
        self.mainFrame = Tkinter.Frame(self)
        self.mainFrame.config(padx='3.0m', pady='3.0m')
        self.mainFrame.grid()
        # initialize button array
        self.buttonList = [[None for j in range(cols)] for i in range(rows)]
        self.create_buttons()

    def create_buttons(self):
        for i in range(self.cols):
            for j in range(self.rows):
                btn = Tkinter.Button(self.mainFrame,text="",
                                 anchor='center',
                                 bd=3,
                                 bg='#ffffff000',
                                 fg="#000000fff",
                                 activebackground = "#000000fff",
                                 activeforeground = "#ffffff000",
                                 font=textFont3,
                                 padx='1.0m',
                                 pady='1.0m',
                                 relief='raised',
                                 state='normal', width=6)
                self.buttonList[i].append(btn)

                btn.grid(row=j, column=i)
                def handler(event, i=i, j=j):
                    return self.__buttonHandler(event, i, j)
                btn.bind(sequence="<ButtonRelease-1>", func=handler)

    def __buttonHandler(self, event, i, j):
        event.widget.configure(text=", ".join([str(obj) for obj in [i, j, random.randint(1,100)]]))

app = App(6,6)
app.mainloop()
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.