Skip to content

Python GUI Programming

Python offers several libraries for creating Graphical User Interfaces (GUIs). Some popular options include:

  • tkinter: (Formerly Tkinter in Python 2) The standard, built-in Python interface to the Tcl/Tk GUI toolkit. It’s cross-platform and relatively easy to start with. This chapter focuses on tkinter.
  • PyQt / PySide: Bindings for the powerful Qt framework. Offers a wide range of modern widgets and features. Often used for complex desktop applications.
  • Kivy: An open-source library for developing multi-touch applications. Suitable for modern UIs, games, and mobile development.
  • wxPython: Bindings for the wxWidgets C++ library. Another mature cross-platform toolkit.
  • Web Frameworks (with wrappers): Using frameworks like Flask/Django with tools like PyWebView or Electron-like wrappers to create desktop apps with web technologies.

tkinter provides a straightforward way to build GUI applications directly with Python. It wraps the Tcl/Tk toolkit, making its components available as Python classes.

Basic Steps to Create a tkinter Application:

  • Import the tkinter module (commonly imported as tk).
  • Create the main application window (an instance of tk.Tk).
  • Add widgets (buttons, labels, entries, etc.) to the main window or other container widgets (like Frames).
  • Configure widget properties and layout using geometry managers (pack, grid, place).
  • Define event handlers (functions to call when buttons are clicked, etc.).
  • Start the tkinter main event loop (mainloop()) to make the window interactive and responsive to user actions.
#!/usr/bin/env python3
import tkinter as tk
# 1. Create the main window
root = tk.Tk()
root.title("Simple Tkinter App") # Set window title
root.geometry("300x150") # Set initial window size (width x height)
# 2. Add widgets (example: a label)
my_label = tk.Label(root, text="Hello, Tkinter!")
my_label.pack(pady=20) # Use pack geometry manager with padding
# 3. Add a button with an action
def on_button_click():
print("Button clicked!")
my_label.config(text="Button was clicked!")
my_button = tk.Button(root, text="Click Me", command=on_button_click)
my_button.pack()
# 4. Start the main event loop
root.mainloop()
# Code after mainloop() executes only after the window is closed.

Running this script will create a small window with the title “Simple Tkinter App”, containing the text “Hello, Tkinter!” and a button labeled “Click Me”. Clicking the button will print a message to the console and change the label’s text.

Widgets are the building blocks of a tkinter GUI (buttons, text boxes, labels, etc.). Each widget is represented by a class.

Commonly used widgets:

Widget Class / ModuleDescription
TkThe main (root) window of the application.
ToplevelCreates additional, separate windows.
FrameA container widget used to group and organize other widgets.
LabelDisplays static text or images.
ButtonA clickable button that can trigger a function call (command).
EntryA single-line text input field.
TextA multi-line text input and display area.
CheckbuttonA checkbox allowing selection/deselection.
RadiobuttonAllows selection of one option from a group (linked by a shared variable).
ListboxDisplays a list of items from which the user can select.
ScrollbarAdds scrolling capability to widgets like Listbox, Text, Canvas.
MenuCreates menu bars and context menus.
ScaleA slider widget for selecting a value within a range.
CanvasA versatile widget for drawing shapes, lines, text, images, and embedding other widgets.
SpinboxAn Entry widget with up/down arrows to select from a range of values.
LabelFrameA Frame with a visible border and a text label.
PanedWindowA container that manages panes that can be resized by the user.
MessageSimilar to Label but designed for multi-line text messages.
messagebox (module)tkinter.messagebox provides functions for standard dialog boxes (info, warning, error, askyesno, etc.).

Widgets have numerous options (attributes) that control their appearance and behavior. These can be set during creation or modified later using the config() or configure() method (or dictionary-like access widget['option'] = value).

Common options include:

  • text: The text displayed on the widget (Label, Button, etc.).
  • textvariable: A tkinter variable (StringVar, IntVar, etc.) linked to the widget’s text/value. Changes to the variable update the widget, and vice versa (for Entry, Checkbutton, Radiobutton, Scale).
  • command: A function to call when the widget is activated (e.g., Button click).
  • width, height: Size of the widget (units depend on the widget, often text units or pixels).
  • bg (or background), fg (or foreground): Background and text/foreground colors.
  • font: Specifies the font (e.g., ('Arial', 12, 'bold')).
  • relief: Border style (flat, raised, sunken, groove, ridge).
  • borderwidth (or bd): Width of the border.
  • state: Widget state (normal, disabled, readonly).
  • anchor: Where to position content within the widget space (e.g., tk.N, tk.S, tk.E, tk.W, tk.CENTER).
  • padx, pady: Internal padding (space between content and border).
  • image: Displays an image (requires PhotoImage or BitmapImage object).

