A widget and grid based framework for building command line user interfaces in python.
—
Global and widget-specific key binding system for handling user input, navigation, and custom commands in both overview and focus modes.
System for binding keys to functions that work in overview mode (when no widget is focused).
def add_key_command(key: Union[int, List[int]], command: Callable[[], Any]) -> None:
"""
Add a global key binding for overview mode.
Parameters:
- key: Key code or list of key codes to bind
- command: No-argument function to execute when key is pressed
"""Usage example:
import py_cui
def save_action():
print('Save action triggered')
def quit_action():
print('Quitting application')
root.stop()
def help_action():
root.show_message_popup('Help', 'F1: Help, S: Save, Q: Quit')
root = py_cui.PyCUI(3, 3)
# Single key binding
root.add_key_command(py_cui.keys.KEY_S_LOWER, save_action)
# Multiple keys for same action
root.add_key_command([py_cui.keys.KEY_Q_LOWER, py_cui.keys.KEY_ESCAPE], quit_action)
# Function key binding
root.add_key_command(py_cui.keys.KEY_F1, help_action)System for binding keys to functions that work when a specific widget is in focus mode.
# Widget method for adding key commands
def add_key_command(key: Union[int, List[int]], command: Callable[[], Any]) -> None:
"""
Add a key binding specific to this widget (focus mode).
Parameters:
- key: Key code or list of key codes to bind
- command: No-argument function to execute when key is pressed in focus mode
"""Usage example:
import py_cui
root = py_cui.PyCUI(3, 3)
menu = root.add_scroll_menu('Options', 0, 0)
text_box = root.add_text_box('Input', 1, 0)
# Menu-specific key bindings
def delete_item():
menu.remove_selected_item()
def clear_menu():
menu.clear()
menu.add_key_command(py_cui.keys.KEY_DELETE, delete_item)
menu.add_key_command(py_cui.keys.KEY_CTRL_K, clear_menu)
# Text box specific key bindings
def clear_text():
text_box.clear()
def insert_timestamp():
import datetime
text_box.write(f' [{datetime.datetime.now()}]')
text_box.add_key_command(py_cui.keys.KEY_CTRL_L, clear_text)
text_box.add_key_command(py_cui.keys.KEY_F5, insert_timestamp)Configuration for navigation keys and widget cycling behavior.
def set_widget_cycle_key(forward_cycle_key: int = None,
reverse_cycle_key: int = None) -> None:
"""
Set keys for cycling between widgets.
Parameters:
- forward_cycle_key: Key for forward widget cycling (default: Ctrl+Right)
- reverse_cycle_key: Key for reverse widget cycling (default: Ctrl+Left)
"""Usage example:
root = py_cui.PyCUI(3, 3)
# Use Tab/Shift+Tab for widget cycling
root.set_widget_cycle_key(
forward_cycle_key=py_cui.keys.KEY_TAB,
reverse_cycle_key=py_cui.keys.KEY_SHIFT_TAB
)
# Add some widgets to cycle through
menu = root.add_scroll_menu('Menu', 0, 0)
text_box = root.add_text_box('Input', 0, 1)
button = root.add_button('Submit', 0, 2)System for handling mouse clicks and events on widgets.
# Widget method for adding mouse commands
def add_mouse_command(mouse_event: int, command: Callable[[], Any]) -> None:
"""
Add a mouse event binding to this widget.
Parameters:
- mouse_event: Mouse event code (button click, double-click, etc.)
- command: No-argument function to execute on mouse event
"""Usage example:
import py_cui
def handle_right_click():
root.show_menu_popup('Context Menu', ['Edit', 'Delete', 'Properties'],
lambda x: print(f'Selected: {x}'))
def handle_double_click():
print('Double-clicked on menu item')
root = py_cui.PyCUI(3, 3)
menu = root.add_scroll_menu('File List', 0, 0, row_span=2)
# Add mouse event handlers
menu.add_mouse_command(py_cui.keys.MOUSE_RIGHT_CLICK, handle_right_click)
menu.add_mouse_command(py_cui.keys.MOUSE_DOUBLE_CLICK, handle_double_click)Key codes and utilities for working with keyboard input.
# Common key constants
KEY_ENTER: int
KEY_ESCAPE: int
KEY_TAB: int
KEY_BACKSPACE: int
KEY_DELETE: int
# Arrow keys
KEY_UP_ARROW: int
KEY_DOWN_ARROW: int
KEY_LEFT_ARROW: int
KEY_RIGHT_ARROW: int
ARROW_KEYS: List[int] # List of all arrow key codes
# Function keys
KEY_F1: int
KEY_F2: int
# ... F3 through F12
# Control key combinations
KEY_CTRL_A: int
KEY_CTRL_C: int
KEY_CTRL_V: int
KEY_CTRL_X: int
KEY_CTRL_Z: int
# ... other Ctrl combinations
# Letter keys (upper and lower case)
KEY_A_UPPER: int
KEY_A_LOWER: int
# ... other letters
# Number keys
KEY_0: int
KEY_1: int
# ... other numbers
# Special keys
KEY_SPACE: int
KEY_SHIFT_TAB: int
# Mouse events
MOUSE_LEFT_CLICK: int
MOUSE_RIGHT_CLICK: int
MOUSE_DOUBLE_CLICK: int
# Utility functions
def get_char_from_ascii(key_code: int) -> Optional[str]:
"""
Convert key code to character representation.
Parameters:
- key_code: ASCII key code
Returns:
String representation of key or None if not printable
"""Usage example:
import py_cui
def handle_key_press():
# Example of using key constants
print('Key pressed!')
def show_key_help():
help_text = f"""
Available Keys:
{py_cui.keys.get_char_from_ascii(py_cui.keys.KEY_F1)} - Help
{py_cui.keys.get_char_from_ascii(py_cui.keys.KEY_S_LOWER)} - Save
{py_cui.keys.get_char_from_ascii(py_cui.keys.KEY_Q_LOWER)} - Quit
Arrows - Navigate
Enter - Select
Escape - Cancel
"""
root.show_message_popup('Key Help', help_text)
root = py_cui.PyCUI(3, 3)
# Bind various key types
root.add_key_command(py_cui.keys.KEY_F1, show_key_help)
root.add_key_command(py_cui.keys.KEY_CTRL_H, show_key_help)
root.add_key_command(py_cui.keys.KEY_SPACE, handle_key_press)
# Check if keys are arrow keys
def handle_any_key(key_code: int):
if key_code in py_cui.keys.ARROW_KEYS:
print('Arrow key pressed')
char = py_cui.keys.get_char_from_ascii(key_code)
if char:
print(f'Printable key: {char}')Examples of complex key binding scenarios and patterns.
# Advanced binding patterns (examples)Usage example:
import py_cui
class KeyBindingDemo:
def __init__(self):
self.root = py_cui.PyCUI(3, 3)
self.mode = 'normal'
# Create widgets
self.setup_widgets()
self.setup_key_bindings()
def setup_widgets(self):
self.menu = self.root.add_scroll_menu('Commands', 0, 0)
self.output = self.root.add_text_block('Output', 0, 1, column_span=2)
self.input_box = self.root.add_text_box('Input', 1, 0, column_span=3)
def setup_key_bindings(self):
# Modal key bindings - different behavior based on mode
self.root.add_key_command(py_cui.keys.KEY_I_LOWER, self.enter_insert_mode)
self.root.add_key_command(py_cui.keys.KEY_ESCAPE, self.enter_normal_mode)
# Conditional key bindings
self.root.add_key_command(py_cui.keys.KEY_ENTER, self.context_sensitive_enter)
# Multi-key sequences (simple implementation)
self.last_key = None
self.root.add_key_command(py_cui.keys.KEY_G_LOWER, self.handle_g_key)
def enter_insert_mode(self):
self.mode = 'insert'
self.root.set_status_bar_text('-- INSERT MODE --')
self.root.move_focus(self.input_box)
def enter_normal_mode(self):
self.mode = 'normal'
self.root.set_status_bar_text('-- NORMAL MODE --')
self.root.lose_focus()
def context_sensitive_enter(self):
if self.mode == 'normal':
selected = self.root.get_selected_widget()
if selected == self.menu:
self.execute_command()
elif self.mode == 'insert':
self.process_input()
def handle_g_key(self):
# Simple two-key sequence: 'gg' to go to top
if self.last_key == py_cui.keys.KEY_G_LOWER:
self.goto_top()
self.last_key = None
else:
self.last_key = py_cui.keys.KEY_G_LOWER
def execute_command(self):
command = self.menu.get_selected_item()
self.output.write(f'Executing: {command}\n')
def process_input(self):
text = self.input_box.get()
self.output.write(f'Input: {text}\n')
self.input_box.clear()
def goto_top(self):
self.menu.set_selected_item_index(0)
self.output.write('Jumped to top\n')
def run(self):
self.menu.add_item_list(['save', 'load', 'quit', 'help'])
self.root.start()
# Usage
demo = KeyBindingDemo()
demo.run()Install with Tessl CLI
npx tessl i tessl/pypi-py-cui