A simple yet powerful Python library for creating interactive CLI menus
Project description
pymenu-cli
A Python library for creating interactive terminal user interface (TUI) menus from JSON configuration files. Define your menu structure in JSON, write your actions in Python, and get a full-featured TUI application with keyboard navigation, mouse support, global search, theming, and more.
v2.0 — Complete rewrite with a modern TUI powered by Textual. The classic numbered-menu mode is still available via
--classic.
Features
- Full TUI Application — Sidebar navigation, breadcrumb trail, action output panel, footer with keybinding hints
- Keyboard & Mouse Navigation — Arrow keys, vim keys (
j/k), Enter to select, mouse click support - Global Search — Press
/to search across all menus and submenus instantly - Dark & Light Themes — Toggle with
Tat runtime, or start with--theme light - 5 Banner Styles — Rich text, box-drawing, FIGlet, gradient, and emoji
- Classic Mode — Original v1 numbered-menu experience via
--classicflag - JSON-Driven — Define menus, submenus, colors, and banners in simple JSON
- Backward Compatible — Existing v1 JSON files and action modules work unchanged
- Python 3.9+ — Modern Python with
pyproject.tomlpackaging
Screenshots
Submenu Navigation
Action Output
Global Search
Light Theme
Installation
pip install pymenu-cli
Quick Start
1. Create a menu JSON file (menu.json)
{
"banner": {
"title": "My App",
"style": "gradient",
"colors": ["red", "magenta"],
"subtitle": "v1.0"
},
"title": "Main Menu",
"items": [
{
"title": "Say Hello",
"action": "say_hello"
},
{
"title": "Settings",
"submenu": {
"title": "Settings",
"items": [
{
"title": "Show Config",
"action": "show_config"
}
]
}
}
]
}
2. Create an actions file (actions.py)
import platform
def say_hello():
print("Hello from pymenu-cli!")
print(f"Running on {platform.system()} {platform.machine()}")
def show_config():
print("Config: default settings loaded")
3. Run it
# Launch the TUI (default)
pymenu-cli --menu menu.json --actions actions.py
# Classic numbered-menu mode
pymenu-cli --menu menu.json --actions actions.py --classic
# Start with light theme
pymenu-cli --menu menu.json --actions actions.py --theme light
Or use the Python API
from pymenu_cli.pymenu import load_menu
menu = load_menu("menu.json", "actions.py")
menu.display() # TUI mode (default)
menu.display(classic=True) # Classic mode
menu.display(theme="light") # Light theme
Keyboard Shortcuts
| Key | Action |
|---|---|
↑ / k |
Move cursor up |
↓ / j |
Move cursor down |
Enter |
Select item (enter submenu or run action) |
Esc |
Go back / clear search |
Backspace |
Go back to parent menu |
/ |
Focus global search bar |
T |
Toggle dark/light theme |
Q |
Quit application |
Mouse: Click menu items, sidebar nodes, or scroll the output panel.
Menu JSON Format
Full Structure
{
"banner": {
"title": "App Name",
"style": "gradient",
"colors": ["red", "blue"],
"subtitle": "Optional subtitle"
},
"title": "Main Menu",
"color": {
"text": "yellow",
"background": "light_blue"
},
"items": [
{
"title": "Action Item",
"action": "function_name",
"color": { "text": "green", "background": "black" }
},
{
"title": "Submenu Item",
"submenu": {
"title": "Submenu Title",
"items": [ ... ]
}
}
]
}
Properties
| Property | Required | Description |
|---|---|---|
title |
Yes | Menu or item title |
items |
Yes | Array of menu items |
banner |
No | Header banner configuration |
color |
No | Text and background color for the title |
action |
No | Name of the Python function to execute |
submenu |
No | Nested submenu (same structure as root) |
Banner Styles
All banner styles are rendered using Rich/Textual — no extra dependencies needed (except pyfiglet for FIGlet style).
Rich Text ("style": "rich")
Clean, bold title with optional subtitle.
"banner": { "title": "My App", "style": "rich", "subtitle": "v1.0" }
Box Drawing ("style": "box")
Unicode box frame around the title.
"banner": { "title": "My App", "style": "box" }
╔════════════════╗
║ My App ║
╚════════════════╝
FIGlet ("style": "figlet")
Large ASCII art text. Supports font selection via the font parameter. See pyfiglet fonts.
"banner": { "title": "My App", "style": "figlet", "font": "slant" }
__ ___ ___
/ |/ /__ __ / | ____ ____
/ /|_/ / / / / / / /| | / __ \ / __ \
/ / / / /_/ / / ___ |/ /_/ // /_/ /
/_/ /_/\__, / /_/ |_/ .___// .___/
/____/ /_/ /_/
Gradient ("style": "gradient")
Color gradient across the title text.
"banner": { "title": "My App", "style": "gradient", "colors": ["red", "blue"] }
Emoji ("style": "emoji")
Emoji icon paired with styled text.
"banner": { "title": "My App", "style": "emoji", "icon": "🚀" }
Backward Compatibility
Existing v1 format with "font" (no "style") automatically maps to FIGlet:
"banner": { "title": "My App", "font": "standard" }
Color Options
Colors work in classic mode for text and background styling. In TUI mode, they act as overrides on top of the active theme.
Available colors: RED, LIGHT_RED, BLUE, LIGHT_BLUE, YELLOW, LIGHT_YELLOW, GREEN, LIGHT_GREEN, CYAN, LIGHT_CYAN, MAGENTA, LIGHT_MAGENTA, BLACK, LIGHT_BLACK, WHITE, LIGHT_WHITE
{
"title": "Highlighted Item",
"color": { "text": "yellow", "background": "blue" },
"action": "my_function"
}
Actions File
The actions file is a plain Python module. Each function name corresponds to an "action" value in the JSON. Functions receive no arguments. In TUI mode, print() output is captured and displayed in the output panel.
import os
import platform
def show_system_info():
print(f"OS: {platform.system()} {platform.release()}")
print(f"Python: {platform.python_version()}")
print(f"CWD: {os.getcwd()}")
def create_file():
with open("output.txt", "w") as f:
f.write("Hello from pymenu-cli!\n")
print("✓ Created output.txt")
def risky_action():
# Exceptions are caught and shown in the output panel
raise ValueError("Something went wrong!")
Global Search
Press / to activate the search bar. It searches across all menus and submenus, not just the current view. Results show the full path to each matching item:
❯ General Settings ⚡ (Main Menu › Tools › Settings)
Advanced Settings ⚡ (Main Menu › Tools › Settings)
Generate Password ⚡ (Main Menu › Tools)
Press Enter to navigate to the item and execute it. Press Esc to clear the search.
Theming
Two built-in themes: dark (default) and light.
- Toggle at runtime: press
T - Start with a theme:
--theme light - Via API:
menu.display(theme="light")
Themes are implemented as Textual CSS files in pymenu_cli/themes/. Custom themes can be added by creating new .tcss files.
Classic Mode
The original v1 numbered-menu experience is preserved:
pymenu-cli --menu menu.json --actions actions.py --classic
Main Menu
1. File
2. Edit
3. Tools
4. Help
B. Back
X. Exit
Enter your choice:
CLI Reference
pymenu-cli [-h] [-m MENU] [-a ACTIONS] [--classic] [--theme {dark,light}]
Options:
-m, --menu MENU Path to the menu JSON file
-a, --actions ACTIONS Path to the actions Python file
--classic Use classic numbered-menu mode
--theme {dark,light} Color theme for TUI mode (default: dark)
-h, --help Show help message
Examples
The examples/ directory contains a full working example with real actions:
# Clone and run
git clone https://github.com/moraneus/pymenu-cli.git
cd pymenu-cli
# Install
pip install -e ".[dev]"
# Run the TUI example
pymenu-cli --menu examples/menus/colored_menu.json --actions examples/actions/actions.py
# Run in classic mode
pymenu-cli --menu examples/menus/colored_menu.json --actions examples/actions/actions.py --classic
# Run with light theme
pymenu-cli --menu examples/menus/colored_menu.json --actions examples/actions/actions.py --theme light
The example includes:
- File operations — Create files, list workspace, export as text/JSON/XML
- Edit operations — Clipboard cut/copy/paste, workspace stats
- Tools — System info, password generator, plugin manager, backup/restore
- Help — User guide with keybindings, FAQ, about page
Migration from v1
pymenu-cli v2.0 is fully backward compatible. Your existing JSON files and action modules work without changes:
| What | v1 | v2 |
|---|---|---|
| Default display | Numbered menu (input()) |
Full TUI (Textual) |
| Old display mode | — | --classic flag |
| Dependencies | colorama, art |
textual, pyfiglet, colorama |
| Python | 3.8+ | 3.9+ |
| Packaging | setup.py |
pyproject.toml |
| Banner format | "font": "standard" |
Still works (auto-detected as FIGlet) |
| New banner styles | — | rich, box, gradient, emoji |
| Theming | — | dark / light with T toggle |
| Search | — | Global search with / |
Breaking changes: Python 3.8 is no longer supported (EOL since Oct 2024).
Development
# Clone
git clone https://github.com/moraneus/pymenu-cli.git
cd pymenu-cli
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run linter
pylint pymenu_cli/
# Run with Textual dev console (live CSS reload)
textual run --dev pymenu_cli.app:MenuApp
Project Structure
pymenu_cli/
├── __init__.py # Public API exports
├── app.py # MenuApp — main Textual TUI application
├── banner.py # Banner rendering (5 styles)
├── classic.py # Classic v1 numbered-menu mode
├── pymenu.py # CLI entry point, JSON/module loading
├── models/
│ ├── menu.py # Menu class
│ └── menu_item.py # MenuItem class
├── widgets/
│ ├── sidebar.py # Menu tree sidebar
│ ├── menu_list.py # Navigable menu items panel
│ ├── breadcrumb.py # Breadcrumb navigation bar
│ ├── search_bar.py # Global search/filter bar
│ └── output_panel.py # Action stdout/stderr panel
└── themes/
├── dark.tcss # Dark theme (default)
└── light.tcss # Light theme
License
This project is licensed under the MIT License.
Author
Created by Moraneus.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters