Skip to main content

Keyboard-driven interactive button menus for Python terminal apps

Project description

interactive-buttons-v2

Keyboard-driven interactive button menus for your Python terminal apps.

Navigate with arrow keys, confirm with Enter. Three layout modes, 12 built-in style presets, and optional hotkey bindings. Works in any real terminal on Linux, Windows and macOS.


Installation

pip install interactive-buttons-v2

Requires Python 3.10+ and two lightweight dependencies:

Package Purpose
colorama Cross-platform ANSI color codes
readchar Raw single-keystroke input

Quick example

from interactive_buttons import Button, Component

buttons = [
    Button(label="Continue", value="continue"),
    Button(label="Exit",     value="exit"),
]

comp   = Component(buttons, global_buttons_style=ButtonStyle(**HACKER_STYLE))
choice = comp.column_buttons()

# print(f"You chose: {choice}")

Default example

Use up / down to navigate and Enter to confirm.


Layout modes

Component exposes three layout methods. Each one blocks until the user confirms a selection and returns that button's value.

Column layout

Buttons are stacked vertically. Navigate with up / down, confirm with Enter.

from interactive_buttons import Button, Component, ButtonStyle, CORPORATE_STYLE

buttons = [
    Button(label="New file",  value="new"),
    Button(label="Open file", value="open"),
    Button(label="Save",      value="save"),
    Button(label="Quit",      value="quit"),
]

comp   = Component(buttons)
choice = comp.column_buttons()

Default example

Linear (horizontal) layout

Buttons are placed side by side on a single line. Navigate with ← / →, confirm with Enter. Ideal for binary confirmations (Yes / No, OK / Cancel).

from interactive_buttons import Button, Component, ButtonStyle, MINIMAL_STYLE

buttons = [
    Button(label="Yes", value=True),
    Button(label="No",  value=False),
]

comp    = Component(buttons, global_buttons_style=ButtonStyle(**MINIMAL_STYLE))
confirm = comp.linear_buttons()

Matrix (grid) layout

Buttons are arranged in a square grid. Navigate with all four arrow keys, confirm with Enter. The grid side length is ceil(sqrt(len(buttons))).

from interactive_buttons import Button, Component, ButtonStyle, SUNSET_STYLE

buttons = [
    Button(label="Attack",  value="attack"),
    Button(label="Magic",   value="magic"),
    Button(label="Item",    value="item"),
    Button(label="Defend",  value="defend"),
    Button(label="Flee",    value="flee"),
    Button(label="Status",  value="status"),
    Button(label="Options", value="options"),
    Button(label="Save",    value="save"),
    Button(label="Quit",    value="quit"),
]

comp   = Component(buttons, global_buttons_style=ButtonStyle(**SUNSET_STYLE))
action = comp.matrix_buttons()

Default example


Component options

Parameter Type Default Description
buttons list[Button] - Ordered list of buttons
global_buttons_style ButtonStyle DEFAULT_STYLE Style applied to buttons without a local override
multi_select MultiSelectStyle | None None Enable multi-selection mode (see Multi-select)
display_index bool False Auto-prefix each button with a digit shortcut (1, 2, …)
index_begin int 1 Starting digit when display_index is active
auto_erase bool False Erase the rendered buttons automatically after selection

Note: display_index=True and per-button ButtonKeyBind cannot be used at the same time. Doing so raises KeyBindError.


Styles

Built-in presets

12 presets are exported from the top-level package as plain dict. Unpack with **:

from interactive_buttons import ButtonStyle, HACKER_STYLE

style = ButtonStyle(**HACKER_STYLE)
  • DEFAULT_STYLE
  • HACKER_STYLE
  • MINIMAL_STYLE
  • MONOCHROME_STYLE
  • CORPORATE_STYLE
  • TERMINAL_RETRO_STYLE
  • MATRIX_STYLE
  • CYBERPUNK_STYLE
  • DANGER_STYLE
  • SUCCESS_STYLE
  • SUNSET_STYLE
  • GHOST_STYLE

Custom style

from colorama import Fore, Back
from interactive_buttons import ButtonStyle, Button, Component

my_style = ButtonStyle(
    text_color=Fore.MAGENTA,
    highlight_color=Back.WHITE,
    spaces_count=4,
    left_decorator="* ",
    right_decorator=" *",
)

comp = Component(buttons, global_buttons_style=my_style)
Parameter Type Description
text_color str ANSI code for the label text color
highlight_color str ANSI code applied to the selected button background
spaces_count int Padding spaces on each side of the label (minimum 2)
left_decorator str String prepended to the label
right_decorator str String appended to the label

Per-button style override

Each Button accepts a local_button_style that overrides the component-level style for that button only:

from interactive_buttons import Button, Component, ButtonStyle, SUCCESS_STYLE, DANGER_STYLE

buttons = [
    Button(label="Confirm", value="confirm", local_button_style=ButtonStyle(**SUCCESS_STYLE)),
    Button(label="Cancel",  value="cancel",  local_button_style=ButtonStyle(**DANGER_STYLE)),
]

