Tkinter Button Widget

This post contains examples of using the tkinter button widget. Python code for creating buttons and commands, setting styles, and adding images.

Updated

Table of Contents

Create Button Widget


A button widget is usually placed in another widget like root window, frame, etc. Buttons can contain text, bitmaps, images.


# import tkinter module
import tkinter as tk

# create a root window that will host the button
# root, an instance of the tkinter.Tk class, is a top-level widget
root = tk.Tk()

# create a button widget, an instance of the tkinter.Button class
button = tk.Button(root, text="Go")

# pack the button so that it is visible in the window
button.pack()

# start the GUI event loop
root.mainloop()

Buttons and Callbacks


The purpose of a tkinter button is to perform an action in response to user interaction with graphical interface elements. For example, confirm the completion of the form, start or stop the process.

We can associate a function call with a button click in tkinter. When a button is pressed, the corresponding function is triggered.

Such a function is usually passed as an argument with a command parameter when creating a button and is called a callback.


Button With Command


import tkinter as tk

def callback():
    # do some important things
    print("callback")

root = tk.Tk()

button = tk.Button(root, text="Go", command=callback)
button.pack()

root.mainloop()

Button With Event Binding


Another option is to add a callback function as a handler for a specific event.

In this case, the callback function requires an event parameter. This parameter represents an instance of the tkinter Event class.

The event is a container for event properties. It can provide some useful information and access to the event widget.


import tkinter as tk

def callback(event):
    """This function is an event handler."""
    # do some important things
    print(event)
    print(event.widget, event.widget["state"])

root = tk.Tk()

button = tk.Button(root, text="Go")
button.pack()

# leftmost button click event
button.bind("<Button-1>", callback)

root.mainloop()

Button and Functions With Arguments


Sometimes we need to pass specific values to a callback for further processing.

If we do command=callback(values), the function will simply be executed by the Python interpreter just before we click the button.

Lambda, an anonymous inline function, is most often used to pass the required values. The syntax for creating this function is lambda [parameters]: expression. The callback(values) is specified instead of expression. When we click on the button, the lambda function is called, which in turn calls the callback.


import tkinter as tk

def callback1(arg):
    print(arg)

def callback2(event, arg):
    # event parameter is required anyway
    print(arg)

command_text = "Command callback."
event_text = "Event callback."

root = tk.Tk()

button = tk.Button(root, text="Go",
                   command=lambda: callback1(command_text))
# or like this:
#button = tk.Button(root, text="Go",
                   #command=lambda arg=command_text: callback1(arg))
button.pack()
# leftmost button click event
button.bind("<Button-1>",
            lambda event, arg=event_text: callback2(event, arg))
            
# or like this:
#button.bind("<Button-1>",
            #lambda event: callback2(event, event_text))

root.mainloop()

Button Options


Buttons can accept additional configuration options that can change their appearance and behavior.

Some of these options have default values such as background and foreground colors, highlight options, font, borderwidth, justify, padx, pady, relief, state, and others.


Button Widget Options


Standard options:

activebackground, activeforeground, anchor, background (or bg), bitmap, borderwidth (or bd), compound, cursor, disabledforeground, font, foreground (or fg), highlightbackground, highlightcolor, highlightthickness, image, justify, padx, pady, relief, repeatdelay, repeatinterval, takefocus, text, textvariable, underline, wraplength

Widget-specific options:

command, default, height, overrelief, state, width

Button commands:

cget, configure (or config), flash, invoke

See details: Tcl8.6/Tk8.6 Button man page. The effect of using the options may differ depending on the platform.


We can change the state and style properties of the button after it has been created.


import tkinter as tk

def callback():
    # set or update options after creating the button
    button["text"] = "Disabled!"
    # update multiple options
    button.config(state="disabled", relief="flat",
                  disabledforeground="black",
                  background="red")
    
root = tk.Tk()

# set options
button = tk.Button(root, text="Go",
                   background="blue", foreground="white")
button.pack()

# set or update options after creating the button
button["command"] = callback

# get option values
print(button.config()) # all options
print(button["background"])
print(button.cget("relief"))

root.mainloop()

The tk.Button has three states: "normal", "active", or "disabled".

button["option"] is Python feature. See def cget, __getitem__, __setitem__ in the Misc class in the tkinter source.

The implementation of __getitem__ and __setitem__ allows us to get the value of an option by key or set a new value for an option, respectively. Tkinter widgets inherit methods from this class.

The tkinter Button class also inherits many data attributes and methods from other classes. We can view a list of them using print(dir(button)).


Background, Foreground, Font


