npyscreen Documentation

repository·master·Indexed 19 days ago

https://github.com/npcole/npyscreen

A Python-based widget library and application framework for building terminal and console applications using ncurses. It provides a variety of default widgets, from simple text fields to complex tree and grid views, and includes the NPSAppManaged class to handle application lifecycles, form registration, and screen transitions.

Tokens
65K
Snippets
206
Records
268
Agent score
67%

What's inside npyscreen

  1. Overview of npyscreen

    master
    npyscreen is a Python widget library and application framework designed for creating terminal or console applications. It is built on top of ncurses (part of the Python standard library). The framework provides a wide variety of default widgets, ranging from simple text fields to complex tree and grid views, and is designed to scale from simple scripts to complex, multi-screen applications.
  2. Build a Form with Widgets

    master

    A Form defines the display presented to the user. To create a functional form, you must add widgets to it using the add() method.

    To allow a user to interact with the form, call F.edit(). To retrieve data from a widget, access its .value attribute.

    Common widgets include:

    • npyscreen.TitleText: A text input widget with a title.
    • npyscreen.TitleDateCombo: A date input widget with a title.
    • npyscreen.TitleSelectOne: A multi-line selection widget. Use max_height to limit screen space and values to provide options.
    import npyscreen
    
    def myFunction(*args):
        F = npyscreen.Form(name='My Test Application')
        # Add a widget and capture the reference to access its value later
        my_widget = F.add(npyscreen.TitleText, name="First Widget")
        F.edit()
        return my_widget.value
    
    if __name__ == '__main__':
        print npyscreen.wrapper_basic(myFunction)
  3. How npyscreen applications are structured

    master

    An npyscreen application is composed of three primary object types that work together to manage the terminal interface:

    1. Application Objects: These manage the lifecycle of the application and the switching between different screens (Forms). Using these is highly recommended to avoid errors when managing multiple screens.
    2. Form Objects: These act as containers for widgets. A Form typically occupies the terminal screen (or a portion of it) and handles user interactions like menu systems, button presses (e.g., an "ok" button), and key-press routines.
    3. Widget Objects: These are the individual interactive controls placed inside a Form, such as text boxes, labels, and sliders.
    import npyscreen
    
    class MyTestApp(npyscreen.NPSAppManaged):
        def onStart(self):
            self.registerForm("MAIN", MainForm())
    
    class MainForm(npyscreen.Form):
        def create(self):
            self.add(npyscreen.TitleText, name="Text:", value="Hello World!")
    
    if __name__ == '__main__':
        TA = MyTestApp()
        TA.run()
  4. How key bindings work in npyscreen

    master

    Key binding functionality is provided by the InputHandler class. When a user presses a key, the system searches for a matching action in two stages:

    1. handlers (Dictionary): A mapping of keys to functions. If a key exists in this dictionary, its associated function is executed immediately. Supported notations include standard curses constants and string notations like "^N" for Control-N or "!a" for Alt-A.
    2. complex_handlers (List of pairs): If no match is found in handlers, the system iterates through this list. Each entry is a pair of (test_func, dispatch_func). The test_func is executed; if it returns True, the dispatch_func is called and the search stops.

    Event Bubbling: When a widget is being edited, the system first checks the widget's own handlers. If the widget does not define an action for the pressed key, the search bubbles up to the parent Form's handlers and complex handlers.

    # Example of what a handlers dictionary looks like internally
    {
        curses.ascii.NL:   self.h_exit_down,
        curses.ascii.CR:   self.h_exit_down,
        curses.ascii.TAB:  self.h_exit_down,
        curses.KEY_DOWN:   self.h_exit_down,
        curses.KEY_UP:     self.h_exit_up,
        "^P":              self.h_exit_up,
        "^N":              self.h_exit_down,
        curses.ascii.ESC:  self.h_exit_escape,
    }
  5. Configure Titled Widgets

    master

    Many widgets provide a version with a label (e.g., Textbox vs TitleText). If a label is long, it may be placed on its own line. Use these additional arguments in the constructor to control layout:

    • use_two_lines: Boolean to force or prevent the label from occupying its own line.
    • field_width: (For text fields) Sets the width of the entry portion.
    • begin_entry_at: (For text fields) Sets the column where the entry portion begins.

    Internally, titled widgets consist of a label_widget and an entry_widget. While you can access these via attributes, you should generally interact with the combined widget using the .value and .values attributes.

  6. Implement Mutt-style forms with FormMuttActive classes

    master

    To create terminal applications with a layout similar to Mutt, Vim, or less (title bar, status bar, main display area, and a command line at the bottom), use the FormMuttActive family of classes. These classes coordinate several widgets and a controller to manage user input and data display.

    Available Form Classes

    • FormMuttActive
    • FormMuttActiveWithMenus
    • FormMuttActiveTraditional
    • FormMuttActiveTraditionalWithMenus

    Key Differences

    • Traditional vs. Non-Traditional: In Traditional forms, focus remains on the command line widget, but certain keypresses are passed to the MAIN_WIDGET_CLASS to allow simultaneous interaction (e.g., navigating a list while typing a command).

    Default Instance Attributes

    After initialization, the following attributes are available on the form instance:

    • self.wStatus1: The title bar.
    • self.wStatus2: The status bar located just above the command line.
    • self.wMain: The main display area (defaults to a wgmultiline.MultiLine object).
    • self.wCommand: The command line widget.
    • self.action_controller: An object (not a widget) that handles command execution.

    Configuration via Class Attributes

    You can customize the form by overriding these class attributes:

    • MAIN_WIDGET_CLASS: The class for the main area.
    • MAIN_WIDGET_CLASS_START_LINE: Starting line for the main widget.
    • STATUS_WIDGET_CLASS: The class for status bars.
    • STATUS_WIDGET_X_OFFSET: X offset for status widgets.
    • COMMAND_WIDGET_CLASS: The class for the command line.
    • COMMAND_WIDGET_NAME: Name of the command widget.
    • COMMAND_WIDGET_BEGIN_ENTRY_AT: Where the command entry begins.
    • COMMAND_ALLOW_OVERRIDE_BEGIN_ENTRY_AT: Boolean for entry override behavior.
    • DATA_CONTROLLER: The class used for the form's .value attribute (defaults to npysNPSFilteredData.NPSFilteredDataList).
    • ACTION_CONTROLLER: The class for handling commands (defaults to ActionControllerSimple).
    class FmSearchActive(npyscreen.FormMuttActiveTraditional):
        ACTION_CONTROLLER = ActionControllerSearch
  7. Persist option collections with OptionList

    master

    An OptionList object acts as a container for a collection of Option objects. It is primarily used to save and restore configuration settings to a file.

    Features

    • Storage Format: Uses a custom text format (similar to Unix files) that uses tab characters as separators to store and restore lists of strings. Only values that differ from the DEFAULT are stored.
    • Persistence Methods:
      • write_to_file(fn=None): Saves the current state of the options to the specified filename.
      • reload_from_file(fn=None): Loads the saved values from the specified filename.

    Usage Pattern

    1. Create an OptionList instance.
    2. Append Option objects to its .options attribute.
    3. Use reload_from_file() at application startup (handling FileNotFoundError if the file doesn't exist yet).
    4. Use write_to_file() when the user exits or saves settings.
    import npyscreen
    
    # Setup
    options_collection = npyscreen.OptionList()
    options_collection.options.append(npyscreen.OptionFreeText('Username'))
    
    # Load existing settings
    try:
        options_collection.reload_from_file('/path/to/config')
    except FileNotFoundError:
        pass
    
    # ... run application ...
    
    # Save settings
    options_collection.write_to_file('/path/to/config')
  8. Create a custom ThemeManager

    master

    To create a custom color scheme, subclass npyscreen.ThemeManager and define a default_colors dictionary. This dictionary maps semantic color names (used by widgets) to specific color-pair strings defined in the theme.

    Common semantic keys include:

    • DEFAULT: General widget color
    • FORMDEFAULT: Form widget color
    • LABEL: Color for widget labels
    • LABELBOLD: Color for bold labels
    • CONTROL: Color for control elements
    • IMPORTANT, SAFE, GOOD: Success/Positive colors
    • WARNING, CAUTION: Warning colors
    • DANGER, CRITICAL: Error/Danger colors
    • CURSOR: Color for the cursor

    Example of a custom theme definition:

    class MyCustomTheme(npyscreen.ThemeManager):
        default_colors = {
            'DEFAULT'     : 'WHITE_BLACK',
            'LABEL'       : 'GREEN_BLACK',
            'DANGER'      : 'RED_BLACK',
            'IMPORTANT'   : 'GREEN_BLACK',
        }
    class DefaultTheme(npyscreen.ThemeManager):
        default_colors = {
            'DEFAULT'     : 'WHITE_BLACK',
            'FORMDEFAULT' : 'WHITE_BLACK',
            'NO_EDIT'     : 'BLUE_BLACK',
            'STANDOUT'    : 'CYAN_BLACK',
            'CURSOR'      : 'WHITE_BLACK',
            'CURSOR_INVERSE': 'BLACK_WHITE',
            'LABEL'       : 'GREEN_BLACK',
            'LABELBOLD'   : 'WHITE_BLACK',
            'CONTROL'     : 'YELLOW_BLACK',
            'IMPORTANT'   : 'GREEN_BLACK',
            'SAFE'        : 'GREEN_BLACK',
            'WARNING'     : 'YELLOW_BLACK',
            'DANGER'      : 'RED_BLACK',
            'CRITICAL'    : 'BLACK_RED',
            'GOOD'        : 'GREEN_BLACK',
            'GOODHL'      : 'GREEN_BLACK',
            'VERYGOOD'    : 'BLACK_GREEN',
            'CAUTION'     : 'YELLOW_BLACK',
            'CAUTIONHL'   : 'BLACK_YELLOW',
            }
  9. Use MultiLine widgets to pick options

    master

    The MultiLine widget (and its derivatives) allows users to select from a list of options.

    • Data Storage: The list of available options is stored in the .values attribute.
    • Selection: The .value attribute stores the index of the user's current selection.
    • Retrieving Objects: To get the actual objects from the list instead of just the index, use the .get_selected_objects() method.

    Customizing Display

    You can pass a list of arbitrary Python objects to the widget. By default, they are displayed using str(). To display custom representations, override the display_value(self, vl) method, where vl is the object being displayed.

    Filtering Options

    MultiLine widgets support filtering (default keys: l, L, n, p).

    • Disable Filtering: Set .allow_filtering = False or pass it as an argument to the constructor.
    • Custom Filter Logic: Override filter_value(self, index) to control matching. It should accept an index and return True on a match or False otherwise.
    # Example of customizing display for custom objects
    class MyCustomMultiLine(MultiLine):
        def display_value(self, vl):
            return f"ID: {vl.id} | Name: {vl.name}"
  10. Understand the difference between NPSApp and NPSAppManaged

    master
    • Automatically manages the application main loop.
    • Manages the display and transitions between various Form objects.
    • Provides built-in lifecycle hooks and navigation management.
    • Best for almost all new projects.

    NPSApp (Legacy/Internal)

    • Requires the developer to provide their own .main() definition and manage the main loop manually.
    • Provides maximum flexibility but is significantly more complex to implement correctly.
    • Should be regarded as an internal base class; do not use it for new projects.
  11. Use Option objects to manage configuration values

    master

    An Option object is the core abstraction for storing single or multiple values, along with associated documentation. You can create options using the following signature:

    OptionType(name, value=None, documentation=None, short_explanation=None, option_widget_keywords=None, default=None)

    Key Methods

    • set(value): Updates the value stored in the option.
    • get(): Retrieves the current value.
    • when_set(): A method you can override to trigger logic immediately after a value is changed.

    Option Classes

    The following classes are available for different data types:

    • OptionFreeText: For plain text input.
    • OptionSingleChoice: For selecting one item from a list.
    • OptionMultiChoice: For selecting multiple items from a list.
    • OptionMultiFreeList: For a list of multiple free-text entries.
    • OptionBoolean: For boolean values.
    • OptionFilename: For file paths.
    • OptionDate: For date values.
    • OptionMultiFreeText: For multi-line text input.

    Choice-based Options

    Classes that allow selection from a limited range (like OptionSingleChoice or OptionMultiChoice) also support:

    • setChoices(choices): Defines the list of available options.
    • getChoices(): Retrieves the current list of choices.
    • The class attributes DEFAULT and WIDGET_TO_USE define the default value and the specific widget class used for user interaction, respectively.
    import npyscreen
    
    # Example of creating a choice-based option
    opt = npyscreen.OptionSingleChoice('MyChoice', choices=['A', 'B', 'C'])
    opt.setChoices(['X', 'Y', 'Z'])
    val = opt.get()
  12. Implement screen transition logic in Forms

    master

    When using NPSAppManaged, there are three ways to control the NEXT_ACTIVE_FORM (the next screen to show):

    1. The afterEditing() pattern (Preferred): If a Form does not implement activate(), NPSAppManaged calls afterEditing() when the form exits. Use this method to call self.parentApp.setNextForm(formid) to determine the next screen (e.g., based on whether the user pressed 'OK' or 'Cancel').
    2. The switchForm(formid) pattern: Call self.parentApp.switchForm(formid) to immediately stop editing the current form and jump to another. This may bypass some form logic.
    3. The activate() pattern: If a Form implements activate(), NPSAppManaged calls this instead of the usual .edit() method. This provides maximum flexibility but requires you to call self.edit() manually if you want the standard edit loop to run.

    Form lifecycle hooks:

    • beforeEditing(): Called before the edit loop starts.
    • afterEditing(): Called when the form is exited.
    class MyForm(Form):
        def afterEditing(self):
            # Preferred way to handle navigation
            if self.user_pressed_ok:
                self.parentApp.setNextForm("NEXT_SCREEN")
            else:
                self.parentApp.setNextForm("MAIN")
    
        def activate(self):
            # Advanced way to override the lifecycle
            self.edit()
            self.parentApp.setNextForm(None) # Exit app after this form