comp   = Component(buttons)
choice = comp.linear_buttons()

Key bindings

ButtonKeyBind lets you assign one or more hotkeys to a button. Pressing a bound key moves the cursor to that button, or immediately confirms it if press_on_selection=True.

Basic binding

from interactive_buttons import Button, ButtonKeyBind, Component, ButtonStyle, HACKER_STYLE

save_bind = ButtonKeyBind(b_keys=["s", "1"])
quit_bind = ButtonKeyBind(b_keys=["q", "2"])

buttons = [
    Button(label="Save", value="save", key_bind=save_bind),
    Button(label="Quit", value="quit", key_bind=quit_bind),
]

comp   = Component(buttons, global_buttons_style=ButtonStyle(**HACKER_STYLE))
choice = comp.column_buttons()

Pressing s or 1 jumps to Save. The user still needs Enter to confirm.

Instant-confirm on key press

Set press_on_selection=True to confirm the button the moment its key is pressed, no Enter needed:

yes_bind = ButtonKeyBind(b_keys=["y"], press_on_selection=True)
no_bind  = ButtonKeyBind(b_keys=["n"], press_on_selection=True)

buttons = [
    Button(label="Yes", value=True,  key_bind=yes_bind),
    Button(label="No",  value=False, key_bind=no_bind),
]

comp    = Component(buttons)
confirm = comp.linear_buttons()
# Pressing "y" immediately returns True and pressing "n" immediately returns False.

Case-sensitive keys

By default, key matching is case-insensitive (a and A trigger the same binding). Set case_sensitive=True to distinguish them:

lower_bind = ButtonKeyBind(b_keys=["a"], case_sensitive=True)
upper_bind = ButtonKeyBind(b_keys=["A"], case_sensitive=True)

Accepted characters

Each entry in b_keys must be a single ASCII alphanumeric character (A-Z, a-z, 0-9) or a space (' '). Anything else raises KeyBindError at construction time. Each key must also be unique across all buttons in the same Component.


Multi-select

Pass a MultiSelectStyle to Component to let the user tick multiple buttons before confirming. SPACE (or a custom key) toggles the selection of the highlighted button and Enter confirms once the selection satisfies the configured bounds.

from interactive_buttons import Button, Component, MultiSelectStyle

toppings = [
    Button(label="Cheese",    value="cheese"),
    Button(label="Mushrooms", value="mushrooms"),
    Button(label="Peppers",   value="peppers"),
    Button(label="Olives",    value="olives"),
]

ms = MultiSelectStyle(
    tick_on="[x] ",  # Default
    tick_off="[ ] ",  # Default
    min_selected_amount=1,
    max_selected_amount=3,
    display_error="Pick between 1 and 3 toppings.",
)

comp    = Component(toppings, multi_select=ms)
choices = comp.column_buttons()   # returns the list of selected elements

print(f"Chosen: {choices}")

Default example

column_buttons, linear_buttons, and matrix_buttons all support multi-select. The return type changes from a single value to a list[Any].

MultiSelectStyle parameter Type Default Description
tick_on str "[x] " Prefix shown on a selected button
tick_off str "[ ] " Prefix shown on an unselected button
min_selected_amount int 0 Minimum ticked buttons before Enter is accepted
max_selected_amount int -1 Maximum ticked buttons (-1 = unlimited)
display_error str | None None Message shown when the user tries to confirm an invalid selection
select_keys list[str] [SPACE] Keys that toggle the highlighted button's ticked state

Documentation

Full documentation at interactive-buttons.mbinc.tech.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

interactive_buttons_v2-1.1.2.tar.gz (86.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

interactive_buttons_v2-1.1.2-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file interactive_buttons_v2-1.1.2.tar.gz.

File metadata

  • Download URL: interactive_buttons_v2-1.1.2.tar.gz
  • Upload date:
  • Size: 86.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for interactive_buttons_v2-1.1.2.tar.gz
Algorithm Hash digest
SHA256 d5f14ac32bbe00b2f44575e3f48feb38f4c6f2f79170da736b029d9f8016d85e
MD5 5c7c4c250d22422656f627f418e4d668
BLAKE2b-256 e2d82439261983f1ad49d94d2c63eb240fe2fddc997c9e4918a8b64c8b97a74c

See more details on using hashes here.

Provenance

The following attestation bundles were made for interactive_buttons_v2-1.1.2.tar.gz:

Publisher: publish.yml on mbcraft-exe/interactive-buttons-v2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file interactive_buttons_v2-1.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for interactive_buttons_v2-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4378101ac145d9d3fa168cbe88318a93030e52e8265fd6677efa42737893b6ae
MD5 9fb587e7f8f9a37eac53d4c744a4e3fa
BLAKE2b-256 d0ad9922ca477d55b73d83df7606186451f1b6edd1d2e15a5d18bd2b28564dfe

See more details on using hashes here.

Provenance

The following attestation bundles were made for interactive_buttons_v2-1.1.2-py3-none-any.whl:

Publisher: publish.yml on mbcraft-exe/interactive-buttons-v2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page