Skip to main content

Simple, keyboard-driven terminal menus for Python (1D lists & 2D grids).

Project description

ExpertTUI

Simple, keyboard-driven terminal menus for Python — 1D lists and 2D grids.

PyPI Python License: MIT

╭──────────────────────────────────╮      ╭──────────────────┬──────────────────╮
│  Settings                        │      │  New Game        │                  │
│ ─── Audio / Video ────────────── │      ├──────────────────┼──────────────────┤
│  > [x] Music                     │      │  > Class: < Mage │  Diff:   Normal  │
│    [ ] Fullscreen                │      │    Name: hero... │                  │
│    [x] Sound FX                  │      ├──────────────────┼──────────────────┤
│  ────────────────────────────── │      │  > Start         │    Back          │
│    Save & Exit                   │      ╰──────────────────┴──────────────────╯
╰──────────────────────────────────╯
        menu_1D                                      menu_2D

Features

  • Five item types — action, selector (←→), text input, checkbox, hr separator
  • Two layouts — vertical menu_1D and grid-based menu_2D
  • Five border stylessingle double rounded heavy block with correct Unicode junction characters where borders meet separators (├ ┤ ┼ ╬ ┬ ┴ …)
  • Column alignmentalign=True keeps 2D columns in sync across all rows
  • Gap fill — styled column separators (│ ║ ┃ █) that connect to borders
  • Inline editing — cursor jumps to the input field; menu stays visible
  • Live border resizewidth="auto" shrinks/grows the border as you type
  • Scrollheight=N shows a window, scrolls with the cursor
  • enter_selector — require Enter to activate a selector (global or per-item)
  • Full type hints — explicit keyword-only parameters for IDE autocomplete
  • TypedDicts — importable item types for dict-key autocomplete

Installation

pip install experttui

Requirements: Python ≥ 3.8 · colorama · readchar


Quick Start

from experttui import menu_1D

menu_1D([
    "Choose an option",
    ("Start game",  lambda: print("Starting!")),
    ("Settings",    lambda: print("Opening settings…")),
    ("Quit",        lambda: quit()),
])

Item Types

Type Format Description
Static label "text" Non-selectable line
Action (tuple) ("label", fn) Calls fn on Enter
Action (dict) {"text": …, "function": fn} Same, with extra options
Selector {"type": "selector", "options": […], "index": 0} ←→ cycles values
Input {"type": "input", "value": "", "placeholder": "…"} Enter opens inline editor
Checkbox {"type": "checkbox", "checked": False} Enter toggles
Separator {"type": "hr"} Horizontal line connecting to border

Extra keys per type

Action dictdisabled (bool), color_selected (str), prefix (str), suffix (str)

Selectorcolor_selected, color_options, enter (bool — per-item enter_selector)

Inputcolor_selected

Checkboxdisabled, color_selected

Hrfill (border style name), label (centred text)

Importing TypedDicts for autocomplete

from experttui import SelectorItem, InputItem, CheckboxItem, HrItem

quality: SelectorItem = {
    "type": "selector",
    "text": "Quality",
    "options": ["Low", "Medium", "High"],
    "index": 1,
}

menu_1D — Full Reference

from experttui import menu_1D

menu_1D(
    items,                           # list — see Item Types above
    *,
    color               = "YELLOW",  # colorama colour name
    selection_type      = "left_arrow",  # "left_arrow" | "two_arrows"
    moving              = "both",    # "both" | "arrows" | "wsad"
    border              = False,     # False | True/"double" | "single" |
                                     # "rounded" | "heavy" | "block"
    wait_after_function = False,     # pause after action before redrawing
    return_after_action = False,     # exit menu after action (skip redraw)
    enter_selector      = False,     # require Enter to activate selector edit mode
    width               = "auto",   # "auto" (live resize) | int (fixed cols)
    height              = "auto",   # "auto" | int (scrollable window)
    margin              = 0,         # space outside border, all sides
    margin_top          = None,      # per-side override
    margin_right        = None,
    margin_bottom       = None,
    margin_left         = None,
    padding             = 0,         # space inside border, all sides
    padding_top         = None,
    padding_right       = None,
    padding_bottom      = None,
    padding_left        = None,
)

Keyboard reference