There are several ways to specify colors and fonts for a tkinter button. For colors, you can use a symbolic color name or an RGB value (#FF5 or #FEF65B). The font can be a tuple of font settings or a named object of the Font class.

Some color and font family names are system-specific.


import tkinter as tk
from tkinter import font

root = tk.Tk()

# a) Font tuple
# family, size, weight, slant, "underline", and "overstrike"
left_button_font = ("Calibri", 10, "bold")

# b) Font object
# available font families - print(font.families())
right_button_font = font.Font(family="Courier", size=10,
                              weight="bold", slant="italic",
                              underline=1, name="RightButtonFont")

left_button = tk.Button(root, text="Left button",
                        background="#AAAFFF", foreground="black",
                        font=left_button_font)
left_button.pack(fill="x", side="left", expand=True,
                 padx=10, pady=10)

right_button = tk.Button(root, text="Right button",
                         background="grey", foreground="#FFFAAA",
                         font=right_button_font)
right_button.pack(fill="x", side="right", expand=True,
                  padx=10, pady=10)

root.mainloop()


Padding, Relief, Border


We can use the padx and pady options to add padding to the content of the tkinter button. The padx value indicates how much extra space we want for the widget in the X direction. The pady value indicates how much extra space we want for the widget in the Y direction. These are internal button paddings.

To visually separate the button from other elements, we can also use the padx and pady options of the geometry manager (pack or grid). These are will be external button paddings.

We can set two values, for each direction x and y, in pack or grid geometry managers.


import tkinter as tk

root = tk.Tk()
root.geometry("500x100")

# paddings and borderwidth in pixels

frame = tk.Frame(root, background="white")
frame.pack()

# padx, pady - paddings from content(text) to button borders
button1 = tk.Button(frame, text="B1",
                    background="green", borderwidth=5,
                    # one value for each direction x and y
                    # left - 5, right - 5
                    # top - 5, bottom - 5
                    padx=5, pady=5)
button1.pack(side="left")

# padx, pady - paddings from content(text) to button borders
button2 = tk.Button(frame, text="B2",
                    background="green", borderwidth=10,
                    # one value for each direction x and y
                    # left - 20, right - 20
                    # top - 5, bottom - 5
                    padx=20, pady=5)
button2.pack(side="left")

# button with external paddings
button3 = tk.Button(frame, text="B3",
                    background="green", borderwidth=5,
                    padx=5, pady=5)
# two values for each direction x and y
# left - 10, right - 50
# top - 0, bottom - 20
button3.pack(side="left", padx=[10, 50], pady=[0, 20])

# button with external paddings
button4 = tk.Button(frame, text="B4",
                    background="green", borderwidth=5,
                    padx=15, pady=5)
# two values for x direction
# left - 0, right - 5
# top - 10, bottom - 10
button4.pack(side="left", padx=[0, 5], pady=10)

root.mainloop()


We can use the borderwidth and relief options to create a 3D effect around the button. The appearance of the border depends on the value of the relief option.

The following example creates tkinter buttons in a loop to showcase relief styles.


import tkinter as tk

root = tk.Tk()
root.config(background="lightyellow")

# all relief styles
relief_styles = [
    "raised", "sunken", "flat", "ridge", "solid", "groove"
    ]
row = 0 # put the buttons in one line
column = 0 # but in different columns

# create table-like structure
# paddings and borderwidth in pixels
for i in relief_styles:
    button = tk.Button(root, text=i,
                       background="lightgreen",
                       borderwidth=10, relief=i,
                       # paddings from content(text) to button borders
                       padx=5, pady=3)
    # use grid geometry manager
    button.grid(row=row, column=column,
                # paddings from content(button) to grid cell borders
                padx=10, pady=10)
    column += 1

root.mainloop()


Button With Image


The buttons can display text and/or an image. To specify how the image should be positioned relative to the text, we need to use the compound option.


import tkinter as tk

root = tk.Tk()
root.config(background="silver")

# png 16x16
python_image = tk.PhotoImage(file="python.png")

# png 100x100
cat_image = tk.PhotoImage(file="cat.png")
cat_image_subsample = cat_image.subsample(x=3, y=3)

# button with text and image on the left side
button1 = tk.Button(root, text="Python",
                    image=python_image, compound="left",
                    padx=5, font=("Courier", 10))
button1.pack(padx=10, pady=10, side="left")

# button with reduced image
button2 = tk.Button(root, borderwidth=5,
                    image=cat_image_subsample,
                    relief="groove")
button2.pack(padx=10, pady=10, side="left")

# button that looks like an image with a border
label = tk.LabelFrame(root, background="white")
label.pack(padx=10, pady=10, side="left")
button3 = tk.Button(label, image=cat_image,
                    borderwidth=0, cursor="hand2",
                    highlightthickness=0)
button3.pack(padx=5, pady=5) # make a white border around

# button with sizes and text on the image
button4 = tk.Button(root, text="CATS",
                    # compound="center" - text on image
                    image=cat_image, compound="center",
                    borderwidth=3,
                    height=30, width=60,
                    foreground="white",
                    font=("Calibri", 10, "bold"))
button4.pack(padx=10, pady=10, side="left")

root.mainloop()


The images used in the example:



Buttons and Place Geometry Manager


There is also a third geometry manager - place. It provides absolute or relative placement of widgets in some container, root window, or other widget. A button can be positioned using specific x and y coordinates in the container window.

The place geometry manager can be useful if we need to place a button relative to another window element. For example, in forms or custom dialog boxes. We can combine the absolute and relative options.


import tkinter as tk

root = tk.Tk()
root.geometry("250x150")
root.config(background="lightgreen")

entry1 = tk.Entry(root)
entry1.place(x=10, y=10)

button1 = tk.Button(root, text="B1")
# placed relative to the entry1
button1.place(in_= entry1, bordermode="outside",
              relx=1.1, rely=0,
              width=50, relheight=1.0)

entry2 = tk.Entry(root)
entry2.place(x=10, y=60)

button2 = tk.Button(root, text="B2")
# placed relative to the entry2
button2.place(in_= entry2, bordermode="outside",
              anchor="center",
              relx=0.5, rely=2,
              width=50, relheight=1.0)

root.mainloop()


Another case where we can use place() is when we need to overlay a button on another window element.


import tkinter as tk

root = tk.Tk()
root.geometry("500x150")
root.config(background="lightgreen")

label = tk.Label(root, background="white",
                 width=50, height=5)
label.pack(side="top", pady=20)

button = tk.Button(root, text="Button",
                   relief="flat", overrelief="ridge",
                   background="#E5E0D8", borderwidth=5)

# button in the upper right corner of the label
button.place(in_=label, anchor="ne",
             relx=1.0, rely=0)

root.mainloop()


If we use place, we need to calculate the position of each widget in pixels. Widgets must fit each other and the root window. Using a grid for complex window layouts is more maintainable.


Popular posts from this blog