pytermgui.context_managers

Ease-of-use context-manager classes & functions.

There isn't much (or any) additional functionality provided in this module, most things are nicer-packaged combinations to already available methods from pytermgui.ansi_interface.

  1"""
  2Ease-of-use context-manager classes & functions.
  3
  4There isn't much (or any) additional functionality provided in this module,
  5most things are nicer-packaged combinations to already available methods from
  6`pytermgui.ansi_interface`.
  7"""
  8
  9from __future__ import annotations
 10
 11from os import name
 12from contextlib import contextmanager
 13from typing import Callable, Generator, Any, Union, List
 14
 15from .ansi_interface import (
 16    save_cursor,
 17    restore_cursor,
 18    print_to,
 19    show_cursor,
 20    hide_cursor,
 21    set_echo,
 22    unset_echo,
 23    set_alt_buffer,
 24    unset_alt_buffer,
 25    cursor_up,
 26    report_mouse,
 27    translate_mouse,
 28    MouseEvent,
 29)
 30
 31from .terminal import get_terminal
 32
 33# This is technically meant to be here, but it has to be in `input.py` due
 34# to some package structure issues.
 35from .input import timeout  # pylint: disable=unused-import
 36
 37# TODO: Move this absolute beast to a types submodule
 38MouseTranslator = Callable[[str], Union[List[Union[MouseEvent, None]], None]]
 39
 40
 41@contextmanager
 42def cursor_at(pos: tuple[int, int]) -> Generator[Callable[..., None], None, None]:
 43    """Gets callable to print at `pos`, incrementing `y` on every print.
 44
 45    Args:
 46        pos: The position to start printing at. Follows the order (columns, rows).
 47
 48    Yields:
 49        A callable printing function. This function forwards all arguments to `print`,
 50        but positions the cursor before doing so. After every call, the y position is
 51        incremented.
 52    """
 53
 54    offset = 0
 55    posx, posy = pos
 56
 57    def printer(*args: tuple[Any, ...]) -> None:
 58        """Print to posx, current y"""
 59
 60        nonlocal offset
 61
 62        print_to((posx, posy + offset), *args)
 63        offset += 1
 64
 65    try:
 66        save_cursor()
 67        yield printer
 68
 69    finally:
 70        restore_cursor()
 71
 72
 73@contextmanager
 74def alt_buffer(echo: bool = False, cursor: bool = True) -> Generator[None, None, None]:
 75    """Creates non-scrollable alt-buffer.
 76
 77    This is useful for retrieving original terminal state after program end.
 78
 79    Args:
 80        echo: Whether `unset_echo` should be called on startup.
 81        cursor: Whether `hide_cursor` should be called on startup.
 82    """
 83
 84    terminal = get_terminal()
 85
 86    try:
 87        set_alt_buffer()
 88
 89        if not echo and name == "posix" and not terminal.is_interactive():
 90            unset_echo()
 91
 92        if not cursor:
 93            hide_cursor()
 94
 95        yield
 96
 97    finally:
 98        unset_alt_buffer()
 99
100        if not echo and name == "posix" and not terminal.is_interactive():
101            set_echo()
102            cursor_up()
103
104        if not cursor:
105            show_cursor()
106            cursor_up()
107
108
109@contextmanager
110def mouse_handler(
111    events: list[str], method: str = "decimal_xterm"
112) -> Generator[MouseTranslator | None, None, None]:
113    """Return a mouse handler function
114
115    See `help(report_mouse)` for help about all of the methods.
116
117    Args:
118        events: A list of `pytermgui.ansi_interface.report_mouse` events.
119        method: The method to use for reporting. Only `decimal_urxvt` and
120            `decimal_xterm` are currently supported.
121
122    Example use:
123
124    ```python3
125    import pytermgui as ptg
126
127    with ptg.mouse_handler(["press", "hover"]) as mouse:
128        while True:
129          event = mouse(ptg.getch())
130          print(type(event))
131          print(event.action)
132          print(event.position)
133
134    'pytermgui.ansi_interface.MouseEvent'
135    'pytermgui.ansi_interface.MouseAction.LEFT_CLICK'
136    (33, 55)
137    ```
138
139    """
140
141    event = None
142    try:
143        for event in events:
144            report_mouse(event, method=method)
145
146        yield lambda code: translate_mouse(code, method=method)
147
148    finally:
149        if event is not None:
150            report_mouse(event, method=method, stop=True)
@contextmanager
def cursor_at( pos: tuple[int, int]) -> Generator[Callable[..., NoneType], NoneType, NoneType]:
42@contextmanager
43def cursor_at(pos: tuple[int, int]) -> Generator[Callable[..., None], None, None]:
44    """Gets callable to print at `pos`, incrementing `y` on every print.
45
46    Args:
47        pos: The position to start printing at. Follows the order (columns, rows).
48
49    Yields:
50        A callable printing function. This function forwards all arguments to `print`,
51        but positions the cursor before doing so. After every call, the y position is
52        incremented.
53    """
54
55    offset = 0
56    posx, posy = pos
57
58    def printer(*args: tuple[Any, ...]) -> None:
59        """Print to posx, current y"""
60
61        nonlocal offset
62
63        print_to((posx, posy + offset), *args)
64        offset += 1
65
66    try:
67        save_cursor()
68        yield printer
69
70    finally:
71        restore_cursor()