Item Up / Down Left / Right Enter ESC
Navigate ↑↓ / WS Exit
Action Run function
Selector Cycle value Activate (if enter_selector)
Input Open inline editor Exit editor
Checkbox Toggle

When enter_selector is active for a selector: Enter opens edit mode, ←→/AD change value, Enter confirms, ESC restores the original value.


menu_2D — Full Reference

All menu_1D parameters, plus:

menu_2D(
    items,       # list of rows — each row is a list or a single item
    *,
    # … all menu_1D params …
    align    = True,   # align every column to the max width of that column
    gap      = 2,      # columns of space between cells
    gap_fill = False,  # False | border-style name — vertical char in the gap
)

Row structure:

menu_2D([
    "Full-width header",               # single item → spans entire row
    {"type": "hr", "label": "Setup"},  # full-width separator
    [selector_a, selector_b],          # two columns
    [input_field],                     # one column (full width)
    [("OK", fn), ("Cancel", fn2)],     # two action buttons
])

Keyboard reference (2D)

Action Key
Move between rows ↑↓ / WS
Move between columns ←→ / AD
Cycle single-col selector ←→ / AD
Activate selector (enter_selector) Enter

Border Styles

single    rounded   double    heavy     block
┌──────┐  ╭──────╮  ╔══════╗  ┏━━━━━━┓  ██████
│ item │  │ item │  ║ item ║  ┃ item ┃  █ item █
└──────┘  ╰──────╯  ╚══════╝  ┗━━━━━━┛  ██████

Junction characters are inserted automatically when border + hr or border + gap_fill are both set:

┌──────────────┬──────────────┐   ← ┬ where gap meets top border
│ Class: Mage  │ Diff: Normal │
├──────────────┼──────────────┤   ← ┼ where gap meets hr
│ Start        │ Back         │
└──────────────┴──────────────┘   ← ┴ where gap meets bottom border

Examples

Settings menu with checkboxes

from experttui import menu_1D

quality  = {"type": "selector",  "text": "Quality",
            "options": ["Low", "Medium", "High"], "index": 1}
username = {"type": "input",     "text": "Username",
            "value": "", "placeholder": "enter name…"}
music    = {"type": "checkbox",  "text": "Music",    "checked": True}
sfx      = {"type": "checkbox",  "text": "Sound FX", "checked": True}

menu_1D([
    "Settings",
    {"type": "hr", "fill": "single"},
    quality, username,
    {"type": "hr", "fill": "single", "label": "Audio"},
    music, sfx,
    {"type": "hr"},
    ("Save & Exit", lambda: print("Saved!")),
    {"text": "Locked option", "disabled": True},
],
    color="cyan", border="rounded", padding=1, margin_left=2,
    wait_after_function=True, return_after_action=True, width=32,
)

Character select (2D grid)

from experttui import menu_2D

char_class = {"type": "selector", "text": "Class",
              "options": ["Mage", "Warrior", "Rogue"], "index": 0}
difficulty = {"type": "selector", "text": "Diff",
              "options": ["Easy", "Normal", "Hard"], "index": 1,
              "enter": True}   # this one needs Enter to activate
char_name  = {"type": "input",   "text": "Name",
              "value": "", "placeholder": "hero…"}

def start():
    print(f"Class={char_class['options'][char_class['index']]}, "
          f"Diff={difficulty['options'][difficulty['index']]}, "
          f"Name={char_name['value']}")

menu_2D([
    "New Game",
    {"type": "hr"},
    [char_class, difficulty],
    [char_name],
    {"type": "hr"},
    [("Start", start), ("Back", lambda: None)],
],
    color="green", border="single", padding=1,
    gap=3, gap_fill="single", align=True,
    wait_after_function=True,
)

Utilities

from experttui import clear_screen, hide_cursor, show_cursor

clear_screen()   # clear the entire terminal
hide_cursor()    # hide blinking cursor
show_cursor()    # restore cursor

Contributing

  1. Fork → feature branch → PR
  2. Run pytest before submitting
  3. Follow existing code style (type hints, _ prefix for private functions)

License

MIT — see LICENSE.



ExpertTUI — Dokumentacja (PL)

Proste, klawiaturowe menu terminalowe dla Pythona — listy 1D i siatki 2D.


Instalacja

pip install experttui

Wymagania: Python ≥ 3.8 · colorama · readchar


Szybki start

from experttui import menu_1D

