Sun 24 August 2025

ButtonPad: A Simple, Limited Python GUI Toolkit Built on Tkinter

Posted by Al Sweigart in misc   

GUI programming is tedious and you don't always need all the options and UI widgets they give you. I've created ButtonPad, a simple, intentionally-limited Python GUI framework for creating a grid of buttons, labels, and text boxes. I modelled them after the design of programmable stream deck or drum machine hardware. The source is on GitHub. It's built on top of tkinter, so it is lightweight and doesn't need additional GUI packages. Widget layout is done with a single comma-separated multiline string. No widget subclassing, no designer files, no sprawling boilerplate; just a layout string and some basic options. Still, I've made a surprising variety of simple apps with it which can all be run from the launcher program: python -m buttonpad

ButtonPad simplifies menubar creation and uses PyMsgBox for JavaScript-like dialog boxes (with alert(), confirm(), prompt(), and password() functions). You can set on_click, on_enter, and on_exit callback functions as well as tool tips. The ButtonPad window supports a status bar. You can set foreground and background color, button size, spacing, and font face/size. Other than that, it's strictly for simple GUI app creation, suited for:

  • Beginners who want to learn GUI programming without wrestling with verbose frameworks.
  • Experienced developers who want to crank out prototypes, internal tools, game ideas, or teaching demos fast.

To install, run pip install ButtonPad. To run the launcher program to view the included examples, run python -m buttonpad. These examples include:

  • Pocket calculator app
  • Connect Four game
  • Conway's Game of Life simulation
  • Dodger Race game (an original creation)
  • Emoji Copy Pad (a one-click way to copy emoji to the clipboard; uses pyperclip)
  • Fish tank simulation
  • Flood It clone game
  • Gomoku board game
  • Lights Out clone game
  • Magic 8 ball fortune teller
  • Memory puzzle game
  • Othello puzzle game (two-player and vs computer)
  • Peg solitaire board game
  • SameGame clone game
  • Simon memory testing game
  • 15-tile slide puzzle
  • Stopwatch app
  • Tic Tac Toe (two-player and vs computer)
  • 2048 clone game

TODO screenshots

Layout Configuration and Available Widgets

You can configure the buttons with a comma-separated multiline string. For example, here's a simple ButtonPad program that creates a phone keypad:

import buttonpad

bp = buttonpad.ButtonPad(
    """1,2,3
    4,5,6
    7,8,9
    *,0,#""",
    title="Telephone Keypad Demo",
)
bp.run()  # Start the GUI event loop.

When you run this program, it looks like this:

TODO SCREENSHOT

By default, the on_click action for a button prints the text on the button. The buttons have default text and background colors, and they automatically resize as the window changes its size.

ButtonPad offers button widgets, but also text boxes, labels, and images.

If you want a text box, enclose the layout text in [square brackets]. If you just want a text label instead of a button, enclose the layout text in 'single quotes' or "double quotes". If you want an image, use IMG_ followed by an absolute or relative file path to the image file. For example, here's a ButtonPad app that has a button, two text labels, a text box, and an image:

import buttonpad

bp = buttonpad.ButtonPad(
    """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png""",
    title="Widget Demo",
)
bp.run()  # Start the GUI event loop.

When you run this program, it looks like this:

TODO SCREENSHOT

If Pillow isn't installed, then the image is shown in its grid cell at its normal size. If Pillow is installed, then ButtonPad automatically resizes it to fit proportionately in its cell. (You can also have it stretch to always fill the cell, which is discussed later.)

You can merge cells in the grid together by having identically named widgets in the layout string:

import buttonpad

bp = buttonpad.ButtonPad(
    """
    Hello, Hello, A, B
    Hello, Hello, C, D
    Are all your pets named Eric?,Are all your pets named Eric?,Are all your pets named Eric?,Are all your pets named Eric?
    """,
    title="Merge Demo",
)
bp.run()  # Start the GUI event loop.

There is one "Hello" button that occupies a 2 x 2 area and a "Are all your pets named Eric?" button that spans across a 4 x 1 area at the bottom. When you run this program, it looks like this:

TODO SCREENSHOT

If you want to use the same text for a widget but don't want them merged, begin the label with a ` backtick character:

import buttonpad

bp = buttonpad.ButtonPad(
    """
    `Hello, `Hello, `Hello, `Hello
    `Hello, `Hello, `Hello, `Hello
    `Hello, `Hello, `Hello, `Hello
    `Hello, `Hello, `Hello, `Hello
    """,
    title="No-Merge Demo",
)
bp.run()  # Start the GUI event loop.

This program creates sixteen separate "Hello" buttons in a 4 x 4 grid. When you run this program, it looks like this:

TODO SCREENSHOT

Features

ButtonPad offers six kinds of UI widgets:

  • Buttons, which have a text label and slight animation when clicked.
  • Labels, which are just text.
  • Text boxes, which are editable, multiline text fields.
  • Images, containing an image stretched to fill the cell.
  • Status bar, displaying text at the bottom of the window.
  • Menu bar, offering menus and submenus.

Buttons, labels, text boxes, and images can all have callback functions for mouse clicks, mouse enter/exits. Their click callback function can be associated with a hotkey. They can have tooltips that appear when the mouse hovers over them.

Widgets all have a text_color and background_color setting that can be an HTML-style RGB color value like '#000000', '#ff00ff', or '#FF00FF'. They can also be a

ButtonPad Customization

You can make general customizations to the ButtonPad by passing it different arguments. Here's the def statement of the ButtonPad class's __init__() method with its parameters and default arguments:

class ButtonPad:
  def __init__(
    self,
    layout: str,  # """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png"""
    cell_width: Union[int, Sequence[int]] = 60,  # width of each grid cell in pixels; int for all cells or list of ints for per-column widths
    cell_height: Union[int, Sequence[int]] = 60,  # height of each grid cell in pixels; int for all cells or list of ints for per-row heights
    h_gap: int = 0,  # horizontal gap between cells in pixels
    v_gap: int = 0,  # vertical gap between cells in pixels
    window_color: str = '#f0f0f0',  # background color of the window
    default_bg_color: str = '#f0f0f0',  # default background color for widgets
    default_text_color: str = 'black',  # default text color for widgets
    title: str = 'ButtonPad App',  # window title
    resizable: bool = True,  # whether the window is resizable
    border: int = 0,  # padding between the grid and the window edge
    status_bar: Optional[str] = None,  # initial status bar text; None means no status bar
    menu: Optional[Dict[str, Any]] = None,  # menu definition dict; see menu property for details
  ):

TODO - can dynamically update layout

Button Customization

TODO - you'll never create the widget objects directly, but you can change them after the ButtonPad object has been amde.

import buttonpad

bp = buttonpad.ButtonPad(
    """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png""",
    title="Widget Demo",
)

# TODO

bp.run()  # Start the GUI event loop.

Label Customization

import buttonpad

bp = buttonpad.ButtonPad(
    """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png""",
    title="Widget Demo",
)

# TODO

bp.run()  # Start the GUI event loop.

Text Box Customization

import buttonpad

bp = buttonpad.ButtonPad(
    """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png""",
    title="Widget Demo",
)

# TODO

bp.run()  # Start the GUI event loop.

Image Customization

import buttonpad

bp = buttonpad.ButtonPad(
    """Button, 'Label 1', "Label 2", [Text Box], IMG_~/monalisa.png""",
    title="Widget Demo",
)

# TODO

bp.run()  # Start the GUI event loop.

Check out other books by Al Sweigart, free online or available for purchase:

...and other books as well! Or register for the online video course. You can also donate to or support the author directly.