Gets callable to print at pos, incrementing y on every print.

Args
  • pos: The position to start printing at. Follows the order (columns, rows).
Yields

A callable printing function. This function forwards all arguments to print, but positions the cursor before doing so. After every call, the y position is incremented.

@contextmanager
def alt_buffer( echo: bool = False, cursor: bool = True) -> Generator[NoneType, NoneType, NoneType]:
 74@contextmanager
 75def alt_buffer(echo: bool = False, cursor: bool = True) -> Generator[None, None, None]:
 76    """Creates non-scrollable alt-buffer.
 77
 78    This is useful for retrieving original terminal state after program end.
 79
 80    Args:
 81        echo: Whether `unset_echo` should be called on startup.
 82        cursor: Whether `hide_cursor` should be called on startup.
 83    """
 84
 85    terminal = get_terminal()
 86
 87    try:
 88        set_alt_buffer()
 89
 90        if not echo and name == "posix" and not terminal.is_interactive():
 91            unset_echo()
 92
 93        if not cursor:
 94            hide_cursor()
 95
 96        yield
 97
 98    finally:
 99        unset_alt_buffer()
100
101        if not echo and name == "posix" and not terminal.is_interactive():
102            set_echo()
103            cursor_up()
104
105        if not cursor:
106            show_cursor()
107            cursor_up()

Creates non-scrollable alt-buffer.

This is useful for retrieving original terminal state after program end.

Args
  • echo: Whether unset_echo should be called on startup.
  • cursor: Whether hide_cursor should be called on startup.
@contextmanager
def mouse_handler( events: list[str], method: str = 'decimal_xterm') -> Generator[Optional[Callable[[str], Optional[List[Optional[pytermgui.ansi_interface.MouseEvent]]]]], NoneType, NoneType]:
110@contextmanager
111def mouse_handler(
112    events: list[str], method: str = "decimal_xterm"
113) -> Generator[MouseTranslator | None, None, None]:
114    """Return a mouse handler function
115
116    See `help(report_mouse)` for help about all of the methods.
117
118    Args:
119        events: A list of `pytermgui.ansi_interface.report_mouse` events.
120        method: The method to use for reporting. Only `decimal_urxvt` and
121            `decimal_xterm` are currently supported.
122
123    Example use:
124
125    ```python3
126    import pytermgui as ptg
127
128    with ptg.mouse_handler(["press", "hover"]) as mouse:
129        while True:
130          event = mouse(ptg.getch())
131          print(type(event))
132          print(event.action)
133          print(event.position)
134
135    'pytermgui.ansi_interface.MouseEvent'
136    'pytermgui.ansi_interface.MouseAction.LEFT_CLICK'
137    (33, 55)
138    ```
139
140    """
141
142    event = None
143    try:
144        for event in events:
145            report_mouse(event, method=method)
146
147        yield lambda code: translate_mouse(code, method=method)
148
149    finally:
150        if event is not None:
151            report_mouse(event, method=method, stop=True)

Return a mouse handler function

See help(report_mouse) for help about all of the methods.

Args

Example use:

import pytermgui as ptg

with ptg.mouse_handler(["press", "hover"]) as mouse:
    while True:
      event = mouse(ptg.getch())
      print(type(event))
      print(event.action)
      print(event.position)

'pytermgui.ansi_interface.MouseEvent'
'pytermgui.ansi_interface.MouseAction.LEFT_CLICK'
(33, 55)