menu_1D([
    "Wybierz opcję",
    ("Nowa gra",   lambda: print("Start!")),
    ("Ustawienia", lambda: print("Otwieranie…")),
    ("Wyjście",    lambda: quit()),
])

Typy elementów

Typ Format Opis
Etykieta "tekst" Nieaktywna linia tekstowa
Akcja (tupla) ("etykieta", fn) Wywołuje fn po Enter
Akcja (słownik) {"text": …, "function": fn} To samo, z dodatkowymi opcjami
Selektor {"type": "selector", "options": […], "index": 0} ←→ zmienia wartość
Input {"type": "input", "value": "", "placeholder": "…"} Enter otwiera edytor
Checkbox {"type": "checkbox", "checked": False} Enter przełącza
Separator {"type": "hr"} Pozioma linia łącząca się z ramką

menu_1D — Parametry

menu_1D(
    items,
    *,
    color               = "YELLOW",   # kolor zaznaczenia (nazwa colorama)
    selection_type      = "left_arrow",# "left_arrow" | "two_arrows"
    moving              = "both",     # "both" | "arrows" | "wsad"
    border              = False,      # False | "single"|"double"|"rounded"|"heavy"|"block"
    wait_after_function = False,      # czekaj na klawisz po akcji
    return_after_action = False,      # wyjdź z menu po akcji
    enter_selector      = False,      # Enter wymagany do edycji selectora
    width               = "auto",    # "auto" | int
    height              = "auto",    # "auto" | int (scroll)
    margin              = 0,          # margines zewnętrzny (wszystkie strony)
    margin_top/right/bottom/left = None,
    padding             = 0,          # padding wewnętrzny (wszystkie strony)
    padding_top/right/bottom/left = None,
)

menu_2D — Dodatkowe parametry

menu_2D(
    items,
    *,
    # … wszystkie parametry menu_1D …
    align    = True,   # wyrównaj kolumny do tej samej szerokości
    gap      = 2,      # odstęp między kolumnami (liczba znaków)
    gap_fill = False,  # False | nazwa stylu ramki — pionowy znak w odstępie
)

Struktura items:

menu_2D([
    "Nagłówek pełnej szerokości",
    {"type": "hr", "label": "Opcje"},
    [selektor_a, selektor_b],
    [pole_input],
    [("OK", fn), ("Anuluj", fn2)],
])

Style ramki

single    rounded   double    heavy     block
┌──────┐  ╭──────╮  ╔══════╗  ┏━━━━━━┓  ██████
│ item │  │ item │  ║ item ║  ┃ item ┃  █ item █
└──────┘  ╰──────╯  ╚══════╝  ┗━━━━━━┛  ██████

Klawisze

Akcja Klawisz
Góra / dół ↑↓ lub WS
Lewo / prawo (2D, wiele kolumn) ←→ lub AD
Zmiana selectora (1 kolumna) ←→ lub AD
Aktywacja selectora (enter_selector) Enter → ←→ zmieniają wartość → Enter/ESC
Otwarcie / potwierdzenie inputu Enter
Anulowanie inputu ESC
Wyjście z menu ESC

Licencja

MIT — patrz LICENSE.

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

experttui-1.0.0.tar.gz (25.8 kB view details)

Uploaded Source

Built Distribution

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

experttui-1.0.0-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

File details

Details for the file experttui-1.0.0.tar.gz.

File metadata

  • Download URL: experttui-1.0.0.tar.gz
  • Upload date:
  • Size: 25.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for experttui-1.0.0.tar.gz
Algorithm Hash digest
SHA256 99505e71081f26d61e9ac26232c0494321b0c45309da8d32e59ba7932c03250a
MD5 7c61503f11744e5f52ff1c674c41b7ac
BLAKE2b-256 38a0ba0bf646cf5802e6bcd32a8980f9d18be7377e577657744fcb7f1bc22265

See more details on using hashes here.

File details

Details for the file experttui-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: experttui-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 17.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for experttui-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e0722ef05d41d3dcbc11ab8edbbf24d388ebf7966acc67532f5803acc043ce8a
MD5 be0e0e088bee0a4fc15294a34c21e7d7
BLAKE2b-256 5319c068aeed62ec73f6ed868a8a5ad2ba587f7126568b713cb3f7bc15825ecf

See more details on using hashes here.

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