pytermgui

A simple yet powerful TUI framework for your Python (3.7+) applications.

There is a couple of parts that make up this module, all building on top of eachother to achieve the final result. Your usage depends on which part of the library you use. I will provide an example of usage for each.

  1"""
  2A simple yet powerful TUI framework for your Python (3.7+) applications.
  3
  4There is a couple of parts that make up this module, all building on top
  5of eachother to achieve the final result. Your usage depends on which part
  6of the library you use. I will provide an example of usage for each.
  7
  8.. include:: ../docs/getting_started.md
  9"""
 10
 11# https://github.com/python/mypy/issues/4930
 12# mypy: ignore-errors
 13
 14from __future__ import annotations
 15
 16import sys
 17from typing import Any, Optional
 18
 19from .animations import *
 20from .ansi_interface import *
 21from .colors import *
 22from .context_managers import alt_buffer, cursor_at, mouse_handler
 23from .enums import *
 24from .exceptions import *
 25from .exporters import *
 26from .fancy_repr import *
 27from .file_loaders import *
 28from .helpers import *
 29from .highlighters import *
 30from .input import getch, keys
 31from .inspector import *
 32from .markup import *
 33from .prettifiers import *
 34from .serializer import *
 35from .terminal import *
 36from .widgets import *
 37from .window_manager import *
 38
 39# Silence warning if running as standalone module
 40if "-m" in sys.argv:  # pragma: no cover
 41    import warnings
 42
 43    warnings.filterwarnings("ignore")
 44
 45__version__ = "7.1.0"
 46
 47
 48def auto(data: Any, **widget_args: Any) -> Optional[Widget | list[Splitter]]:
 49    """Creates a widget from specific data structures.
 50
 51    This conversion includes various widget classes, as well as some shorthands for
 52    more complex objects.  This method is called implicitly whenever a non-widget is
 53    attempted to be added to a Widget.
 54
 55
 56    Args:
 57        data: The structure to convert. See below for formats.
 58        **widget_args: Arguments passed straight to the widget constructor.
 59
 60    Returns:
 61        The widget or list of widgets created, or None if the passed structure could
 62        not be converted.
 63
 64    <br>
 65    <details style="text-align: left">
 66        <summary style="all: revert; cursor: pointer">Data structures:</summary>
 67
 68    `pytermgui.widgets.base.Label`:
 69
 70    * Created from `str`
 71    * Syntax example: `"Label value"`
 72
 73    `pytermgui.widgets.extra.Splitter`:
 74
 75    * Created from `tuple[Any]`
 76    * Syntax example: `(YourWidget(), "auto_syntax", ...)`
 77
 78    `pytermgui.widgets.extra.Splitter` prompt:
 79
 80    * Created from `dict[Any, Any]`
 81    * Syntax example: `{YourWidget(): "auto_syntax"}`
 82
 83    `pytermgui.widgets.buttons.Button`:
 84
 85    * Created from `list[str, pytermgui.widgets.buttons.MouseCallback]`
 86    * Syntax example: `["Button label", lambda target, caller: ...]`
 87
 88    `pytermgui.widgets.buttons.Checkbox`:
 89
 90    * Created from `list[bool, Callable[[bool], Any]]`
 91    * Syntax example: `[True, lambda checked: ...]`
 92
 93    `pytermgui.widgets.buttons.Toggle`:
 94
 95    * Created from `list[tuple[str, str], Callable[[str], Any]]`
 96    * Syntax example: `[("On", "Off"), lambda new_value: ...]`
 97    </details>
 98
 99    Example:
100
101    ```python3
102    from pytermgui import Container
103    form = (
104        Container(id="form")
105        + "[157 bold]This is a title"
106        + ""
107        + {"[72 italic]Label1": "[210]Button1"}
108        + {"[72 italic]Label2": "[210]Button2"}
109        + {"[72 italic]Label3": "[210]Button3"}
110        + ""
111        + ["Submit", lambda _, button, your_submit_handler(button.parent)]
112    )
113    ```
114    """
115    # In my opinion, returning immediately after construction is much more readable.
116    # pylint: disable=too-many-return-statements
117
118    # Nothing to do.
119    if isinstance(data, Widget):
120        # Set all **widget_args
121        for key, value in widget_args.items():
122            setattr(data, key, value)
123
124        return data
125
126    # Label
127    if isinstance(data, str):
128        return Label(data, **widget_args)
129
130    # Splitter
131    if isinstance(data, tuple):
132        return Splitter(*data, **widget_args)
133
134    # buttons
135    if isinstance(data, list):
136        label = data[0]
137        onclick = None
138        if len(data) > 1:
139            onclick = data[1]
140
141        # Checkbox
142        if isinstance(label, bool):
143            return Checkbox(onclick, checked=label, **widget_args)
144
145        # Toggle
146        if isinstance(label, tuple):
147            assert len(label) == 2
148            return Toggle(label, onclick, **widget_args)
149
150        return Button(label, onclick, **widget_args)
151
152    # prompt splitter
153    if isinstance(data, dict):
154        rows: list[Splitter] = []
155
156        for key, value in data.items():
157            left = auto(key, parent_align=HorizontalAlignment.LEFT)
158            right = auto(value, parent_align=HorizontalAlignment.RIGHT)
159
160            rows.append(Splitter(left, right, **widget_args))
161
162        if len(rows) == 1:
163            return rows[0]
164
165        return rows
166
167    return None
168
169
170# Alternative binding for the `auto` method
171Widget.from_data = staticmethod(auto)
def auto( data: Any, **widget_args: Any) -> Union[pytermgui.widgets.base.Widget, list[pytermgui.widgets.containers.Splitter], NoneType]:
 49def auto(data: Any, **widget_args: Any) -> Optional[Widget | list[Splitter]]:
 50    """Creates a widget from specific data structures.
 51
 52    This conversion includes various widget classes, as well as some shorthands for
 53    more complex objects.  This method is called implicitly whenever a non-widget is
 54    attempted to be added to a Widget.
 55
 56
 57    Args:
 58        data: The structure to convert. See below for formats.
 59        **widget_args: Arguments passed straight to the widget constructor.
 60
 61    Returns:
 62        The widget or list of widgets created, or None if the passed structure could
 63        not be converted.
 64
 65    <br>
 66    <details style="text-align: left">
 67        <summary style="all: revert; cursor: pointer">Data structures:</summary>
 68
 69    `pytermgui.widgets.base.Label`:
 70
 71    * Created from `str`
 72    * Syntax example: `"Label value"`
 73
 74    `pytermgui.widgets.extra.Splitter`:
 75
 76    * Created from `tuple[Any]`
 77    * Syntax example: `(YourWidget(), "auto_syntax", ...)`
 78
 79    `pytermgui.widgets.extra.Splitter` prompt:
 80
 81    * Created from `dict[Any, Any]`
 82    * Syntax example: `{YourWidget(): "auto_syntax"}`
 83
 84    `pytermgui.widgets.buttons.Button`:
 85
 86    * Created from `list[str, pytermgui.widgets.buttons.MouseCallback]`
 87    * Syntax example: `["Button label", lambda target, caller: ...]`
 88
 89    `pytermgui.widgets.buttons.Checkbox`:
 90
 91    * Created from `list[bool, Callable[[bool], Any]]`
 92    * Syntax example: `[True, lambda checked: ...]`
 93
 94    `pytermgui.widgets.buttons.Toggle`:
 95
 96    * Created from `list[tuple[str, str], Callable[[str], Any]]`
 97    * Syntax example: `[("On", "Off"), lambda new_value: ...]`
 98    </details>
 99
100    Example:
101
102    ```python3
103    from pytermgui import Container
104    form = (
105        Container(id="form")
106        + "[157 bold]This is a title"
107        + ""
108        + {"[72 italic]Label1": "[210]Button1"}
109        + {"[72 italic]Label2": "[210]Button2"}
110        + {"[72 italic]Label3": "[210]Button3"}
111        + ""
112        + ["Submit", lambda _, button, your_submit_handler(button.parent)]
113    )
114    ```
115    """
116    # In my opinion, returning immediately after construction is much more readable.
117    # pylint: disable=too-many-return-statements
118
119    # Nothing to do.
120    if isinstance(data, Widget):
121        # Set all **widget_args
122        for key, value in widget_args.items():
123            setattr(data, key, value)
124
125        return data
126
127    # Label
128    if isinstance(data, str):
129        return Label(data, **widget_args)
130
131    # Splitter
132    if isinstance(data, tuple):
133        return Splitter(*data, **widget_args)
134
135    # buttons
136    if isinstance(data, list):
137        label = data[0]
138        onclick = None
139        if len(data) > 1:
140            onclick = data[1]
141
142        # Checkbox
143        if isinstance(label, bool):
144            return Checkbox(onclick, checked=label, **widget_args)
145
146        # Toggle
147        if isinstance(label, tuple):
148            assert len(label) == 2
149            return Toggle(label, onclick, **widget_args)
150
151        return Button(label, onclick, **widget_args)
152
153    # prompt splitter
154    if isinstance(data, dict):
155        rows: list[Splitter] = []
156
157        for key, value in data.items():
158            left = auto(key, parent_align=HorizontalAlignment.LEFT)
159            right = auto(value, parent_align=HorizontalAlignment.RIGHT)
160
161            rows.append(Splitter(left, right, **widget_args))
162
163        if len(rows) == 1:
164            return rows[0]
165
166        return rows
167
168    return None

Creates a widget from specific data structures.

This conversion includes various widget classes, as well as some shorthands for more complex objects. This method is called implicitly whenever a non-widget is attempted to be added to a Widget.

Args
  • data: The structure to convert. See below for formats.
  • **widget_args: Arguments passed straight to the widget constructor.
Returns

The widget or list of widgets created, or None if the passed structure could not be converted.


Data structures:

pytermgui.widgets.base.Label:

  • Created from str
  • Syntax example: "Label value"

pytermgui.widgets.extra.Splitter:

  • Created from tuple[Any]
  • Syntax example: (YourWidget(), "auto_syntax", ...)

pytermgui.widgets.extra.Splitter prompt:

  • Created from dict[Any, Any]
  • Syntax example: {YourWidget(): "auto_syntax"}

pytermgui.widgets.buttons.Button:

  • Created from list[str, pytermgui.widgets.buttons.MouseCallback]
  • Syntax example: ["Button label", lambda target, caller: ...]

pytermgui.widgets.buttons.Checkbox:

  • Created from list[bool, Callable[[bool], Any]]
  • Syntax example: [True, lambda checked: ...]

pytermgui.widgets.buttons.Toggle:

  • Created from list[tuple[str, str], Callable[[str], Any]]
  • Syntax example: [("On", "Off"), lambda new_value: ...]

Example:

from pytermgui import Container
form = (
    Container(id="form")
    + "[157 bold]This is a title"
    + ""
    + {"[72 italic]Label1": "[210]Button1"}
    + {"[72 italic]Label2": "[210]Button2"}
    + {"[72 italic]Label3": "[210]Button3"}
    + ""
    + ["Submit", lambda _, button, your_submit_handler(button.parent)]
)