Events in TKinter

Image by Karolina Grabowska from Pixabay

Image by Karolina Grabowska from Pixabay

TKinter is a fantastic GUI framework that can be used with Python. Every widget in TKinter can bind an event a user action, and in many ways this is much more powerful than the typical command attribute used on buttons and other widgets.

In this article, I will explain how to use an event called by a button to access and change properties in the button widget.

Command

Call as simple callback function

When building GUI applications with Python and TKinter it's easy to use the command parameter when creating the widget. This is especially true with button widgets.

one_button.py
import tkinter

window = tkinter.Tk()
window.title("Button Example")

def button_action():
    print("Button was pressed!")


button = tkinter.Button(
    window,
    text="Press me!",
    width=10,
    command=button_action
)
button.grid(row=0, column=0)

window.mainloop()

By pressing the button, TKinter will call the "button_action" function, which will print to the terminal "Button pressed!". This is great if you just want to perform a specific action that is bound to a specific button.

But if you have several buttons that perform similar actions you have to declare several functions and use each function with each button like this.

two_buttons.py
import tkinter

window = tkinter.Tk()
window.title("Button Example")

def button_action_one():
    print("Button ONE was pressed!")


def button_action_two():
    print("Button TWO was pressed!")


button_one = tkinter.Button(
    window,
    text="ONE",
    width=10,
    command=button_action_one
)
button_one.grid(row=0, column=0)

button_two = tkinter.Button(
    window,
    text="TWO",
    width=10,
    command=button_action_two
)
button_two.grid(row=0, column=1)

window.mainloop()

As you can read from the code snippet above; this works fine with just two buttons. But imagine having 10 buttons, with similar actions. That's definitely not DRY (Don't Repeat Yourself) code.

Bind

Call callback functions with events

A much better option is to use the "bind" widget method. By using this method an event will be called, and on this event, you can access the widget that invoked the event.

two_buttons_bind.py
import tkinter

window = tkinter.Tk()
window.title("Button Example")

def button_action(event):
    button = event.widget
    print(f"Button {button['text']} was pressed!")


button_one = tkinter.Button(
    window,
    text="ONE",
    width=10,
)
button_one.bind("<Button-1>", button_action)
button_one.grid(row=0, column=0)

button_two = tkinter.Button(
    window,
    text="TWO",
    width=10,
)
button_two.bind("<Button-1>", button_action)
button_two.grid(row=0, column=1)

window.mainloop()

The "<Button-1>" parameter is used to specify which input method was used when clicking on the button. Button-1 is the "main mouse button". This is the left mouse button for right-hand mouse users. A complete list of input methods can be found on StackOverflow.

What's amazing about the bind method is that all TKinter widgets have the option to bind events to them. You can bind events to all widgets including labels and images.

By using bind and events you can use the function that was called by the event to make changes to a widget.

color_changer.py
import tkinter

window = tkinter.Tk()
window.title("Button Example")

def button_action(event):  
    button = event.widget
    if button["text"] == "ONE":
        label.config(fg="Red")
    elif button["text"] == "TWO":
        label.config(fg="Blue")


button_one = tkinter.Button(
    window,
    text="ONE",
    width=10,
)
button_one.bind("<Button-1>", button_action)
button_one.grid(row=0, column=0)

button_two = tkinter.Button(
    window,
    text="TWO",
    width=10,
)
button_two.bind("<Button-1>", button_action)
button_two.grid(row=0, column=1)

label= tkinter.Label(
    window,
    text="This is a test"
)
label.grid(row=1, column=0, columnspan=2)

window.mainloop()

Congratulations, you have now learned to use the bind method on TKinter widgets. By doing so you can use rich functions that will behave differently depending on what widget called it. 🎉