0
# Simple Terminal Menu
1
2
A 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.
3
4
## Package Information
5
6
- **Package Name**: simple-term-menu
7
- **Language**: Python
8
- **Installation**: `pip install simple-term-menu`
9
- **Supported Platforms**: Linux, macOS
10
- **Python Version**: 3.5+
11
12
## Core Imports
13
14
```python
15
from simple_term_menu import TerminalMenu
16
```
17
18
For exceptions:
19
```python
20
from simple_term_menu import (
21
InvalidParameterCombinationError,
22
InvalidStyleError,
23
NoMenuEntriesError,
24
PreviewCommandFailedError,
25
UnknownMenuEntryError
26
)
27
```
28
29
For utility functions and constants:
30
```python
31
from simple_term_menu import (
32
get_locale,
33
wcswidth,
34
BoxDrawingCharacters,
35
# Default constants (optional, for customization)
36
DEFAULT_MENU_CURSOR,
37
DEFAULT_MENU_CURSOR_STYLE,
38
DEFAULT_QUIT_KEYS
39
)
40
```
41
42
For CLI functions:
43
```python
44
from simple_term_menu import main, parse_arguments, get_argumentparser
45
```
46
47
## Basic Usage
48
49
```python
50
from simple_term_menu import TerminalMenu
51
52
def main():
53
options = ["entry 1", "entry 2", "entry 3"]
54
terminal_menu = TerminalMenu(options)
55
menu_entry_index = terminal_menu.show()
56
57
if menu_entry_index is not None:
58
print(f"You have selected {options[menu_entry_index]}!")
59
else:
60
print("Menu was cancelled")
61
62
if __name__ == "__main__":
63
main()
64
```
65
66
Multi-selection example:
67
```python
68
from simple_term_menu import TerminalMenu
69
70
options = ["Option 1", "Option 2", "Option 3", "Option 4"]
71
terminal_menu = TerminalMenu(
72
options,
73
multi_select=True,
74
show_multi_select_hint=True
75
)
76
menu_entry_indices = terminal_menu.show()
77
78
if menu_entry_indices:
79
selected_options = [options[i] for i in menu_entry_indices]
80
print(f"Selected: {', '.join(selected_options)}")
81
```
82
83
Error handling example:
84
```python
85
from simple_term_menu import (
86
TerminalMenu,
87
NoMenuEntriesError,
88
InvalidParameterCombinationError,
89
PreviewCommandFailedError
90
)
91
92
try:
93
# Example of handling common errors
94
options = [] # Empty list will raise NoMenuEntriesError
95
terminal_menu = TerminalMenu(
96
options,
97
preview_command="invalid_command {}" # May cause PreviewCommandFailedError
98
)
99
result = terminal_menu.show()
100
101
except NoMenuEntriesError as e:
102
print(f"Error: No menu entries provided - {e}")
103
104
except InvalidParameterCombinationError as e:
105
print(f"Error: Invalid parameter combination - {e}")
106
107
except PreviewCommandFailedError as e:
108
print(f"Error: Preview command failed - {e}")
109
110
except KeyboardInterrupt:
111
print("Menu cancelled by user")
112
# Note: Set raise_error_on_interrupt=True to get this exception
113
114
except NotImplementedError as e:
115
print(f"Error: Platform not supported - {e}")
116
# Raised on Windows or when TERM environment variable is missing
117
```
118
119
## Capabilities
120
121
### Terminal Menu Creation
122
123
Creates interactive terminal menus with extensive customization options including cursor styles, keyboard shortcuts, multi-selection, search functionality, and preview windows.
124
125
```python { .api }
126
class TerminalMenu:
127
def __init__(
128
self,
129
menu_entries: Iterable[str],
130
*,
131
accept_keys: Iterable[str] = ("enter",),
132
clear_menu_on_exit: bool = True,
133
clear_screen: bool = False,
134
cursor_index: Optional[int] = None,
135
cycle_cursor: bool = True,
136
exit_on_shortcut: bool = True,
137
menu_cursor: Optional[str] = "> ",
138
menu_cursor_style: Optional[Iterable[str]] = ("fg_red", "bold"),
139
menu_highlight_style: Optional[Iterable[str]] = ("standout",),
140
multi_select: bool = False,
141
multi_select_cursor: str = "[*] ",
142
multi_select_cursor_brackets_style: Optional[Iterable[str]] = ("fg_gray",),
143
multi_select_cursor_style: Optional[Iterable[str]] = ("fg_yellow", "bold"),
144
multi_select_empty_ok: bool = False,
145
multi_select_keys: Optional[Iterable[str]] = (" ", "tab"),
146
multi_select_select_on_accept: bool = True,
147
preselected_entries: Optional[Iterable[Union[str, int]]] = None,
148
preview_border: bool = True,
149
preview_command: Optional[Union[str, Callable[[str], str]]] = None,
150
preview_size: float = 0.25,
151
preview_title: str = "preview",
152
quit_keys: Iterable[str] = ("escape", "q", "ctrl-g"),
153
raise_error_on_interrupt: bool = False,
154
search_case_sensitive: bool = False,
155
search_highlight_style: Optional[Iterable[str]] = ("fg_black", "bg_yellow", "bold"),
156
search_key: Optional[str] = "/",
157
shortcut_brackets_highlight_style: Optional[Iterable[str]] = ("fg_gray",),
158
shortcut_key_highlight_style: Optional[Iterable[str]] = ("fg_blue",),
159
show_multi_select_hint: bool = False,
160
show_multi_select_hint_text: Optional[str] = None,
161
show_search_hint: bool = False,
162
show_search_hint_text: Optional[str] = None,
163
show_shortcut_hints: bool = False,
164
show_shortcut_hints_in_status_bar: bool = True,
165
skip_empty_entries: bool = False,
166
status_bar: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None,
167
status_bar_below_preview: bool = False,
168
status_bar_style: Optional[Iterable[str]] = ("fg_yellow", "bg_black"),
169
title: Optional[Union[str, Iterable[str]]] = None
170
):
171
"""
172
Create a terminal menu.
173
174
Parameters:
175
- menu_entries: List of menu entry strings to display
176
- accept_keys: Keys that accept the current selection
177
- clear_menu_on_exit: Whether to clear the menu when exiting
178
- clear_screen: Whether to clear the screen before showing the menu
179
- cursor_index: Initial cursor position (0-based index)
180
- cycle_cursor: Whether cursor wraps around at ends
181
- exit_on_shortcut: Whether to exit immediately on shortcut key
182
- menu_cursor: String to display as cursor
183
- menu_cursor_style: Style tuple for cursor (e.g., ("fg_red", "bold"))
184
- menu_highlight_style: Style tuple for highlighted entries
185
- multi_select: Enable multi-selection mode
186
- multi_select_cursor: Cursor for selected items in multi-select
187
- multi_select_cursor_brackets_style: Style for selection brackets
188
- multi_select_cursor_style: Style for multi-select cursor
189
- multi_select_empty_ok: Allow empty selection in multi-select
190
- multi_select_keys: Keys for toggling selection in multi-select
191
- multi_select_select_on_accept: Select current item on accept in multi-select
192
- preselected_entries: Pre-selected entries by string or index
193
- preview_border: Show border around preview area
194
- preview_command: Command or function to generate preview content
195
- preview_size: Preview area size as fraction of screen (0.0-1.0)
196
- preview_title: Title for preview area
197
- quit_keys: Keys that quit the menu without selection
198
- raise_error_on_interrupt: Raise exception on keyboard interrupt
199
- search_case_sensitive: Whether search is case sensitive
200
- search_highlight_style: Style for search highlighting
201
- search_key: Key to activate search mode
202
- shortcut_brackets_highlight_style: Style for shortcut brackets
203
- shortcut_key_highlight_style: Style for shortcut keys
204
- show_multi_select_hint: Show multi-select hint text
205
- show_multi_select_hint_text: Custom multi-select hint text
206
- show_search_hint: Show search hint text
207
- show_search_hint_text: Custom search hint text
208
- show_shortcut_hints: Show shortcut hints
209
- show_shortcut_hints_in_status_bar: Show shortcuts in status bar
210
- skip_empty_entries: Skip empty entries in navigation
211
- status_bar: Status bar content (string, list, or callable)
212
- status_bar_below_preview: Position status bar below preview
213
- status_bar_style: Style for status bar
214
- title: Menu title (string or list of strings)
215
"""
216
217
def show(self) -> Optional[Union[int, Tuple[int, ...]]]:
218
"""
219
Display the menu and wait for user interaction.
220
221
Returns:
222
- For single-select: Selected menu entry index (int) or None if cancelled
223
- For multi-select: Tuple of selected indices or None if cancelled
224
"""
225
```
226
227
### Menu Interaction Properties
228
229
Access information about the last menu interaction, including selected entries and the key used to accept the selection.
230
231
```python { .api }
232
@property
233
def chosen_accept_key(self) -> Optional[str]:
234
"""Key used to accept the last selection."""
235
236
@property
237
def chosen_menu_entry(self) -> Optional[str]:
238
"""Text of the last chosen menu entry (single-select)."""
239
240
@property
241
def chosen_menu_entries(self) -> Optional[Tuple[str, ...]]:
242
"""Texts of the last chosen menu entries (multi-select)."""
243
244
@property
245
def chosen_menu_index(self) -> Optional[int]:
246
"""Index of the last chosen menu entry (single-select)."""
247
248
@property
249
def chosen_menu_indices(self) -> Optional[Tuple[int, ...]]:
250
"""Indices of the last chosen menu entries (multi-select)."""
251
```
252
253
### Exception Handling
254
255
Custom exceptions for various error conditions that may occur during menu creation and operation.
256
257
```python { .api }
258
class InvalidParameterCombinationError(Exception):
259
"""Raised when incompatible parameters are used together."""
260
261
class InvalidStyleError(Exception):
262
"""Raised when an invalid style is specified."""
263
264
class NoMenuEntriesError(Exception):
265
"""Raised when no menu entries are provided."""
266
267
class PreviewCommandFailedError(Exception):
268
"""Raised when a preview command fails to execute."""
269
270
class UnknownMenuEntryError(Exception):
271
"""Raised when referencing a non-existent menu entry."""
272
```
273
274
### Box Drawing Characters
275
276
Provides platform-appropriate box drawing characters for terminal UIs. Automatically detects UTF-8 support and uses Unicode or ASCII characters accordingly.
277
278
```python { .api }
279
class BoxDrawingCharacters:
280
"""
281
Box drawing characters that adapt to terminal capabilities.
282
283
Uses Unicode box characters (─│┌┐└┘) for UTF-8 locales,
284
falls back to ASCII characters (-|+++) for other locales.
285
"""
286
horizontal: str # "─" or "-"
287
vertical: str # "│" or "|"
288
upper_left: str # "┌" or "+"
289
upper_right: str # "┐" or "+"
290
lower_left: str # "└" or "+"
291
lower_right: str # "┘" or "+"
292
```
293
294
### Utility Functions
295
296
Helper functions for locale handling and text width calculation, useful for custom terminal applications.
297
298
```python { .api }
299
def get_locale() -> str:
300
"""
301
Get the current system locale.
302
303
Returns:
304
Current locale string
305
"""
306
307
def wcswidth(text: str) -> int:
308
"""
309
Calculate the width of text considering wide characters.
310
311
Parameters:
312
- text: Text string to measure
313
314
Returns:
315
Width of the text in terminal columns
316
"""
317
```
318
319
### Command Line Interface
320
321
Functions for using simple-term-menu as a command-line tool, allowing integration with shell scripts and other tools.
322
323
```python { .api }
324
def main() -> None:
325
"""
326
Main entry point for command-line usage.
327
328
Parses command-line arguments and creates a terminal menu.
329
Available as 'simple-term-menu' console script.
330
331
Example usage:
332
simple-term-menu "Option 1" "Option 2" "Option 3"
333
simple-term-menu -m "Item 1" "Item 2" "Item 3" # Multi-select
334
echo -e "Choice A\\nChoice B\\nChoice C" | simple-term-menu
335
"""
336
337
def parse_arguments() -> AttributeDict:
338
"""
339
Parse command-line arguments for the CLI interface.
340
341
Returns:
342
Parsed arguments as AttributeDict
343
"""
344
345
def get_argumentparser() -> argparse.ArgumentParser:
346
"""
347
Create and return the argument parser for CLI usage.
348
349
Returns:
350
Configured ArgumentParser instance
351
"""
352
```
353
354
#### CLI Arguments
355
356
The command-line interface supports extensive configuration through these arguments:
357
358
**Navigation and Display:**
359
- `-s, --case-sensitive`: Make searches case sensitive
360
- `-X, --no-clear-menu-on-exit`: Do not clear the menu on exit
361
- `-l, --clear-screen`: Clear the screen before showing menu
362
- `-C, --no-cycle`: Do not cycle the menu selection
363
- `-E, --no-exit-on-shortcut`: Do not exit on shortcut keys
364
- `-i, --cursor-index N`: Initially selected item index (default: 0)
365
- `--cursor STR`: Menu cursor string (default: "> ")
366
367
**Styling:**
368
- `--cursor-style STYLES`: Cursor style as comma-separated list (default: "fg_red,bold")
369
- `--highlight-style STYLES`: Selected entry style (default: "standout")
370
- `--search-highlight-style STYLES`: Search highlight style (default: "fg_black,bg_yellow,bold")
371
372
**Multi-Selection:**
373
- `-m, --multi-select`: Enable multi-selection mode (implies --stdout)
374
- `--multi-select-cursor STR`: Multi-select cursor (default: "[*] ")
375
- `--multi-select-cursor-style STYLES`: Multi-select cursor style (default: "fg_yellow,bold")
376
- `--multi-select-cursor-brackets-style STYLES`: Bracket style (default: "fg_gray")
377
- `--multi-select-keys KEYS`: Toggle keys (default: " ,tab")
378
- `--multi-select-no-select-on-accept`: Don't select current item on accept
379
- `--multi-select-empty-ok`: Allow empty multi-selection
380
381
**Preview:**
382
- `-p, --preview COMMAND`: Preview command for entries
383
- `--preview-size FLOAT`: Preview area size as fraction (default: 0.25)
384
- `--preview-title STR`: Preview area title (default: "preview")
385
- `--no-preview-border`: Disable preview border
386
387
**Search:**
388
- `--search-key KEY`: Search activation key (default: "/")
389
- `--no-search-key`: Activate search on any letter key
390
391
**Shortcuts and Hints:**
392
- `--shortcut-key-highlight-style STYLES`: Shortcut key style (default: "fg_blue")
393
- `--shortcut-brackets-highlight-style STYLES`: Shortcut bracket style (default: "fg_gray")
394
- `--show-shortcut-hints`: Show shortcut hints
395
- `--show-multi-select-hint`: Show multi-select hint
396
- `--show-search-hint`: Show search hint
397
398
**Status Bar:**
399
- `--status-bar STR`: Status bar content
400
- `--status-bar-below-preview`: Position status bar below preview
401
- `--status-bar-style STYLES`: Status bar style (default: "fg_yellow,bg_black")
402
403
**Output:**
404
- `--stdout`: Output to stdout instead of stderr
405
- `--title STR`: Menu title
406
- `-V, --version`: Show version and exit
407
408
## Styling Options
409
410
The package supports extensive styling through tuples of style keywords:
411
412
### Color Options
413
- **Foreground colors**: `fg_black`, `fg_blue`, `fg_cyan`, `fg_gray`, `fg_green`, `fg_purple`, `fg_red`, `fg_yellow`
414
- **Background colors**: `bg_black`, `bg_blue`, `bg_cyan`, `bg_gray`, `bg_green`, `bg_purple`, `bg_red`, `bg_yellow`
415
416
### Style Options
417
- **Text formatting**: `bold`, `dim`, `italic`, `underline`, `blink`, `reverse`, `strikethrough`
418
- **Special**: `standout` (platform-specific highlighting)
419
420
Example styling:
421
```python
422
menu = TerminalMenu(
423
options,
424
menu_cursor_style=("fg_red", "bold"),
425
menu_highlight_style=("bg_blue", "fg_white"),
426
search_highlight_style=("bg_yellow", "fg_black", "bold")
427
)
428
```
429
430
## Keyboard Navigation
431
432
### Default Key Bindings
433
- **Navigation**: Arrow keys, `j`/`k` (vim-style), `Ctrl-n`/`Ctrl-p` (emacs-style), Page Up/Down, `Ctrl-f`/`Ctrl-b`
434
- **Selection**: Enter (accept), Space/Tab (multi-select toggle)
435
- **Search**: `/` (activate search), Escape (exit search)
436
- **Exit**: Escape, `q`, `Ctrl-g`, `Ctrl-c`
437
438
### Shortcut Keys
439
Menu entries can include shortcut keys for quick selection. Enclose the shortcut character in brackets:
440
```python
441
options = ["[a]pple", "[b]anana", "[c]herry"]
442
menu = TerminalMenu(options)
443
```
444
445
### Search Feature
446
Built-in search functionality allows filtering menu entries:
447
- Default search key: `/` (like vim, less)
448
- Search supports regex patterns
449
- Set `search_key=None` to activate search on any letter key
450
- Case sensitivity controlled by `search_case_sensitive` parameter
451
452
### Custom Key Configuration
453
Keys can be customized through the various `*_keys` parameters. Keys are specified as strings:
454
- Single characters: `"a"`, `"/"`, `" "` (space)
455
- Special keys: `"enter"`, `"escape"`, `"tab"`, `"space"`
456
- Control combinations: `"ctrl-g"`, `"ctrl-c"`
457
- Alt combinations: `"alt-a"`, `"alt-x"`
458
459
## Types
460
461
```python { .api }
462
from typing import Iterable, Optional, Union, Tuple, Callable, Any, Dict
463
464
# Menu entry types
465
MenuEntry = str
466
MenuEntries = Iterable[MenuEntry]
467
468
# Selection result types
469
SingleSelection = Optional[int]
470
MultiSelection = Optional[Tuple[int, ...]]
471
Selection = Union[SingleSelection, MultiSelection]
472
473
# Style specification
474
StyleTuple = Tuple[str, ...]
475
Style = Optional[StyleTuple]
476
477
# Preview command types
478
PreviewCommand = Union[str, Callable[[str], str]]
479
480
# Status bar types
481
StatusBar = Union[str, Iterable[str], Callable[[str], str]]
482
483
# Title types
484
Title = Union[str, Iterable[str]]
485
486
# CLI argument dictionary
487
class AttributeDict(dict):
488
"""
489
Dictionary that allows attribute-style access to keys.
490
491
Used by parse_arguments() to return CLI arguments that can be
492
accessed as attributes (args.multi_select) or dict keys (args['multi_select']).
493
"""
494
def __getattr__(self, attr: str) -> Any: ...
495
def __setattr__(self, attr: str, value: Any) -> None: ...
496
497
# Key specification types
498
KeySpec = str # Key names like "enter", "escape", "ctrl-g", "alt-x"
499
KeySequence = Iterable[KeySpec]
500
501
# Preselection types
502
PreselectedEntry = Union[str, int] # Menu entry by text or index
503
PreselectedEntries = Iterable[PreselectedEntry]
504
```
505
506
## Module Constants
507
508
### Version and Package Information
509
510
```python { .api }
511
__author__: str = "Ingo Meyer"
512
__email__: str = "i.meyer@fz-juelich.de"
513
__copyright__: str = "Copyright © 2021 Forschungszentrum Jülich GmbH. All rights reserved."
514
__license__: str = "MIT"
515
__version_info__: Tuple[int, int, int] = (1, 6, 6)
516
__version__: str = "1.6.6"
517
```
518
519
### Default Configuration Values
520
521
Default parameter values used by TerminalMenu constructor:
522
523
```python { .api }
524
# Navigation and behavior defaults
525
DEFAULT_ACCEPT_KEYS: Tuple[str, ...] = ("enter",)
526
DEFAULT_QUIT_KEYS: Tuple[str, ...] = ("escape", "q", "ctrl-g")
527
DEFAULT_CYCLE_CURSOR: bool = True
528
DEFAULT_EXIT_ON_SHORTCUT: bool = True
529
530
# Display defaults
531
DEFAULT_CLEAR_MENU_ON_EXIT: bool = True
532
DEFAULT_CLEAR_SCREEN: bool = False
533
DEFAULT_MENU_CURSOR: str = "> "
534
DEFAULT_MENU_CURSOR_STYLE: Tuple[str, ...] = ("fg_red", "bold")
535
DEFAULT_MENU_HIGHLIGHT_STYLE: Tuple[str, ...] = ("standout",)
536
537
# Multi-selection defaults
538
DEFAULT_MULTI_SELECT: bool = False
539
DEFAULT_MULTI_SELECT_CURSOR: str = "[*] "
540
DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE: Tuple[str, ...] = ("fg_gray",)
541
DEFAULT_MULTI_SELECT_CURSOR_STYLE: Tuple[str, ...] = ("fg_yellow", "bold")
542
DEFAULT_MULTI_SELECT_KEYS: Tuple[str, ...] = (" ", "tab")
543
DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT: bool = True
544
545
# Search defaults
546
DEFAULT_SEARCH_CASE_SENSITIVE: bool = False
547
DEFAULT_SEARCH_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_black", "bg_yellow", "bold")
548
DEFAULT_SEARCH_KEY: str = "/"
549
550
# Preview defaults
551
DEFAULT_PREVIEW_BORDER: bool = True
552
DEFAULT_PREVIEW_SIZE: float = 0.25
553
DEFAULT_PREVIEW_TITLE: str = "preview"
554
555
# Shortcut and hint defaults
556
DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_gray",)
557
DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE: Tuple[str, ...] = ("fg_blue",)
558
DEFAULT_SHOW_MULTI_SELECT_HINT: bool = False
559
DEFAULT_SHOW_SEARCH_HINT: bool = False
560
DEFAULT_SHOW_SHORTCUT_HINTS: bool = False
561
DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR: bool = True
562
563
# Status bar defaults
564
DEFAULT_STATUS_BAR_BELOW_PREVIEW: bool = False
565
DEFAULT_STATUS_BAR_STYLE: Tuple[str, ...] = ("fg_yellow", "bg_black")
566
567
# Layout constraints
568
MIN_VISIBLE_MENU_ENTRIES_COUNT: int = 3
569
```