A Python package which creates simple interactive menus on the command line.
npx @tessl/cli install tessl/pypi-simple-term-menu@1.6.0A Python package which creates simple interactive menus on the command line. It provides customizable terminal menus with navigation, multi-selection, search functionality, and preview capabilities, supporting cross-platform terminal environments with automatic feature detection.
pip install simple-term-menufrom simple_term_menu import TerminalMenuFor exceptions:
from simple_term_menu import (
InvalidParameterCombinationError,
InvalidStyleError,
NoMenuEntriesError,
PreviewCommandFailedError,
UnknownMenuEntryError
)For utility functions and constants:
from simple_term_menu import (
get_locale,
wcswidth,
BoxDrawingCharacters,
# Default constants (optional, for customization)
DEFAULT_MENU_CURSOR,
DEFAULT_MENU_CURSOR_STYLE,
DEFAULT_QUIT_KEYS
)For CLI functions:
from simple_term_menu import main, parse_arguments, get_argumentparserfrom simple_term_menu import TerminalMenu
def main():
options = ["entry 1", "entry 2", "entry 3"]
terminal_menu = TerminalMenu(options)
menu_entry_index = terminal_menu.show()
if menu_entry_index is not None:
print(f"You have selected {options[menu_entry_index]}!")
else:
print("Menu was cancelled")
if __name__ == "__main__":
main()Multi-selection example:
from simple_term_menu import TerminalMenu
options = ["Option 1", "Option 2", "Option 3", "Option 4"]
terminal_menu = TerminalMenu(
options,
multi_select=True,
show_multi_select_hint=True
)
menu_entry_indices = terminal_menu.show()
if menu_entry_indices:
selected_options = [options[i] for i in menu_entry_indices]
print(f"Selected: {', '.join(selected_options)}")Error handling example:
from simple_term_menu import (
TerminalMenu,
NoMenuEntriesError,
InvalidParameterCombinationError,
PreviewCommandFailedError
)
try:
# Example of handling common errors
options = [] # Empty list will raise NoMenuEntriesError
terminal_menu = TerminalMenu(
options,
preview_command="invalid_command {}" # May cause PreviewCommandFailedError
)
result = terminal_menu.show()
except NoMenuEntriesError as e:
print(f"Error: No menu entries provided - {e}")
except InvalidParameterCombinationError as e:
print(f"Error: Invalid parameter combination - {e}")
except PreviewCommandFailedError as e:
print(f"Error: Preview command failed - {e}")
except KeyboardInterrupt:
print("Menu cancelled by user")
# Note: Set raise_error_on_interrupt=True to get this exception
except NotImplementedError as e:
print(f"Error: Platform not supported - {e}")
# Raised on Windows or when TERM environment variable is missingCreates interactive terminal menus with extensive customization options including cursor styles, keyboard shortcuts, multi-selection, search functionality, and preview windows.
class TerminalMenu:
def __init__(
self,
menu_entries: Iterable[str],
*,
accept_keys: Iterable[str] = ("enter",),
clear_menu_on_exit: bool = True,
clear_screen: bool = False,
cursor_index: Optional[int] = None,
cycle_cursor: bool = True,
exit_on_shortcut: bool = True,
menu_cursor: Optional[str] = "> ",
menu_cursor_style: Optional[Iterable[str]] = ("fg_red", "bold"),
menu_highlight_style: Optional[Iterable[str]] = ("standout",),
multi_select: bool = False,
multi_select_cursor: str = "[*] ",
multi_select_cursor_brackets_style: Optional[Iterable[str]] = ("fg_gray",),
multi_select_cursor_style: Optional[Iterable[str]] = ("fg_yellow", "bold"),
multi_select_empty_ok: bool = False,
multi_select_keys: Optional[Iterable[str]] = (" ", "tab"),
multi_select_select_on_accept: bool = True,
preselected_entries: Optional[Iterable[Union[str, int]]] = None,
preview_border: bool = True,
preview_command: Optional[Union[str, Callable[[str], str]]] = None,
preview_size: float = 0.25,
preview_title: str = "preview",
quit_keys: Iterable[str] = ("escape", "q", "ctrl-g"),
raise_error_on_interrupt: bool = False,
search_case_sensitive: bool = False,
search_highlight_style: Optional[Iterable[str]] = ("fg_black", "bg_yellow", "bold"),
search_key: Optional[str] = "/",
shortcut_brackets_highlight_style: Optional[Iterable[str]] = ("fg_gray",),
shortcut_key_highlight_style: Optional[Iterable[str]] = ("fg_blue",),
show_multi_select_hint: bool = False,
show_multi_select_hint_text: Optional[str] = None,
show_search_hint: bool = False,
show_search_hint_text: Optional[str] = None,
show_shortcut_hints: bool = False,
show_shortcut_hints_in_status_bar: bool = True,
skip_empty_entries: bool = False,
status_bar: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None,
status_bar_below_preview: bool = False,
status_bar_style: Optional[Iterable[str]] = ("fg_yellow", "bg_black"),
title: Optional[Union[str, Iterable[str]]] = None
):
"""
Create a terminal menu.
Parameters:
- menu_entries: List of menu entry strings to display
- accept_keys: Keys that accept the current selection
- clear_menu_on_exit: Whether to clear the menu when exiting
- clear_screen: Whether to clear the screen before showing the menu
- cursor_index: Initial cursor position (0-based index)
- cycle_cursor: Whether cursor wraps around at ends
- exit_on_shortcut: Whether to exit immediately on shortcut key
- menu_cursor: String to display as cursor
- menu_cursor_style: Style tuple for cursor (e.g., ("fg_red", "bold"))
- menu_highlight_style: Style tuple for highlighted entries
- multi_select: Enable multi-selection mode
- multi_select_cursor: Cursor for selected items in multi-select
- multi_select_cursor_brackets_style: Style for selection brackets
- multi_select_cursor_style: Style for multi-select cursor
- multi_select_empty_ok: Allow empty selection in multi-select
- multi_select_keys: Keys for toggling selection in multi-select
- multi_select_select_on_accept: Select current item on accept in multi-select
- preselected_entries: Pre-selected entries by string or index
- preview_border: Show border around preview area
- preview_command: Command or function to generate preview content
- preview_size: Preview area size as fraction of screen (0.0-1.0)
- preview_title: Title for preview area
- quit_keys: Keys that quit the menu without selection
- raise_error_on_interrupt: Raise exception on keyboard interrupt
- search_case_sensitive: Whether search is case sensitive
- search_highlight_style: Style for search highlighting
- search_key: Key to activate search mode
- shortcut_brackets_highlight_style: Style for shortcut brackets
- shortcut_key_highlight_style: Style for shortcut keys
- show_multi_select_hint: Show multi-select hint text
- show_multi_select_hint_text: Custom multi-select hint text
- show_search_hint: Show search hint text
- show_search_hint_text: Custom search hint text
- show_shortcut_hints: Show shortcut hints
- show_shortcut_hints_in_status_bar: Show shortcuts in status bar
- skip_empty_entries: Skip empty entries in navigation
- status_bar: Status bar content (string, list, or callable)
- status_bar_below_preview: Position status bar below preview
- status_bar_style: Style for status bar
- title: Menu title (string or list of strings)
"""
def show(self) -> Optional[Union[int, Tuple[int, ...]]]:
"""
Display the menu and wait for user interaction.
Returns:
- For single-select: Selected menu entry index (int) or None if cancelled
- For multi-select: Tuple of selected indices or None if cancelled
"""Access information about the last menu interaction, including selected entries and the key used to accept the selection.
@property
def chosen_accept_key(self) -> Optional[str]:
"""Key used to accept the last selection."""
@property
def chosen_menu_entry(self) -> Optional[str]:
"""Text of the last chosen menu entry (single-select)."""
@property
def chosen_menu_entries(self) -> Optional[Tuple[str, ...]]:
"""Texts of the last chosen menu entries (multi-select)."""
@property
def chosen_menu_index(self) -> Optional[int]:
"""Index of the last chosen menu entry (single-select)."""
@property
def chosen_menu_indices(self) -> Optional[Tuple[int, ...]]:
"""Indices of the last chosen menu entries (multi-select)."""Custom exceptions for various error conditions that may occur during menu creation and operation.
class InvalidParameterCombinationError(Exception):
"""Raised when incompatible parameters are used together."""
class InvalidStyleError(Exception):
"""Raised when an invalid style is specified."""
class NoMenuEntriesError(Exception):
"""Raised when no menu entries are provided."""
class PreviewCommandFailedError(Exception):
"""Raised when a preview command fails to execute."""
class UnknownMenuEntryError(Exception):
"""Raised when referencing a non-existent menu entry."""Provides platform-appropriate box drawing characters for terminal UIs. Automatically detects UTF-8 support and uses Unicode or ASCII characters accordingly.
class BoxDrawingCharacters:
"""
Box drawing characters that adapt to terminal capabilities.
Uses Unicode box characters (─│┌┐└┘) for UTF-8 locales,
falls back to ASCII characters (-|+++) for other locales.
"""
horizontal: str # "─" or "-"
vertical: str # "│" or "|"
upper_left: str # "┌" or "+"
upper_right: str # "┐" or "+"
lower_left: str # "└" or "+"
lower_right: str # "┘" or "+"Helper functions for locale handling and text width calculation, useful for custom terminal applications.
def get_locale() -> str:
"""
Get the current system locale.
Returns:
Current locale string
"""
def wcswidth(text: str) -> int:
"""
Calculate the width of text considering wide characters.
Parameters:
- text: Text string to measure
Returns:
Width of the text in terminal columns
"""Functions for using simple-term-menu as a command-line tool, allowing integration with shell scripts and other tools.
def main() -> None:
"""
Main entry point for command-line usage.
Parses command-line arguments and creates a terminal menu.
Available as 'simple-term-menu' console script.
Example usage:
simple-term-menu "Option 1" "Option 2" "Option 3"
simple-term-menu -m "Item 1" "Item 2" "Item 3" # Multi-select
echo -e "Choice A\\nChoice B\\nChoice C" | simple-term-menu
"""
def parse_arguments() -> AttributeDict:
"""
Parse command-line arguments for the CLI interface.
Returns:
Parsed arguments as AttributeDict
"""
def get_argumentparser() -> argparse.ArgumentParser:
"""
Create and return the argument parser for CLI usage.
Returns:
Configured ArgumentParser instance
"""The command-line interface supports extensive configuration through these arguments:
Navigation and Display:
-s, --case-sensitive: Make searches case sensitive-X, --no-clear-menu-on-exit: Do not clear the menu on exit-l, --clear-screen: Clear the screen before showing menu-C, --no-cycle: Do not cycle the menu selection-E, --no-exit-on-shortcut: Do not exit on shortcut keys-i, --cursor-index N: Initially selected item index (default: 0)--cursor STR: Menu cursor string (default: "> ")Styling:
--cursor-style STYLES: Cursor style as comma-separated list (default: "fg_red,bold")--highlight-style STYLES: Selected entry style (default: "standout")--search-highlight-style STYLES: Search highlight style (default: "fg_black,bg_yellow,bold")Multi-Selection:
-m, --multi-select: Enable multi-selection mode (implies --stdout)--multi-select-cursor STR: Multi-select cursor (default: "[*] ")--multi-select-cursor-style STYLES: Multi-select cursor style (default: "fg_yellow,bold")--multi-select-cursor-brackets-style STYLES: Bracket style (default: "fg_gray")--multi-select-keys KEYS: Toggle keys (default: " ,tab")--multi-select-no-select-on-accept: Don't select current item on accept--multi-select-empty-ok: Allow empty multi-selectionPreview:
-p, --preview COMMAND: Preview command for entries--preview-size FLOAT: Preview area size as fraction (default: 0.25)--preview-title STR: Preview area title (default: "preview")--no-preview-border: Disable preview borderSearch:
--search-key KEY: Search activation key (default: "/")--no-search-key: Activate search on any letter keyShortcuts and Hints:
--shortcut-key-highlight-style STYLES: Shortcut key style (default: "fg_blue")--shortcut-brackets-highlight-style STYLES: Shortcut bracket style (default: "fg_gray")--show-shortcut-hints: Show shortcut hints--show-multi-select-hint: Show multi-select hint--show-search-hint: Show search hintStatus Bar:
--status-bar STR: Status bar content--status-bar-below-preview: Position status bar below preview--status-bar-style STYLES: Status bar style (default: "fg_yellow,bg_black")Output:
--stdout: Output to stdout instead of stderr--title STR: Menu title-V, --version: Show version and exitThe package supports extensive styling through tuples of style keywords:
fg_black, fg_blue, fg_cyan, fg_gray, fg_green, fg_purple, fg_red, fg_yellowbg_black, bg_blue, bg_cyan, bg_gray, bg_green, bg_purple, bg_red, bg_yellowbold, dim, italic, underline, blink, reverse, strikethroughstandout (platform-specific highlighting)Example styling:
menu = TerminalMenu(
options,
menu_cursor_style=("fg_red", "bold"),
menu_highlight_style=("bg_blue", "fg_white"),
search_highlight_style=("bg_yellow", "fg_black", "bold")
)j/k (vim-style), Ctrl-n/Ctrl-p (emacs-style), Page Up/Down, Ctrl-f/Ctrl-b/ (activate search), Escape (exit search)q, Ctrl-g, Ctrl-cMenu entries can include shortcut keys for quick selection. Enclose the shortcut character in brackets:
options = ["[a]pple", "[b]anana", "[c]herry"]
menu = TerminalMenu(options)Built-in search functionality allows filtering menu entries:
/ (like vim, less)search_key=None to activate search on any letter keysearch_case_sensitive parameterKeys can be customized through the various *_keys parameters. Keys are specified as strings:
"a", "/", " " (space)"enter", "escape", "tab", "space""ctrl-g", "ctrl-c""alt-a", "alt-x"from typing import Iterable, Optional, Union, Tuple, Callable, Any, Dict
# Menu entry types
MenuEntry = str
MenuEntries = Iterable[MenuEntry]
# Selection result types
SingleSelection = Optional[int]
MultiSelection = Optional[Tuple[int, ...]]
Selection = Union[SingleSelection, MultiSelection]
# Style specification
StyleTuple = Tuple[str, ...]
Style = Optional[StyleTuple]
# Preview command types
PreviewCommand = Union[str, Callable[[str], str]]
# Status bar types
StatusBar = Union[str, Iterable[str], Callable[[str], str]]
# Title types
Title = Union[str, Iterable[str]]
# CLI argument dictionary
class AttributeDict(dict):
"""
Dictionary that allows attribute-style access to keys.
Used by parse_arguments() to return CLI arguments that can be
accessed as attributes (args.multi_select) or dict keys (args['multi_select']).
"""
def __getattr__(self, attr: str) -> Any: ...
def __setattr__(self, attr: str, value: Any) -> None: ...
# Key specification types
KeySpec = str # Key names like "enter", "escape", "ctrl-g", "alt-x"
KeySequence = Iterable[KeySpec]
# Preselection types
PreselectedEntry = Union[str, int] # Menu entry by text or index
PreselectedEntries = Iterable[PreselectedEntry]__author__: str = "Ingo Meyer"
__email__: str = "i.meyer@fz-juelich.de"
__copyright__: str = "Copyright © 2021 Forschungszentrum Jülich GmbH. All rights reserved."
__license__: str = "MIT"
__version_info__: Tuple[int, int, int] = (1, 6, 6)
__version__: str = "1.6.6"Default parameter values used by TerminalMenu constructor:
# Navigation and behavior defaults
DEFAULT_ACCEPT_KEYS: Tuple[str, ...] = ("enter",)
DEFAULT_QUIT_KEYS: Tuple[str, ...] = ("escape", "q", "ctrl-g")
DEFAULT_CYCLE_CURSOR: bool = True
DEFAULT_EXIT_ON_SHORTCUT: bool = True
# Display defaults
DEFAULT_CLEAR_MENU_ON_EXIT: bool = True
DEFAULT_CLEAR_SCREEN: bool = False
DEFAULT_MENU_CURSOR: str = "> "
DEFAULT_MENU_CURSOR_STYLE: Tuple[str, ...] = ("fg_red", "bold")
DEFAULT_MENU_HIGHLIGHT_STYLE: Tuple[str, ...] = ("standout",)
# Multi-selection defaults
DEFAULT_MULTI_SELECT: bool = False
DEFAULT_MULTI_SELECT_CURSOR: str = "[*] "
DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE: Tuple[str, ...] = ("fg_gray",)
DEFAULT_MULTI_SELECT_CURSOR_STYLE: Tuple[str, ...] = ("fg_yellow", "bold")
DEFAULT_MULTI_SELECT_KEYS: Tuple[str, ...] = (" ", "tab")
DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT: bool = True
# Search defaults
DEFAULT_SEARCH_CASE_SENSITIVE: bool = False
DEFAULT_SEARCH_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_black", "bg_yellow", "bold")
DEFAULT_SEARCH_KEY: str = "/"
# Preview defaults
DEFAULT_PREVIEW_BORDER: bool = True
DEFAULT_PREVIEW_SIZE: float = 0.25
DEFAULT_PREVIEW_TITLE: str = "preview"
# Shortcut and hint defaults
DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_gray",)
DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_blue",)
DEFAULT_SHOW_MULTI_SELECT_HINT: bool = False
DEFAULT_SHOW_SEARCH_HINT: bool = False
DEFAULT_SHOW_SHORTCUT_HINTS: bool = False
DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR: bool = True
# Status bar defaults
DEFAULT_STATUS_BAR_BELOW_PREVIEW: bool = False
DEFAULT_STATUS_BAR_STYLE: Tuple[str, ...] = ("fg_yellow", "bg_black")
# Layout constraints
MIN_VISIBLE_MENU_ENTRIES_COUNT: int = 3