Example using options:

import tkinter as tk
root = tk.Tk()
root.title("Widget Options")
# Label with specific font and color
label = tk.Label(
root,
text="Styled Label",
font=("Helvetica", 16, "italic"),
fg="blue",
bg="lightgrey",
padx=10,
pady=10,
relief=tk.RAISED,
borderwidth=2
)
label.pack(pady=10)
# Button initially disabled
button = tk.Button(root, text="Disabled Button", state=tk.DISABLED)
button.pack(pady=5)
# Function to enable the button
def enable_button():
button.config(state=tk.NORMAL)
button['text'] = "Enabled!"
enabler_button = tk.Button(root, text="Enable Other Button", command=enable_button)
enabler_button.pack(pady=5)
root.mainloop()

Geometry managers control how widgets are positioned and sized within their parent container. tkinter provides three main managers:

  • pack(): Organizes widgets in blocks. Widgets are packed sequentially along the sides (top, bottom, left, right) of the remaining space in the container. Simple for basic layouts but can become complex for intricate designs.
  • grid(): Arranges widgets in a table-like grid of rows and columns. Offers more precise control over placement and alignment. Very flexible for complex layouts.
  • place(): Allows positioning widgets using absolute coordinates (x, y) or relative positions/sizes within the container. Less commonly used for general layout as it doesn’t adapt well to window resizing or content changes, but useful for specific positioning needs.

Important: You should not mix pack() and grid() within the same parent container. Use one or the other consistently for widgets directly inside a specific Frame or Window. You can, however, use pack inside one Frame and grid inside a different Frame within the same window.

label1 = tk.Label(root, text="Top", bg="red")
label1.pack(side=tk.TOP, fill=tk.X) # Fill horizontally
label2 = tk.Label(root, text="Left", bg="blue")
label2.pack(side=tk.LEFT, fill=tk.Y) # Fill vertically
label3 = tk.Label(root, text="Center", bg="green")
label3.pack(expand=True, fill=tk.BOTH) # Expand to fill remaining space
entry1 = tk.Entry(root)
entry1.grid(row=0, column=1, padx=5, pady=5)
label1 = tk.Label(root, text="Label 1:")
label1.grid(row=0, column=0, sticky=tk.E) # Align right
entry2 = tk.Entry(root)
entry2.grid(row=1, column=1, padx=5, pady=5)
label2 = tk.Label(root, text="Longer Label 2:")
label2.grid(row=1, column=0, sticky=tk.E)
button = tk.Button(root, text="Submit")
# Span across 2 columns, add padding, stick to bottom-right corner within cell
button.grid(row=2, column=0, columnspan=2, pady=10, sticky=tk.SE)
button1 = tk.Button(root, text="Absolute Position")
button1.place(x=50, y=50)
button2 = tk.Button(root, text="Relative Position")
# Place at 70% width, 70% height relative to parent size
button2.place(relx=0.7, rely=0.7, anchor=tk.CENTER) # anchor center

GUIs respond to user interactions (events) like button clicks, key presses, mouse movements. tkinter uses callbacks and event binding.

  • command option: Many widgets (like Button, Checkbutton, Radiobutton) have a command option that specifies a function to call when the widget is activated.
  • bind() method: Provides a more general way to bind Python functions (event handlers) to specific events (like <Button-1> for left mouse click, <KeyPress> for key press, <Enter> for mouse entering widget area) on any widget.

Example using bind():

import tkinter as tk
def handle_click(event):
# 'event' object contains details like coordinates (event.x, event.y)
print(f"Left mouse button clicked at ({event.x}, {event.y})")
def handle_keypress(event):
print(f"Key pressed: '{event.char}' (Key code: {event.keycode})")
root = tk.Tk()
root.geometry("300x200")
frame = tk.Frame(root, width=200, height=100, bg="lightblue")
# Bind left mouse click event (<Button-1>) on the frame
frame.bind("<Button-1>", handle_click)
frame.pack(pady=20)
entry = tk.Entry(root)
# Bind any key press event (<KeyPress>) on the entry widget
entry.bind("<KeyPress>", handle_keypress)
entry.pack(pady=10)
entry.focus_set() # Give focus to entry so keypresses are captured
root.mainloop()