Widget Classes

Widget Base Classes

Widget

class urwid.Widget

Widget base class.

_selectable = False

The default selectable() method returns this value.

_sizing = frozenset(['flow', 'box', 'fixed'])

The default sizing() method returns this value.

_command_map = urwid.command_map

A shared CommandMap instance. May be redefined in subclasses or widget instances.

rows(size, focus=False)

Note

This method is not implemented in Widget but must be implemented by any flow widget. See sizing().

See Widget.render() for parameter details.

Returns:

The number of rows required for this widget given a number of columns in size

This is the method flow widgets use to communicate their size to other widgets without having to render a canvas. This should be a quick calculation as this function may be called a number of times in normal operation. If your implementation may take a long time you should add your own caching here.

There is some metaclass magic defined in the Widget metaclass WidgetMeta that causes the result of this function to be retrieved from any canvas cached by CanvasCache, so if your widget has been rendered you may not receive calls to this function. The class variable ignore_focus may be defined and set to True if this widget renders the same size regardless of the value of the focus parameter.

get_cursor_coords(size)

Note

This method is not implemented in Widget but must be implemented by any widget that may return cursor coordinates as part of the canvas that render() returns.

Parameters:

size – See Widget.render() for details.

Returns:

(col, row) if this widget has a cursor, None otherwise

Return the cursor coordinates (col, row) of a cursor that will appear as part of the canvas rendered by this widget when in focus, or None if no cursor is displayed.

The ListBox widget uses this method to make sure a cursor in the focus widget is not scrolled out of view. It is a separate method to avoid having to render the whole widget while calculating layout.

Container widgets will typically call the get_cursor_coords() method on their focus widget.

get_pref_col(size)

Note

This method is not implemented in Widget but may be implemented by a subclass.

Parameters:

size – See Widget.render() for details.

Returns:

a column number or 'left' for the leftmost available column or 'right' for the rightmost available column

Return the preferred column for the cursor to be displayed in this widget. This value might not be the same as the column returned from get_cursor_coords().

The ListBox and Pile widgets call this method on a widget losing focus and use the value returned to call move_cursor_to_coords() on the widget becoming the focus. This allows the focus to move up and down through widgets while keeping the cursor in approximately the same column on screen.

move_cursor_to_coords(size, col, row)

Note

This method is not implemented in Widget but may be implemented by a subclass. Not implementing this method is equivalent to having a method that always returns False.

Parameters:
  • size – See Widget.render() for details.

  • col – new column for the cursor, 0 is the left edge of this widget

  • row – new row for the cursor, 0 it the top row of this widget

Returns:

True if the position was set successfully anywhere on row, False otherwise

Initialize a logger scoped to the concrete widget subclass.

_emit(name: Hashable, *args: Any) → None

Emit signals with self as first argument.

_invalidate() → None

Mark cached canvases rendered by this widget as dirty so that they will not be used again.

property base_widget: AbstractWidget

Read-only property that steps through decoration widgets and returns the one at the base.

This default implementation returns self.

property focus: AbstractWidget | None

Read-only property returning the child widget in focus for container widgets.

This default implementation always returns None, indicating that this widget has no children.

property focus_position: Any

Read or set the focus position for container widgets.

This default implementation raises IndexError, making normal widgets fail the same way accessing focus_position on an empty container widget would.

Raises:

IndexError – this widget is not a container widget.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Keyboard input handler.

Parameters:
Returns:

None if key was handled, key (the same value passed) if it was not handled

Warns WidgetWarning:

the widget is not selectable and its class did not call super().__init__().

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Mouse event handler.

Parameters:
  • size – See Widget.render() for details.

  • event – Values such as 'mouse press', 'ctrl mouse press', 'mouse release', 'meta mouse release', 'mouse drag'; see Mouse Input

  • button – a MouseButton value, usually as a plain int. Buttons 1 to 3 are the mouse buttons, 4 to 7 the wheel, and 8 to 11 extra buttons. It is 0 when a release does not say which button was released (a terminal’s X10 report).

  • col – Column of the event, 0 is the left edge of this widget

  • row – Row of the event, 0 it the top row of this widget

  • focus – Set to True if this widget or one of its children is in focus

Returns:

True if the event was handled by this widget, False otherwise

Warns WidgetWarning:

the widget is not selectable and its class did not call super().__init__().

pack(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → tuple[int, int]

See Widget.render() for parameter details.

Returns:

A “packed” size (maxcol, maxrow) for this widget

Raises:
  • NotImplementedError – the widget supports FIXED sizing but does not override this method.

  • WidgetError – size does not match any sizing mode the widget supports.

Calculate and return a minimum size where all content could still be displayed. Fixed widgets must implement this method and return their size when () is passed as the size parameter.

This default implementation returns the size passed, or the maxcol passed and the value of rows() as the maxrow when (maxcol,) is passed as the size parameter.

Note

This is a new method that hasn’t been fully implemented across the standard widget types. In particular it has not yet been implemented for container widgets.

Text widgets have implemented this method. You can use Text.pack() to calculate the minimum columns and rows required to display a text widget without wrapping, or call it iteratively to calculate the minimum number of columns required to display the text wrapped into a target number of rows.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → Canvas

Render widget and produce canvas.

Parameters:
  • size –

    One of the following, maxcol and maxrow are integers > 0:

    (maxcol, maxrow)

    for box sizing – the parent chooses the exact size of this widget

    (maxcol,)

    for flow sizing – the parent chooses only the number of columns for this widget

    ()

    for fixed sizing – this widget is a fixed size which can’t be adjusted by the parent

  • focus – set to True if this widget or one of its children is in focus

Returns:

A Canvas subclass instance containing the rendered content of this widget

Raises:

NotImplementedError – the subclass does not implement rendering.

Text widgets return a TextCanvas (arbitrary text and display attributes), SolidFill widgets return a SolidCanvas (a single character repeated across the whole surface) and container widgets return a CompositeCanvas (one or more other canvases arranged arbitrarily).

If focus is False, the returned canvas may not have a cursor position set.

There is some metaclass magic defined in the Widget metaclass WidgetMeta that causes the result of this method to be cached by CanvasCache. Later calls will automatically look up the value in the cache first.

As a small optimization the class variable ignore_focus may be defined and set to True if this widget renders the same canvas regardless of the value of the focus parameter.

Any time the content of a widget changes it should call _invalidate() to remove any cached canvases, or the widget may render the cached canvas instead of creating a new one.

selectable() → bool

Return whether this widget is designed to take the focus.

Returns:

True if this widget contains something the user might want to interact with, False otherwise.

This default implementation returns _selectable. Subclasses may leave it as is if they are not selectable, or, if they are always selectable, set the _selectable class variable to True.

If this method returns True then the Widget.keypress() method must be implemented.

Returning False does not guarantee that this widget will never be in focus, only that this widget will usually be skipped over when changing focus. It is still possible for non selectable widgets to have the focus (typically when there are no other selectable widgets visible).

sizing() → frozenset[Sizing]

Return the sizing modes this widget supports.

Returns:

A frozenset including one or more of 'box', 'flow' and 'fixed'. Default implementation returns the value of _sizing, which for this class includes all three.

The sizing modes returned indicate the modes that may be supported by this widget, but is not sufficient to know that using that sizing mode will work. Subclasses should make an effort to remove sizing modes they know will not work given the state of the widget, but many do not yet do this.

If a sizing mode is missing from the set then the widget should fail when used in that mode.

If 'flow' is among the values returned then the other methods in this widget must be able to accept a single-element tuple (maxcol,) to their size parameter, and the rows() method must be defined.

If 'box' is among the values returned then the other methods must be able to accept a two-element tuple (maxcol, maxrow) to their size parameter.

If 'fixed' is among the values returned then the other methods must be able to accept an empty tuple () to their size parameter, and the pack() method must be defined.

WidgetWrap

class urwid.WidgetWrap(w: WrappedWidget)

Base class for widgets that compose another widget for their display and behaviour.

Parameters:

w – widget to wrap, stored as self._w

This object will pass the functions defined in Widget interface definition to self._w.

The purpose of this widget is to provide a base class for widgets that compose other widgets for their display and behaviour. The details of that composition should not affect users of the subclass. The subclass may decide to expose some of the wrapped widgets by behaving like a ContainerWidget or WidgetDecoration, or it may hide them from outside access.

Deprecated since version 2.4.0: Wrapping an object that does not implement the Widget API (see AbstractWidget) is deprecated. Support will be removed in version 7.0.

enter_selectable(*, reverse: bool = False) → None

Forward the entry hook to the wrapped widget, when it has one.

A container with select_navigation calls this when it moves its focus to this widget, see Moving the Focus with Tab. The hook goes to the widget that keypress() sends keys to: a subclass that sends keys elsewhere overrides this method to match.

Parameters:

reverse – start at the last selectable widget instead of the first one.

WidgetDecoration

class urwid.WidgetDecoration(original_widget: WrappedWidget)

Base class for decoration widgets: widgets that contain one or more widgets and only ever have a single focus.

This type of widget will affect the display or behaviour of the original_widget, but it is not part of determining a chain of focus.

Parameters:

original_widget – the widget being decorated

Don’t actually do this - use a WidgetDecoration subclass instead, these are not real widgets:

>>> from urwid import Text
>>> WidgetDecoration(Text("hi"))
<WidgetDecoration fixed/flow widget <Text fixed/flow widget 'hi'>>

Warning

WidgetDecoration does not implement the render method. Implement it, or forward to the wrapped widget, in the subclass.

Wrap original_widget, warning when it does not implement the Widget API.

Deprecated since version 2.4.0: Wrapping an object that does not implement the Widget API (see AbstractWidget) is deprecated. Support will be removed in version 7.0.

property base_widget: AbstractWidget

Return the widget without decorations.

If there is only one Decoration, then this is the same as original_widget. The walk stops at a unittest.mock object.

>>> from urwid import Text
>>> t = Text("hello")
>>> wd1 = WidgetDecoration(t)
>>> wd2 = WidgetDecoration(wd1)
>>> wd3 = WidgetDecoration(wd2)
>>> wd3.original_widget is wd2
True
>>> wd3.base_widget is t
True
enter_selectable(*, reverse: bool = False) → None

Forward the entry hook to original_widget, when it has one.

A container with select_navigation calls this when it moves its focus to this widget, see Moving the Focus with Tab. The hook goes to the widget that keypress() sends keys to: a subclass that sends keys elsewhere overrides this method to match.

Parameters:

reverse – start at the last selectable widget instead of the first one.

property original_widget: WrappedWidget

Return the widget this decoration wraps.

selectable() → bool

Return whether the wrapped widget can take the input focus.

sizing() → frozenset[Sizing]

Return the set of sizing modes supported by the wrapped widget.

WidgetContainerMixin

class urwid.WidgetContainerMixin(*args, **kwargs)

Mixin class for widget containers implementing common container methods.

abstract property focus: AbstractWidget | None

Read-only property returning the child widget in focus for container widgets.

This default implementation always returns None, indicating that this widget has no children.

get_focus_path() → list[int | str]

Return the .focus_position values starting from this container.

Proceeds along each child widget until reaching a leaf (non-container) widget.

get_focus_widgets() → list[WidgetContainerMixin[Any] | AbstractWidget]

Return the .focus values starting from this container.

Proceeds along each child widget until reaching a leaf (non-container) widget.

Note that the list does not contain the topmost container widget (i.e., on which this method is called), but does include the lowest leaf widget.

set_focus_path(positions: Iterable[int | str]) → None

Set the .focus_position property starting from this container widget.

Proceeds along newly focused child widgets. Any failed assignment due to incompatible position types or invalid positions will raise an IndexError.

This method may be used to restore a particular widget to the focus by passing in the value returned from an earlier call to get_focus_path().

Parameters:

positions – sequence of positions

SelectNavigationMixin

class urwid.SelectNavigationMixin(*args, **kwargs)

Mixin for container widgets that handle the 'next selectable' and 'prev selectable' commands.

The behaviour is described in Moving the Focus with Tab. A container class that uses it is a Widget and:

SelectNavigationListContentsMixin implements both methods for containers with list contents.

A container that keeps the Widget.focus_position of Widget, which raises IndexError, is a single stop instead: the select commands are returned unhandled, its focus is not changed, and a WidgetWarning names the class.

abstractmethod _select_candidates(*, reverse: bool, from_edge: bool) → Iterator[Any]

Yield the positions of the selectable children in the traversal order.

Positions are produced lazily, so the search stops at the first one used.

Parameters:
  • reverse – yield them from the last to the first.

  • from_edge – start at the first (last when reverse) position instead of after the focus.

abstractmethod _select_enter_focus(*, reverse: bool) → None

Call enter_selectable(reverse=reverse) of the child in focus, when it has one.

_select_keypress_unhandled(key: str) → str | None

Handle a select command that the child in focus returned unhandled.

Move the focus to the next (previous) selectable child and enter it. Past the last (first) one, focus the first (last) selectable child, enter it and return key, so an opted-in parent moves on to its own next child.

enter_selectable(*, reverse: bool = False) → None

Start at the first selectable child, or at the last one when reverse is set.

It is the entry hook a parent container calls, and acts only when select_navigation is set, see Moving the Focus with Tab.

Parameters:

reverse – start at the last selectable child instead of the first one.

focus_first_selectable() → bool

Focus the first selectable child through the focus_position setter and enter it.

It acts whether or not select_navigation is set, see Moving the Focus with Tab.

Returns:

whether a selectable child exists.

focus_last_selectable() → bool

Focus the last selectable child through the focus_position setter and enter it.

It acts whether or not select_navigation is set, see Moving the Focus with Tab.

Returns:

whether a selectable child exists.

select_navigation: bool = False

Whether the container handles the 'next selectable' and 'prev selectable' commands. The urwid containers set it from their select_navigation argument; the class default keeps a subclass that does not set it working, with the commands left unhandled.

SelectNavigationListContentsMixin

class urwid.SelectNavigationListContentsMixin(*args, **kwargs)

SelectNavigationMixin for containers whose positions are indexes into self.contents.

It implements _select_candidates() and _select_enter_focus() from contents and focus_position only, in the order of contents. A child whose options start with (GIVEN, 0) is not drawn, so it is not a candidate.

abstract property contents: MutableSequence[_ContentsItem]

Return the contents of container as a list of (widget, options).

abstract property focus_position: int

Index of child widget in focus.

Basic Widget Classes

Text

class urwid.Text(markup: _TagMarkup, align: Literal['left', 'center', 'right'] | Align = Align.LEFT, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE, layout: text_layout.TextLayout | None = None, *, selectable: bool = False)

A horizontally resizeable text widget.

Parameters:
  • markup –

    content of text widget, one of:

    bytes or unicode

    text to be displayed

    (display attribute, text markup)

    text markup with display attribute applied to all parts of text markup with no display attribute already applied

    [text markup, text markup, … ]

    all text markup in the list joined together

  • align – typically 'left', 'center' or 'right'

  • wrap – typically 'space', 'any', 'clip' or 'ellipsis'

  • layout – defaults to a shared StandardTextLayout instance

  • selectable – True makes the widget take the focus, for example as a row of a list that is chosen by moving the focus to it; it shows no cursor and returns every key

>>> Text("Hello")
<Text fixed/flow widget 'Hello'>
>>> t = Text(("bold", "stuff"), "right", "any")
>>> t
<Text fixed/flow widget 'stuff' align='right' wrap='any'>
>>> print(t.text)
stuff
>>> t.attrib
[('bold', 5)]
>>> Text("Row", selectable=True)
<Text selectable fixed/flow widget 'Row'>
property attrib: list[tuple[Hashable, int]]

Read-only property returning the run-length encoded display attributes of this widget.

get_line_translation(maxcol: int, ta: tuple[str | bytes, list[tuple[Hashable, int]]] | None = None) → list[list[tuple[int, int, int | bytes] | tuple[int, int | None]]]

Return layout structure used to map self.text to a canvas.

This method is used internally, but may be useful for debugging custom layout classes.

Parameters:
  • maxcol – columns available for display

  • ta – None or the (text, display attributes) tuple returned from get_text()

get_text() → tuple[str | bytes, list[tuple[Hashable, int]]]

Return (text, display attributes).

text

complete bytes/unicode content of text widget

display attributes

run length encoded display attributes for text, eg. [('attr1', 10), ('attr2', 5)]; nested markup gives a LayeredAttr of the inner and the enclosing attributes

>>> Text("Hello").get_text()
('Hello', [])
>>> Text(("bright", "Headline")).get_text()
('Headline', [('bright', 8)])
>>> Text([("a", "one"), "two", ("b", "three")]).get_text()
('onetwothree', [('a', 3), (None, 3), ('b', 5)])
>>> Text(("a", ["one", ("b", "two")])).get_text()
('onetwo', [('a', 3), (LayeredAttr('b', 'a'), 3)])
property layout: TextLayout

Return the TextLayout instance used to lay out and wrap this widget’s text.

pack(size: tuple[()] | tuple[int] | None = None, focus: bool = False) → tuple[int, int]

Return the number of screen columns and rows required for this Text widget.

The widget is expected to be displayed without wrapping or clipping, as a single element tuple. With maxcol the size is taken from the layout at that width. Without it the width is searched for:

maxcol = text width + 1, with StandardTextLayout tabs at their widest
    |
    v
lay out at maxcol --> layout has pack()? --no--> widest line, line count
    ^                        |
    |                       yes
    |                        v
double maxcol <--no-- layout.pack() < maxcol? --yes--> pack() columns, line count
Parameters:
  • size – None or () for unlimited screen columns (like FIXED sizing) or (maxcol,) to specify a maximum column size

  • focus – widget is focused on

>>> Text("important things").pack()
(16, 1)
>>> Text("important things").pack((15,))
(9, 2)
>>> Text("important things").pack((8,))
(8, 2)
>>> Text("important things").pack(())
(16, 1)
>>> not_common_separated_text = (
...     "Line feed" + chr(10) + "Line Separator" + chr(0x2028) + "Paragraph Separator" + chr(0x2029)
... )
>>> # U+2028 (Line Separator) and U+2029 (Paragraph Separator) are not splitted by StandardTextLayout
>>> not_common_separated = Text(not_common_separated_text)
>>> not_common_separated.pack()
(33, 2)
>>> not_common_separated_canv = not_common_separated.render(())
>>> not_common_separated_canv.cols()
33
>>> not_common_separated_canv.rows()
2
render(size: tuple[int] | tuple[()], focus: bool = False) → TextCanvas

Render contents with wrapping and alignment. Return canvas.

See Widget.render() for parameter details.

>>> Text("important things").render((18,)).text
[b'important things  ']
>>> Text("important things").render((11,)).text
[b'important  ', b'things     ']
>>> Text("demo text").render(()).text
[b'demo text']
rows(size: tuple[int], focus: bool = False) → int

Return the number of rows the rendered text requires.

See Widget.rows() for parameter details.

>>> Text("important things").rows((18,))
1
>>> Text("important things").rows((11,))
2
set_align_mode(mode: Literal['left', 'center', 'right'] | Align) → None

Set text alignment mode.

Supported modes depend on text layout object in use but defaults to a StandardTextLayout instance.

Parameters:

mode – typically 'left', 'center' or 'right'

Raises:

TextError – mode is not supported by the layout in use.

>>> t = Text("word")
>>> t.set_align_mode("right")
>>> t.align
'right'
>>> t.render((10,)).text  # ... = b in Python 3
[...'      word']
>>> t.align = "center"
>>> t.render((10,)).text
[...'   word   ']
>>> t.align = "somewhere"
Traceback (most recent call last):
TextError: Alignment mode 'somewhere' not supported.
set_layout(align: Literal['left', 'center', 'right'] | Align, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode, layout: text_layout.TextLayout | None = None) → None

Set the text layout object, alignment and wrapping modes at the same time.

Parameters:
  • align – typically ‘left’, ‘center’ or ‘right’

  • wrap – typically ‘space’, ‘any’, ‘clip’ or ‘ellipsis’

  • layout – defaults to a shared StandardTextLayout instance

>>> t = Text("hi")
>>> t.set_layout("right", "clip")
>>> t
<Text fixed/flow widget 'hi' align='right' wrap='clip'>
set_text(markup: _TagMarkup) → None

Set content of text widget.

Parameters:

markup – see Text for description.

>>> t = Text("foo")
>>> print(t.text)
foo
>>> t.set_text("bar")
>>> print(t.text)
bar
>>> t.text = "baz"  # not supported because text stores text but set_text() takes markup
Traceback (most recent call last):
AttributeError: can't set attribute
set_wrap_mode(mode: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode) → None

Set text wrapping mode.

Supported modes depend on text layout object in use but defaults to a StandardTextLayout instance.

Parameters:

mode – typically 'space', 'any', 'clip' or 'ellipsis'

Raises:

TextError – mode is not supported by the layout in use.

>>> t = Text("some words")
>>> t.render((6,)).text  # ... = b in Python 3
[...'some  ', ...'words ']
>>> t.set_wrap_mode("clip")
>>> t.wrap
'clip'
>>> t.render((6,)).text
[...'some w']
>>> t.wrap = "any"  # Urwid 0.9.9 or later
>>> t.render((6,)).text
[...'some w', ...'ords  ']
>>> t.wrap = "somehow"
Traceback (most recent call last):
TextError: Wrap mode 'somehow' not supported.
property text: str | bytes

Read-only property returning the complete bytes/unicode content of this widget.

Edit

class urwid.Edit(caption: _TagMarkup = '', edit_text: str = '', multiline: bool = False, align: Literal['left', 'center', 'right'] | Align = Align.LEFT, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE, allow_tab: bool = False, edit_pos: int | None = None, layout: text_layout.TextLayout | None = None, mask: str | None = None, *, expand_tabs: bool = True, max_length: int | None = None, placeholder: _TagMarkup | None = None, placeholder_grow: bool | None = None, validator: Callable[[str], ValidationMessage | str | Iterable[ValidationMessage] | None] | None = None, validation_attr: Mapping[Severity, _AttrMapLike] | None = None, validation_focus_attr: Mapping[Severity, _AttrMapLike] | None = None, text_completer: Callable[[str, int], Sequence[tuple[str, int]]] | None = None, undo_limit: int = 0, kill_buffer: MutableSequence[str | bytes] | None = None)

Text editing widget implements cursor movement, text insertion and deletion.

A caption may prefix the editing area. Uses text class for text layout.

Users of this class may listen for "change" or "postchange" events. See :func:connect_signal.

  • "change" is sent just before the value of edit_text changes. It receives the new text as an argument. Note that "change" cannot change the text in question as edit_text changes the text afterwards.

  • "postchange" is sent after the value of edit_text changes. It receives the old value of the text as an argument and thus is appropriate for changing the text. It is possible for a "postchange" event handler to get into a loop of changing the text and then being called when the event is re-emitted. It is up to the event handler to guard against this case (for instance, by not changing the text if it is signaled for text that it has already changed once).

  • "submit" is sent when enter, mapped to 'activate', reaches an Edit with multiline=False. It receives the edit text as an argument. keypress() still returns the key.

  • "cancel" is sent when a key mapped to 'menu', esc by default, reaches an Edit. It receives the edit text as an argument. keypress() still returns the key.

  • "validation" is sent when validation changes. It receives the old and the new value as arguments.

  • "completion_cycle" is sent when a completion cycle starts, moves or ends, see text_completer. It receives the new value of completion_cycle as an argument.

Create the Edit widget.

Parameters:
  • caption – markup for caption preceding edit_text, see Text for description of text markup.

  • edit_text – initial text for editing, type (bytes or str) must match the text in the caption

  • multiline – True: enter inserts a newline, False: return it

  • align – typically ‘left’, ‘center’ or ‘right’

  • wrap – typically ‘space’, ‘any’ or ‘clip’

  • allow_tab – True: tab is inserted, False: return it

  • edit_pos – initial position for cursor, None:end of edit_text

  • layout – defaults to a shared StandardTextLayout instance

  • mask – hide text entered with this character, None:disable mask

  • expand_tabs – True: an inserted tab is spaces up to the next tab stop of the line, False: a tab character

  • max_length – the longest edit_text allowed, see max_length

  • placeholder – markup drawn after the caption while edit_text is empty, see placeholder

  • placeholder_grow – whether a placeholder taller than the empty edit adds rows, see placeholder_grow

  • validator – called with edit_text to check it, see validator

  • validation_attr – attribute map applied for each Severity, see validation_attr

  • validation_focus_attr – attribute map applied for each Severity while focused, see validation_focus_attr

  • text_completer – called with edit_text and edit_pos for completions, see text_completer

  • undo_limit – the number of changes undo() can take back, see undo_limit

  • kill_buffer – list that the word and line delete commands put the deleted text into, see kill_buffer

Raises:
  • ValueError – max_length is negative, or edit_text is longer than it. undo_limit is negative.

  • TagMarkupException – placeholder is not valid text markup.

  • UnicodeError – the text of placeholder cannot be converted to the type of caption.

  • TypeError – validator returned something else than its documented return types for edit_text.

  • AttrMapError – a key or value of an attribute map of validation_attr or validation_focus_attr is not hashable.

>>> Edit()
<Edit selectable flow widget '' edit_pos=0>
>>> Edit("Y/n? ", "yes")
<Edit selectable flow widget 'yes' caption='Y/n? ' edit_pos=3>
>>> Edit("Name ", "Smith", edit_pos=1)
<Edit selectable flow widget 'Smith' caption='Name ' edit_pos=1>
>>> Edit("", "3.14", align="right")
<Edit selectable flow widget '3.14' align='right' edit_pos=4>
>>> pin = Edit("PIN: ", "123", max_length=4)
>>> pin.keypress((20,), "4")
>>> pin.keypress((20,), "5")
'5'
>>> print(pin.edit_text)
1234
>>> Edit("Address: ", placeholder="10.0.0.1").render((20,)).text
[b'Address: 10.0.0.1   ']
property attrib: list[tuple[Hashable, int]]

Read-only property returning the run-length encoded display attributes of this widget.

property caption: str | bytes

Read-only property returning the caption for this widget.

property completion_cycle: tuple[tuple[tuple[str | bytes, int], ...], int] | None

(candidates, index) while a completion cycle runs, None otherwise.

A completer result with one replacement starts no cycle.

candidates holds (text, position) pairs. The first one is the text and cursor position the cycle started from, the others are what text_completer returned, converted to the type of the caption. index is the candidate the edit shows now.

property edit_pos

Property controlling the edit position for this widget.

property edit_text: str

Property controlling the edit text for this widget.

get_cursor_coords(size: tuple[int]) → tuple[int, int] | None

Return the (x, y) coordinates of cursor within widget, or None when it has no columns to show it in.

>>> Edit("? ", "yes").get_cursor_coords((10,))
(5, 0)
>>> Edit("? ", "yes").get_cursor_coords((0,)) is None
True
get_edit_text() → str

Return the edit text for this widget.

>>> e = Edit("What? ", "oh, nothing.")
>>> print(e.get_edit_text())
oh, nothing.
>>> print(e.edit_text)
oh, nothing.
get_line_translation(maxcol: int, ta: tuple[str | bytes, list[tuple[Hashable, int]]] | None = None) → list[list[tuple[int, int, int | bytes] | tuple[int, int | None]]]

Return the line translation for ta, shifted to keep the cursor visible if needed.

get_pref_col(size: tuple[int]) → int

Return the preferred column for the cursor, or the current cursor x value.

May also return 'left' or 'right' to indicate the leftmost or rightmost column available.

This method is used internally and by other widgets when moving the cursor up or down between widgets so that the column selected is one that the user would expect.

>>> size = (10,)
>>> Edit().get_pref_col(size)
0
>>> e = Edit("", "word")
>>> e.get_pref_col(size)
4
>>> e.keypress(size, "left")
>>> e.get_pref_col(size)
3
>>> e.keypress(size, "end")
>>> e.get_pref_col(size)
<Align.RIGHT: 'right'>
>>> e = Edit("", "2\nwords")
>>> e.keypress(size, "left")
>>> e.keypress(size, "up")
>>> e.get_pref_col(size)
4
>>> e.keypress(size, "left")
>>> e.get_pref_col(size)
0
get_text() → tuple[str | bytes, list[tuple[Hashable, int]]]

Return (text, display attributes).

See Text.get_text() for details.

Text returned includes the caption and edit_text, possibly masked.

>>> Edit("What? ", "oh, nothing.").get_text()
('What? oh, nothing.', [])
>>> Edit(("bright", "user@host:~$ "), "ls").get_text()
('user@host:~$ ls', [('bright', 13)])
>>> Edit("password:", "seekrit", mask="*").get_text()
('password:*******', [])
insert_text(text: str) → None

Insert text at the cursor position and update cursor.

This method is used by the keypress() method when inserting one or more characters into edit_text.

Parameters:

text – text for inserting, type (bytes or unicode) must match the text in the caption

Raises:

ValueError – the result is longer than max_length; the text is left as it was.

>>> e = Edit("", "42")
>>> e.insert_text(".5")
>>> e
<Edit selectable flow widget '42.5' edit_pos=4>
>>> e.set_edit_pos(2)
>>> e.insert_text("a")
>>> print(e.edit_text)
42a.5
insert_text_result(text: str) → tuple[str | bytes, int]

Return result of insert_text(text) without actually performing the insertion.

Handy for pre-validation. When the inserted text joins the grapheme cluster after it, the returned position is the end of that cluster.

Parameters:

text – text for inserting, type (bytes or unicode) must match the text in the caption

Raises:

ValueError – text cannot be inserted at the current edit position. The message carries the texts involved unless a mask is set.

keypress(size: tuple[int], key: str) → str | None

Handle editing keystrokes, return others.

A key whose text would make edit_text longer than max_length inserts nothing and is returned, as IntEdit returns a digit past its limit. A tab is inserted whole or not at all.

The readline delete commands described in CommandMap and 'yank' go through set_edit_text(). A subclass whose set_edit_text() raises for some text has to handle those commands itself once an application binds them, or the exception leaves keypress().

A key that completes steps the completion cycle, and any other key ends it, see text_completer.

A key that changes the text is one step of the undo history, see undo_limit. A subclass that changes the text after this method returns makes a change from elsewhere, which clears the history; it makes the change in an override of set_edit_text() or insert_text() instead.

>>> e, size = Edit(), (20,)
>>> e.keypress(size, "x")
>>> e.keypress(size, "left")
>>> e.keypress(size, "1")
>>> print(e.edit_text)
1x
>>> e.keypress(size, "backspace")
>>> e.keypress(size, "end")
>>> e.keypress(size, "2")
>>> print(e.edit_text)
x2
>>> e.keypress(size, "shift f1")
'shift f1'
Raises:
  • ValueError – the preferred cursor column could not be determined. A completion is longer than max_length.

  • Exception – what set_edit_text() raises for a completion, see text_completer. What it raises for the text of 'undo', 'redo' or 'yank', see undo().

property kill_buffer: MutableSequence[str | bytes] | None

The list that the readline word and line delete commands put the deleted text into, or None for none.

The application creates the list, and several edits may share it. Deleted text joins the newest entry while the deletes follow one another in this edit, as in readline: text deleted forward is appended to it, text deleted backward is put before it. Any other key, a mouse event, a change from elsewhere, or a change of the list made by someone else starts a new entry. A masked edit puts nothing into the list. backspace, delete and 'delete char right' delete without putting the text into the list.

The 'yank' command, which no key is mapped to by default, inserts the newest entry at the cursor. The entry is not inserted, and the key is returned, when the keys of this edit could not type the result: a newline in an edit without multiline, a letter in an IntEdit, text that makes edit_text longer than max_length.

>>> from urwid import CommandMap
>>> class ReadlineEdit(Edit):
...     _command_map = CommandMap()
...     _command_map.bind_readline()
>>> kills = []
>>> e = ReadlineEdit("", "one two three", kill_buffer=kills)
>>> e.keypress((20,), "ctrl w")
>>> e.keypress((20,), "ctrl w")
>>> kills
['two three']
>>> e.keypress((20,), "ctrl y")
>>> print(e.edit_text)
one two three
property layout: TextLayout

Return the TextLayout instance used to lay out and wrap this widget’s text.

property max_length: int | None

The longest edit_text allowed, or None for no limit.

It is counted like edit_pos: in code points, or in bytes when the caption is bytes. A key that would go over it is refused, see keypress(); set_edit_text() and insert_text() raise ValueError instead. Setting it negative or below the length of edit_text raises ValueError too.

mouse_event(size: tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Move the cursor to the location clicked for button 1.

>>> size = (20,)
>>> e = Edit("", "words here")
>>> e.mouse_event(size, "mouse press", 1, 2, 0, True)
True
>>> e.edit_pos
2
move_cursor_to_coords(size: tuple[int], x: int | Literal[Align.LEFT, Align.RIGHT], y: int) → bool

Set the cursor position with (x,y) coordinates.

Returns:

True if move succeeded, False otherwise.

>>> size = (10,)
>>> e = Edit("", "edit\ntext")
>>> e.move_cursor_to_coords(size, 5, 0)
True
>>> e.edit_pos
4
>>> e.move_cursor_to_coords(size, 5, 3)
False
>>> e.move_cursor_to_coords(size, 0, 1)
True
>>> e.edit_pos
5
property placeholder: _TagMarkup | None

Markup drawn after the caption while edit_text is empty, or None for none.

It is drawn focused or not, and it is never masked. It is not part of the text: get_text(), edit_text, max_length and pack() ignore it. It is drawn after caption and its attributes, not after the caption part of get_text(). The cursor stays where it is without a placeholder, so with align="left" it is on its first column. With align="center" or align="right" the placeholder moves the caption left, and the cursor is inside the placeholder or after it. The placeholder is not drawn when the caption alone does not leave the cursor a column to show in.

Raises:
  • TagMarkupException – on assignment, the markup is not valid text markup.

  • UnicodeError – on assignment, the text cannot be converted to the type of the caption.

property placeholder_grow: bool | None

Whether a placeholder laid out on more rows than the empty edit adds rows.

False cuts the placeholder to the rows of the empty edit, so the height does not change on the first key. True adds rows while the edit is empty. None grows a multiline edit and cuts a single-line one.

position_coords(maxcol: int, pos: int) → tuple[int, int]

Return (x, y) coordinates for an offset into self.edit_text.

redo() → bool

Make the last change undo() took back again.

Any new change clears what there is to redo.

Returns:

whether the text changed; False when there is nothing to redo, or the text to restore is longer than max_length.

Raises:

Exception – what set_edit_text() raises for the restored text. When it raises before changing the text, the history is left as it was.

render(size: tuple[int], focus: bool = False) → TextCanvas | CompositeCanvas

Render edit widget and return canvas.

Include cursor when in focus.

>>> edit = Edit("? ", "yes")
>>> c = edit.render((10,), focus=True)
>>> c.text
[b'? yes     ']
>>> c.cursor
(5, 0)
rows(size: tuple[int], focus: bool = False) → int

Return the number of rows the rendered edit requires, including a placeholder that grows it.

>>> Edit("", multiline=True, placeholder="line 1\nline 2").rows((20,))
2
selectable() → bool

Return True: this widget is always selectable.

Wrapped Text widget is not selectable while the edit widget has to be selectable.

set_align_mode(mode: Literal['left', 'center', 'right'] | Align) → None

Set text alignment mode. See Text.set_align_mode() for details.

set_caption(caption: _TagMarkup) → None

Set the caption markup for this widget.

Parameters:

caption – markup for caption preceding edit_text, see Text.__init__() for description of text markup.

>>> e = Edit("")
>>> e.set_caption("cap1")
>>> print(e.caption)
cap1
>>> e.set_caption(("bold", "cap2"))
>>> print(e.caption)
cap2
>>> e.attrib
[('bold', 4)]
>>> e.caption = "cap3"  # not supported because caption stores text but set_caption() takes markup
Traceback (most recent call last):
AttributeError: can't set attribute
set_edit_pos(pos: int) → None

Set the cursor position with a self.edit_text offset.

Clips pos to [0, len(edit_text)], then moves a position inside a grapheme cluster back to the cluster start, so that the cursor never splits what is drawn as one character.

Parameters:

pos – cursor position

>>> e = Edit("", "word")
>>> e.edit_pos
4
>>> e.set_edit_pos(2)
>>> e.edit_pos
2
>>> e.edit_pos = -1  # Urwid 0.9.9 or later
>>> e.edit_pos
0
>>> e.edit_pos = 20
>>> e.edit_pos
4
>>> e = Edit("", "e" + chr(0x301) + "x")  # "e" followed by a combining acute accent
>>> e.set_edit_pos(1)
>>> e.edit_pos
0
set_edit_text(text: str, *, edit_pos: int | None = None) → None

Set the edit text for this widget.

Parameters:
  • text – text for editing, type (bytes or unicode) must match the text in the caption

  • edit_pos – cursor position to set before "postchange" is sent, clipped as by set_edit_pos(); None keeps the current position, clipped to the new text. An override passes None on unchanged: an editing key gives its position to this method by another route, which an explicit position replaces.

Raises:
  • ValueError – text is longer than max_length; the text is left as it was.

  • TypeError – validator returned something else than the types it lists; the text is changed already.

Called from elsewhere than a key, undo() or redo(), it clears the undo history, see undo_limit.

>>> e = Edit()
>>> e.set_edit_text("yes")
>>> print(e.edit_text)
yes
>>> e
<Edit selectable flow widget 'yes' edit_pos=0>
>>> e.edit_text = "no"  # Urwid 0.9.9 or later
>>> print(e.edit_text)
no
>>> e.set_edit_text("maybe", edit_pos=5)
>>> e.edit_pos
5
set_layout(align: Literal['left', 'center', 'right'] | Align, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode, layout: text_layout.TextLayout | None = None) → None

Set the text layout object, alignment and wrapping modes at the same time.

See Text.set_layout() for details.

set_mask(mask: str | None) → None

Set the character for masking text away.

Parameters:

mask – hide text entered with this character, None:disable mask

set_wrap_mode(mode: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode) → None

Set text wrapping mode. See Text.set_wrap_mode() for details.

property severity: Severity | None

The worst Severity in validation, or None when it is empty.

sizing() → frozenset[Sizing]

Sizing information.

Edit widget sizing is narrower than Text: we cannot predict the final input length.

property text: str | bytes

Read-only property returning the complete bytes/unicode content of this widget.

Includes caption plus edit_text, possibly masked.

property text_completer: Callable[[str, int], Sequence[tuple[str, int]]] | None

Callable that returns the completions of edit_text, or None for no completion.

It is called with the edit text and the cursor position. It returns whole replacements, each a (text, position) pair, and decides itself what is completed. A replacement equal to the current text and position is left out.

Keys that complete:

  • tab and shift tab, unless allow_tab is set;

  • the 'complete next' and 'complete previous' commands, which no key is mapped to by default.

A completion key with no cycle running calls the completer:

  • when it returns nothing, the key is returned, so that focus navigation still works;

  • when it returns one replacement, that replacement is applied and no cycle starts, so the next completion key calls the completer again from the new text;

  • when it returns several, a cycle starts with the first replacement, or the last one for 'complete previous'.

A completer that returns the common prefix of several matches as its one replacement, and the matches once the prefix is complete, inserts the prefix first and then cycles through the matches. Further completion keys step through the replacements and back to the text the cycle started from. Any other key ends the cycle, and so does a change of the text or the cursor made from elsewhere. Each step sets the text, so validator runs once per step. Setting the completer ends the cycle.

A step applies a replacement through set_edit_text(). An override may raise there for a replacement it does not accept, before it changes the text. The exception then leaves keypress(). The cycle stays where it was. The next completion key tries the same replacement again. A completer for such a subclass returns only replacements the subclass accepts.

>>> words = ["alpha", "alpine", "beta"]
>>> def complete(text, pos):
...     return [(w, len(w)) for w in words if w.startswith(text[:pos])]
>>> e = Edit("", "al", text_completer=complete)
>>> e.keypress((20,), "tab")
>>> print(e.edit_text)
alpha
>>> e.keypress((20,), "tab")
>>> e.keypress((20,), "tab")
>>> print(e.edit_text)
al
>>> Edit("", "x", text_completer=complete).keypress((20,), "tab")
'tab'
>>> e = Edit("", "b", text_completer=complete)
>>> e.keypress((20,), "tab")
>>> print(e.edit_text)
beta
>>> e.keypress((20,), "tab")
'tab'
undo() → bool

Take back the last change, restoring the text and the cursor position from before it.

See undo_limit. The text goes through set_edit_text(), so signals and validator run as for any change.

Returns:

whether the text changed; False when there is nothing to undo, or the text to restore is longer than max_length.

Raises:

Exception – what set_edit_text() raises for the restored text. When it raises before changing the text, the history is left as it was.

property undo_limit: int

The number of changes undo() can take back, 0 for no undo history.

A change is what a key does to the text, through keypress(). Consecutive typed characters are one change, and so is a run of backspace or of delete. A change from elsewhere, through set_edit_text() or insert_text(), clears the history, so that a reused edit does not bring back an earlier value. Lowering the limit drops the oldest changes, and 0 clears the history. The 'undo' and 'redo' commands, which no key is mapped to by default, call undo() and redo().

>>> e = Edit(undo_limit=10)
>>> for key in "one two":
...     e.keypress((20,), key)
>>> e.keypress((20,), "backspace")
>>> e.undo()
True
>>> print(e.edit_text)
one two
>>> e.undo()
True
>>> print(e.edit_text)

>>> e.redo()
True
>>> print(e.edit_text)
one two
Raises:

ValueError – on assignment, the limit is negative.

valid_char(ch: str) → bool

Filter for text that may be entered into this widget by the user.

Parameters:

ch – character to be inserted

This implementation returns True for a wide character and for any single character that is not a control character (Unicode category Cc): the displays print a control character as ? while the width calculation counts it as zero columns. A single byte is accepted unless it is a C0 control or DEL, because 0x80-0xff are printable characters in a single-byte encoding.

validate() → tuple[ValidationMessage, ...]

Run validator on edit_text now and return the new validation.

This is for a check that depends on something else than the text, which has changed. "validation" is sent when the result differs from the previous one. This class never calls it, so a subclass may define its own validate() for something else.

Raises:

TypeError – the validator returned something else than the types validator lists; validation is left as it was.

property validation: tuple[ValidationMessage, ...]

Findings of validator for the current edit_text; empty when valid or without a validator.

property validation_attr: dict[Severity, dict[Hashable, Hashable]]

Attribute map applied to the rendered edit for each severity.

It is set from a mapping of Severity to what AttrMap takes as its attribute map, or None. A single attribute is stored as {None: attribute}, so the caption’s own attributes are placed over it. A severity with no entry is not coloured. A copy is returned.

>>> e = Edit(validator=lambda text: "empty" if not text else None, validation_attr={Severity.ERROR: "error"})
>>> next(iter(e.render((4,)).content()))
[('error', None, b'    ')]
property validation_focus_attr: dict[Severity, dict[Hashable, Hashable]]

Attribute map applied to the rendered edit for each severity while it is focused.

It is set as validation_attr. A severity with no entry here uses its validation_attr entry when focused. A copy is returned.

property validator: Callable[[str], ValidationMessage | str | Iterable[ValidationMessage] | None] | None

Callable that checks the whole edit_text, or None for no check.

It is called with the edit text and returns its findings:

It is called once by each set_edit_text() call, after "postchange", when it is assigned, and by validate(), never while rendering. A subclass whose set_edit_text() does not call this class’s method calls Edit.validate(self) itself, which a validate() of its own does not replace. The result is validation. Keys, focus, "submit" and "cancel" do not depend on it. An application reads validation to refuse a value.

>>> e = Edit("Port: ", validator=lambda text: None if text.isdecimal() else "not a number")
>>> e.validation
(ValidationMessage(severity=<Severity.ERROR: 3>, message='not a number'),)
>>> e.keypress((20,), "8")
>>> e.validation
()
>>> e.validator = lambda text: ValidationMessage(Severity.WARNING, "privileged") if int(text) < 1024 else None
>>> e.severity
<Severity.WARNING: 2>
Raises:

TypeError – on assignment, the validator returned something else than the types above; the previous validator is kept.

IntEdit

class urwid.IntEdit(caption: _TagMarkup = '', default: int | str | None = None)

Edit widget for integer values.

Create the IntEdit widget.

Parameters:
  • caption – caption markup

  • default – default edit value

>>> IntEdit("", 42)
<IntEdit selectable flow widget '42' edit_pos=2>
insert_text(text: str) → None

Insert text at the cursor position and update the cursor, removing the leading zeros before the cursor.

The result is set at once, so “change” and “postchange” are sent once, and not at all when the text stays the same.

Parameters:

text – text for inserting, type (bytes or unicode) must match the text in the caption

>>> e = IntEdit("", 0)
>>> e.insert_text("7")
>>> e
<IntEdit selectable flow widget '7' edit_pos=1>
keypress(size: tuple[int], key: str) → str | None

Handle editing keystrokes. Remove leading zeros.

A digit is refused when the resulting text would exceed sys.get_int_max_str_digits() digits, so that value() can convert whatever the user typed.

>>> e, size = IntEdit("", 5002), (10,)
>>> e.keypress(size, "home")
>>> e.keypress(size, "delete")
>>> print(e.edit_text)
002
>>> e.keypress(size, "end")
>>> print(e.edit_text)
2
valid_char(ch: str) → bool

Return true for decimal digits.

value() → int

Return the numeric value of self.edit_text.

>>> e, size = IntEdit(), (10,)
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> e.value() == 51
True
Raises:

ValueError – the text was set programmatically to something that is not a decimal integer, or to more digits than sys.get_int_max_str_digits() allows.

TreeWidget

class urwid.TreeWidget(node: _Node_co)

A widget representing something in a nested tree display.

Build the widget representing node, expanded as is_initially_expanded() says.

Deprecated since version 4.0.4: Passing a node that is not a ParentNode but defines get_first_child is deprecated.

Symbols

alias of TreeSymbols

collapse() → None

Hide the children of this node and update the icon.

A leaf has no children, so nothing changes for it. A descendant in focus keeps the focus although it is hidden. Move the focus first, for example with TreeListBox.move_focus_to_parent().

connector_lines: bool = False

Draw lines in the indentation that connect each node to its parent and its siblings.

expand() → None

Show the children of this node and update the icon.

A leaf has no children, so nothing changes for it.

first_child() → TreeWidget[TreeNode[Any]] | None

Return first child if expanded.

get_display_text() → _TagMarkup

Return the text to display for this node, combining its key and value.

get_indent_cols() → int

Return the number of columns this widget is indented by, based on its node’s depth.

get_indented_widget() → Padding[Text | Columns]

Return this node’s widget wrapped with its icon and indentation.

The icon column is as wide as the widest icon. A leaf has an icon column only when leaf_icon is set. With connector_lines set, the lines from get_lines_widget() fill the indentation. On a row too narrow for the indentation and one more column, the lines are left out.

get_inner_widget() → Text

Return the cached inner widget, creating it with load_inner_widget() if necessary.

get_lines_widget() → Widget | None

Return the widget that draws this node’s connector lines, as wide as the indentation.

It is created on the first call and the same widget is returned after that. A layout of a subclass’s own places it at the start of the row, as a box column.

Returns:

the lines widget, or None when connector_lines is not set, for the root, and when indent_cols is 0.

Raises:

TreeWidgetError – a line glyph is not one column wide.

get_node() → _Node_co

Return the TreeNode this widget represents.

is_initially_expanded() → bool

Return whether the widget starts expanded.

The default starts every node expanded. Override it to start collapsed, for example below a given depth. It is called from __init__ once the node is set, before the display widget is built.

keypress(size: tuple[int] | tuple[()], key: str) → str | None

Handle expand & collapse requests (non-leaf nodes).

last_child() → TreeWidget[TreeNode[Any]] | None

Return the last visible descendant if expanded.

The descent is a loop rather than a recursion, so the depth of the tree is not limited by the interpreter’s recursion limit. A descendant widget whose class overrides this method is asked for its own last descendant.

Raises:

TreeWidgetError – the descent reaches a node it already passed, so the tree has a cycle.

leaf_icon: Widget | None = None

The icon of a leaf; with one set, leaves get an icon column too, so their text lines up with a parent’s.

line_attr: Hashable = None

The display attribute of the connector lines.

line_corner: str = '└'

The one-column connector of the last child.

line_horizontal: str = '─'

The one-column line from the connector to the icon.

line_tee: str = '├'

The one-column connector of a node that has a sibling further down.

line_tip: str | None = None

The one-column glyph that ends a connector next to the icon, or None to end it with line_horizontal. With an indent_cols of 1 the connector has no room for it.

line_tip_attr: Hashable = None

The display attribute of line_tip, or None to use line_attr.

line_vertical: str = '│'

The one-column line passing a node that has a sibling further down.

load_inner_widget() → Text

Create the inner widget displaying this node’s text.

mouse_event(size: tuple[int] | tuple[()], event: str, button: int, col: int, row: int, focus: bool) → bool

Toggle expand/collapse when the icon is left-clicked.

next_inorder() → TreeWidget[TreeNode[Any]] | None

Return the next TreeWidget depth first from this one.

Raises:

ValueError – the tree walker returned a node at an unexpected depth.

prev_inorder() → TreeWidget[TreeNode[Any]] | None

Return the previous TreeWidget depth first from this one.

selectable() → bool

Allow selection of non-leaf nodes so children may be (un)expanded.

toggle_expanded() → None

Switch this node between expanded and collapsed.

Nothing changes for a leaf.

update_expanded_icon() → None

Update display widget text for parent widgets.

class urwid.widget.treetools.TreeSymbols(*values)

Common symbols for the icons and the connector lines of TreeWidget.

On a terminal without UTF-8:

Note

The default terminal fonts of GNOME, KDE Plasma and Xfce on Ubuntu, Fedora and Arch, and of Windows Terminal, show every member. The default fonts of the Linux virtual console are:

  • Ubuntu: Fixed 8x16.

  • Fedora and openSUSE: eurlatgr.

  • Arch: the built-in 8x16 font of the kernel.

The default fonts of the Linux virtual console and of the classic Windows console show these non-ASCII members:

Member

Symbol

Ubuntu

Fedora

Arch

Windows

TRIANGLE_RIGHT

▶

yes

no

yes

no

TRIANGLE_DOWN

▼

yes

no

yes

yes

SMALL_TRIANGLE_RIGHT

▸

yes

no

no

no

SMALL_TRIANGLE_DOWN

▾

yes

no

no

no

BULLET

yes

yes

yes

yes

ARROW_TIP

➤

no

no

no

no

LIGHT_VERTICAL

│

yes

yes

yes

yes

LIGHT_TEE

├

yes

yes

yes

yes

LIGHT_CORNER

└

yes

yes

yes

yes

LIGHT_CORNER_ROUNDED

╰

yes

no

no

no

LIGHT_HORIZONTAL

─

yes

yes

yes

yes

HEAVY_VERTICAL

┃

yes

yes

no

no

HEAVY_TEE

┣

yes

yes

no

no

HEAVY_CORNER

┗

yes

yes

no

no

HEAVY_HORIZONTAL

━

yes

yes

no

no

DOUBLE_VERTICAL

║

yes

yes

yes

yes

DOUBLE_TEE

╠

yes

yes

yes

yes

DOUBLE_CORNER

╚

yes

yes

yes

yes

DOUBLE_HORIZONTAL

═

yes

yes

yes

yes

A member marked no needs a console font with wider coverage.

ARROW_TIP = '➤'
BULLET = '•'
DOUBLE_CORNER = '╚'
DOUBLE_HORIZONTAL = '═'
DOUBLE_TEE = '╠'
DOUBLE_VERTICAL = '║'
HEAVY_CORNER = '┗'
HEAVY_HORIZONTAL = '━'
HEAVY_TEE = '┣'
HEAVY_VERTICAL = '┃'
LIGHT_CORNER = '└'
LIGHT_CORNER_ROUNDED = '╰'
LIGHT_HORIZONTAL = '─'
LIGHT_TEE = '├'
LIGHT_VERTICAL = '│'
MINUS = '-'
PLUS = '+'
SMALL_TRIANGLE_DOWN = '▾'
SMALL_TRIANGLE_RIGHT = '▸'
SPACE = ' '
TRIANGLE_DOWN = '▼'
TRIANGLE_RIGHT = '▶'

Extended Numerical Editing Classes

IntegerEdit

class urwid.numedit.IntegerEdit(caption: _TagMarkup = '', default: int | str | decimal.Decimal | None = None, base: int = 10, *, allow_negative: bool = False)

Edit widget for integer values.

Initialize an edit widget restricted to integer values.

Parameters:
  • caption – caption markup

  • default – numeric initial value, or a string of digits in the selected base

  • base – the numeric base of the value, from 2 to 36

  • allow_negative – accept a negative default and a leading minus sign in input

Raises:
  • TypeError – base is not an integer.

  • ValueError – base is outside 2 to 36. default is not an int, str or integral decimal.Decimal, does not consist of digits valid for base, is negative without allow_negative, or has more digits than sys.get_int_max_str_digits() allows.

>>> IntegerEdit("", 42)
<IntegerEdit selectable flow widget '42' edit_pos=2>
>>> e, size = IntegerEdit("", "5002"), (10,)
>>> e.keypress(size, "home")
>>> e.keypress(size, "delete")
>>> assert e.edit_text == "002"
>>> e.keypress(size, "end")
>>> assert e.edit_text == "2"
>>> e.keypress(size, "9")
>>> e.keypress(size, "0")
>>> assert e.edit_text == "290"
>>> e, size = IntegerEdit("", ""), (10,)
>>> assert e.value() is None
>>> # binary
>>> e, size = IntegerEdit("", "1010", base=2), (10,)
>>> e.keypress(size, "end")
>>> e.keypress(size, "1")
>>> assert e.edit_text == "10101"
>>> assert e.value() == decimal.Decimal("21")
>>> # HEX
>>> e, size = IntegerEdit("", "10", base=16), (10,)
>>> e.keypress(size, "end")
>>> e.keypress(size, "F")
>>> e.keypress(size, "f")
>>> assert e.edit_text == "10Ff"
>>> assert e.keypress(size, "G") == "G"  # unhandled key
>>> assert e.edit_text == "10Ff"
>>> # keep leading 0's when not base 10
>>> e, size = IntegerEdit("", "10FF", base=16), (10,)
>>> assert e.edit_text == "10FF"
>>> assert e.value() == decimal.Decimal("4351")
>>> e.keypress(size, "home")
>>> e.keypress(size, "delete")
>>> e.keypress(size, "0")
>>> assert e.edit_text == "00FF"
>>> # test exception on incompatible value for base
>>> e, size = IntegerEdit("", "10FG", base=16), (10,)
Traceback (most recent call last):
    ...
ValueError: invalid value: 10FG for base 16
>>> # test exception on float init value
>>> e, size = IntegerEdit("", 10.0), (10,)
Traceback (most recent call last):
    ...
ValueError: default: Only 'str', 'int' or Decimal input allowed
>>> e, size = IntegerEdit("", decimal.Decimal("10.0")), (10,)
Traceback (most recent call last):
    ...
ValueError: not an 'integer Decimal' instance
value() → Decimal | None

Return the numeric value of self.edit_text.

>>> e, size = IntegerEdit(), (10,)
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> assert e.value() == 51

FloatEdit

class urwid.numedit.FloatEdit(caption: _TagMarkup = '', default: str | int | decimal.Decimal | None = None, preserveSignificance: bool | None = None, decimalSeparator: str | None = None, *, preserve_significance: bool = True, decimal_separator: str = '.', allow_negative: bool = False)

Edit widget for float values.

Initialize a decimal edit widget.

Parameters:
  • caption – caption markup

  • default – default edit value. Strings must use the configured decimal separator.

  • preserveSignificance – deprecated positional form of preserve_significance

  • decimalSeparator – deprecated positional form of decimal_separator

  • preserve_significance – return value has the same signif. as default

  • decimal_separator – use ‘.’ as separator by default, optionally a ‘,’

  • allow_negative – accept a leading minus sign in string defaults and input

Raises:

ValueError – default is not an int, str or decimal.Decimal. default is a string with the other decimal separator, is not a number, or is not a finite value. decimal_separator is neither '.' nor ','.

Deprecated since version 2.2.3: The preserveSignificance and decimalSeparator arguments are deprecated, use the preserve_significance and decimal_separator keyword arguments instead. The arguments will be removed in version 7.0.

>>> FloatEdit("", "1.065434")
<FloatEdit selectable flow widget '1.065434' edit_pos=8>
>>> e, size = FloatEdit("", "1.065434"), (10,)
>>> e.keypress(size, "home")
>>> e.keypress(size, "delete")
>>> assert e.edit_text == ".065434"
>>> e.keypress(size, "end")
>>> e.keypress(size, "backspace")
>>> assert e.edit_text == ".06543"
>>> e, size = FloatEdit(), (10,)
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> e.keypress(size, ".")
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> assert e.value() == decimal.Decimal("51.51"), e.value()
>>> e, size = FloatEdit(decimal_separator=":"), (10,)
Traceback (most recent call last):
    ...
ValueError: invalid decimal separator: :
>>> e, size = FloatEdit(decimal_separator=","), (10,)
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> e.keypress(size, ",")
>>> e.keypress(size, "5")
>>> e.keypress(size, "1")
>>> assert e.edit_text == "51,51"
>>> e, size = FloatEdit("", "3.1415", preserve_significance=True), (10,)
>>> e.keypress(size, "end")
>>> e.keypress(size, "backspace")
>>> e.keypress(size, "backspace")
>>> assert e.edit_text == "3.14"
>>> assert e.value() == decimal.Decimal("3.1400")
>>> e.keypress(size, "1")
>>> e.keypress(size, "5")
>>> e.keypress(size, "9")
>>> assert e.value() == decimal.Decimal("3.1416"), e.value()
>>> e, size = FloatEdit("", ""), (10,)
>>> assert e.value() is None
>>> e, size = FloatEdit("", 10.0), (10,)
Traceback (most recent call last):
    ...
ValueError: default: Only 'str', 'int' or Decimal input allowed
valid_char(ch: str) → bool

Return true for allowed characters, refusing a second decimal separator.

value() → Decimal | None

Return the numeric value of self.edit_text.

Control Widget Classes

Button

class urwid.Button(label: _TagMarkup, on_press: Callable[[Self, _T], Any] | None = None, user_data: _T = ..., *, align: Literal['left', 'center', 'right'] | Align = ..., wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = ..., layout: TextLayout | None = ..., cursor_position: int | None = ..., left: _TagMarkup | None = ..., right: _TagMarkup | None = ...)
class urwid.Button(label: _TagMarkup, on_press: Callable[[Self], Any] | None = None, user_data: None = None, *, align: Literal['left', 'center', 'right'] | Align = ..., wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = ..., layout: TextLayout | None = ..., cursor_position: int | None = ..., left: _TagMarkup | None = ..., right: _TagMarkup | None = ...)

Selectable widget that emits a click signal when activated.

Create the Button widget.

Parameters:
  • label – markup for button label

  • on_press – shorthand for connect_signal() function call for a single callback

  • user_data – user_data for on_press

  • align – typically 'left', 'center' or 'right'

  • wrap – typically 'space', 'any', 'clip' or 'ellipsis'

  • layout – defaults to a shared StandardTextLayout instance

  • cursor_position – position of the cursor in the label when the button is in focus; None shows no cursor, see SelectableIcon

  • left – markup for the left bracket, in a column as wide as its text; None uses button_left in a column of width 1, and markup with no text leaves out the bracket together with the space after it

  • right – markup for the right bracket, the same way as left with button_right

..note:: pack method expect, that Columns backend widget is not modified from outside

Signals supported: 'click'

Register signal handler with:

urwid.connect_signal(button, "click", callback, user_data)

where callback is callback(button [,user_data]) Unregister signal handlers with:

urwid.disconnect_signal(button, "click", callback, user_data)
>>> from urwid.util import set_temporary_encoding
>>> Button("Ok")
<Button selectable fixed/flow widget 'Ok'>
>>> b = Button("Cancel")
>>> b.render((15,), focus=True).text  # ... = b in Python 3
[b'< Cancel      >']
>>> aligned_button = Button("Test", align=Align.CENTER)
>>> aligned_button.render((10,), focus=True).text
[b'<  Test  >']
>>> wrapped_button = Button("Long label", wrap=WrapMode.ELLIPSIS)
>>> with set_temporary_encoding("utf-8"):
...     wrapped_button.render((7,), focus=False).text[0].decode("utf-8")
'< Lo… >'
>>> Button("Ok", left="[[", right="]]").render((8,)).text
[b'[[ Ok ]]']
>>> Button("Link", left="", right="").render((4,)).text
[b'Link']
>>> Button("Ok", cursor_position=None).render((6,), focus=True).cursor
get_label() → str | bytes

Return label text.

>>> b = Button("Ok")
>>> print(b.get_label())
Ok
>>> print(b.label)
Ok
keypress(size: tuple[int], key: str) → str | None

Send ‘click’ signal on ‘activate’ command.

>>> assert Button._command_map[" "] == "activate"
>>> assert Button._command_map["enter"] == "activate"
>>> size = (15,)
>>> b = Button("Cancel")
>>> clicked_buttons = []
>>> def handle_click(button):
...     clicked_buttons.append(button.label)
>>> key = connect_signal(b, "click", handle_click)
>>> b.keypress(size, "enter")
>>> b.keypress(size, " ")
>>> clicked_buttons
['Cancel', 'Cancel']
property label: str | bytes

Return label text.

>>> b = Button("Ok")
>>> print(b.get_label())
Ok
>>> print(b.label)
Ok
mouse_event(size: tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool

Send ‘click’ signal on button 1 press.

>>> size = (15,)
>>> b = Button("Ok")
>>> clicked_buttons = []
>>> def handle_click(button):
...     clicked_buttons.append(button.label)
>>> key = connect_signal(b, "click", handle_click)
>>> b.mouse_event(size, "mouse press", 1, 4, 0, True)
True
>>> b.mouse_event(size, "mouse press", 2, 4, 0, True)  # ignored
False
>>> clicked_buttons
['Ok']
pack(size: tuple[()] | tuple[int] | None = (), focus: bool = False) → tuple[int, int]

Pack for widget.

Parameters:
  • size – size data. Special case: () - get minimal widget size to fit

  • focus – widget is focused

Returns:

the (columns, rows) the widget needs.

>>> btn = Button("Some button")
>>> btn.pack((10,))
(10, 2)
>>> btn.pack()
(15, 1)
>>> btn.pack((), True)
(15, 1)
set_label(label: _TagMarkup) → None

Change the button label.

Parameters:

label – markup for button label

>>> b = Button("Ok")
>>> b.set_label("Yup yup")
>>> b
<Button selectable fixed/flow widget 'Yup yup'>

CheckBox

class urwid.CheckBox(label: _TagMarkup, state: bool = False, has_mixed: Literal[False] = False, on_state_change: Callable[[Self, bool, _T], Any] | None = None, user_data: _T = ..., *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)
class urwid.CheckBox(label: _TagMarkup, state: Literal['mixed'] | bool = False, has_mixed: Literal[True] = True, on_state_change: Callable[[Self, bool | Literal['mixed'], _T], Any] | None = None, user_data: _T = ..., *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)
class urwid.CheckBox(label: _TagMarkup, state: bool = False, has_mixed: Literal[False] = False, on_state_change: Callable[[Self, bool], Any] | None = None, user_data: None = None, *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)
class urwid.CheckBox(label: _TagMarkup, state: Literal['mixed'] | bool = False, has_mixed: Literal[True] = True, on_state_change: Callable[[Self, bool | Literal['mixed']], Any] | None = None, user_data: None = None, *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)

Selectable widget that toggles between checked, unchecked, and mixed states.

Create the CheckBox widget.

Parameters:
  • label – markup for check box label

  • state – False, True or “mixed”

  • has_mixed – True if “mixed” is a state to cycle through

  • on_state_change – shorthand for connect_signal() function call for a single callback

  • user_data – user_data for on_state_change

  • checked_symbol – the symbol drawn inside the brackets for the checked state, None for the default one (keyword only, see CheckBox.Symbols)

  • unchecked_symbol – the same for the unchecked state

  • mixed_symbol – the same for the “mixed” state

Raises:

ValueError – state is not one of the states of this check box.

..note:: pack method expect, that Columns backend widget is not modified from outside

Signals supported: 'change', "postchange"

  • "change" is sent before the state changes. The callback gets the new state. state still holds the old one.

  • "postchange" is sent after the state changes. The callback gets the old state. state holds the new one.

Register signal handler with:

urwid.connect_signal(check_box, "change", callback, user_data)

where callback is callback(check_box, new_state [,user_data]) Unregister signal handlers with:

urwid.disconnect_signal(check_box, "change", callback, user_data)
>>> CheckBox("Confirm")
<CheckBox selectable fixed/flow widget 'Confirm' state=False>
>>> CheckBox("Yogourt", "mixed", True)
<CheckBox selectable fixed/flow widget 'Yogourt' state='mixed'>
>>> cb = CheckBox("Extra onions", True)
>>> cb
<CheckBox selectable fixed/flow widget 'Extra onions' state=True>
>>> cb.render((20,), focus=True).text
[b'[X] Extra onions    ']
>>> CheckBox("Test", None)
Traceback (most recent call last):
...
ValueError: None not in (True, False, 'mixed')
class Symbols(*values)

Common symbols suitable for the state marks of CheckBox and RadioButton.

Only the ASCII members are drawn on terminals without UTF-8.

Note

The default terminal fonts of GNOME, KDE Plasma and Xfce on Ubuntu, Fedora and Arch, and of Windows Terminal, show every member. The default fonts of the Linux virtual console are:

  • Ubuntu: Fixed 8x16.

  • Fedora and openSUSE: eurlatgr.

  • Arch: the built-in 8x16 font of the kernel.

The default fonts of the Linux virtual console and of the classic Windows console show these non-ASCII members:

Member

Symbol

Ubuntu

Fedora

Arch

Windows

CHECK_MARK

✓

no

yes

no

no

BALLOT_X

✗

yes

yes

no

no

BULLET

yes

yes

yes

yes

MIDDLE_DOT

·

yes

yes

yes

yes

BLACK_CIRCLE

●

yes

yes

no

yes

WHITE_CIRCLE

○

no

no

yes

yes

FISHEYE

◉

no

no

no

no

CIRCLED_SLASH

⊘

yes

no

no

no

FULL_BLOCK

█

yes

yes

yes

yes

MEDIUM_SHADE

▒

yes

yes

yes

yes

MINUS_SIGN

U+2212

yes

yes

no

yes

EN_DASH

U+2013

yes

yes

no

yes

LIGHT_SHADE

░

yes

yes

yes

yes

HALF_CIRCLE

◐

no

no

no

no

BLACK_SQUARE

■

yes

yes

yes

yes

WHITE_SQUARE

□

no

no

no

yes

BLACK_SQUARE_SMALL

▪

no

no

no

yes

A member marked no needs a console font with wider coverage.

get_label() → str | bytes

Return label text.

>>> cb = CheckBox("Seriously")
>>> print(cb.get_label())
Seriously
>>> print(cb.label)
Seriously
>>> cb.set_label([("bright_attr", "flashy"), " normal"])
>>> print(cb.label)  #  only text is returned
flashy normal
get_state() → bool | Literal['mixed']

Return the state of the checkbox.

keypress(size: tuple[int], key: str) → str | None

Toggle state on ‘activate’ command.

>>> assert CheckBox._command_map[" "] == "activate"
>>> assert CheckBox._command_map["enter"] == "activate"
>>> size = (10,)
>>> cb = CheckBox("press me")
>>> cb.state
False
>>> cb.keypress(size, " ")
>>> cb.state
True
>>> cb.keypress(size, " ")
>>> cb.state
False
property label: str | bytes

Return label text.

>>> cb = CheckBox("Seriously")
>>> print(cb.get_label())
Seriously
>>> print(cb.label)
Seriously
>>> cb.set_label([("bright_attr", "flashy"), " normal"])
>>> print(cb.label)  #  only text is returned
flashy normal
mouse_event(size: tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool

Toggle state on button 1 press.

>>> size = (20,)
>>> cb = CheckBox("clickme")
>>> cb.state
False
>>> cb.mouse_event(size, "mouse press", 1, 2, 0, True)
True
>>> cb.state
True
pack(size: tuple[()] | tuple[int] | None = (), focus: bool = False) → tuple[int, int]

Pack for widget.

Parameters:
  • size – size data. Special case: () - get minimal widget size to fit

  • focus – widget is focused

Returns:

the (columns, rows) the widget needs.

>>> cb = CheckBox("test")
>>> cb.pack((10,))
(10, 1)
>>> cb.pack()
(8, 1)
>>> ml_cb = CheckBox("Multi\nline\ncheckbox")
>>> ml_cb.pack()
(12, 3)
>>> ml_cb.pack((), True)
(12, 3)
set_label(label: _TagMarkup) → None

Change the check box label.

Parameters:

label – markup for label. See Text widget for description of text markup.

>>> cb = CheckBox("foo")
>>> cb
<CheckBox selectable fixed/flow widget 'foo' state=False>
>>> cb.set_label(("bright_attr", "bar"))
>>> cb
<CheckBox selectable fixed/flow widget 'bar' state=False>
set_state(state: bool | Literal['mixed'], do_callback: bool = True) → None

Set the CheckBox state.

Parameters:
  • state – True, False or “mixed”

  • do_callback – False to suppress signal from this change

Raises:

CheckBoxError – state is not one of the states of this check box.

>>> from urwid import disconnect_signal
>>> changes = []
>>> def callback_a(user_data, cb, state):
...     changes.append("A %r %r" % (state, user_data))
>>> def callback_b(cb, state):
...     changes.append("B %r" % state)
>>> cb = CheckBox("test", False, False)
>>> key1 = connect_signal(cb, "change", callback_a, user_args=("user_a",))
>>> key2 = connect_signal(cb, "change", callback_b)
>>> cb.set_state(True)  # both callbacks will be triggered
>>> cb.state
True
>>> disconnect_signal(cb, "change", callback_a, user_args=("user_a",))
>>> cb.state = False
>>> cb.state
False
>>> cb.set_state(True)
>>> cb.state
True
>>> cb.set_state(False, False)  # don't send signal
>>> changes
["A True 'user_a'", 'B True', 'B False', 'B True']
property state: bool | Literal['mixed']

Return the state of the checkbox.

toggle_state() → None

Cycle to the next valid state.

>>> cb = CheckBox("3-state", has_mixed=True)
>>> cb.state
False
>>> cb.toggle_state()
>>> cb.state
True
>>> cb.toggle_state()
>>> cb.state
'mixed'
>>> cb.toggle_state()
>>> cb.state
False

RadioButton

class urwid.RadioButton(group: MutableSequence[RadioButton], label: _TagMarkup, state: bool | Literal['first True'] = ..., on_state_change: Callable[[Self, bool, _T], Any] | None = None, user_data: _T = ..., *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)
class urwid.RadioButton(group: MutableSequence[RadioButton], label: _TagMarkup, state: bool | Literal['first True'] = ..., on_state_change: Callable[[Self, bool], Any] | None = None, user_data: None = None, *, checked_symbol: str | None = ..., unchecked_symbol: str | None = ..., mixed_symbol: str | None = ...)

Checkbox-like widget that exclusively selects itself within a shared group list.

Create the RadioButton widget.

Parameters:
  • group – list for radio buttons in same group

  • label – markup for radio button label

  • state – False, True, “mixed” or “first True”

  • on_state_change – shorthand for connect_signal() function call for a single ‘change’ callback

  • user_data – user_data for on_state_change

  • checked_symbol – symbol inside the parentheses for the True state (keyword only)

  • unchecked_symbol – the same for the False state

  • mixed_symbol – the same for the “mixed” state

..note:: pack method expect, that Columns backend widget is not modified from outside

This function will append the new radio button to group. “first True” will set to True if group is empty. A button created with state True clears the other buttons of the group, as set_state() does.

Signals supported: 'change', "postchange", sent as described for CheckBox.

Selecting a button with set_state() sends its own signals first. Then the previously selected button of the group is cleared and sends its signals. So in the handlers of the newly selected button the previously selected button is still True. In the "postchange" handler of the cleared button, selected_button is the new selection.

Register signal handler with:

urwid.connect_signal(radio_button, "change", callback, user_data)

where callback is callback(radio_button, new_state [,user_data]) Unregister signal handlers with:

urwid.disconnect_signal(radio_button, "change", callback, user_data)
>>> bgroup = []  # button group
>>> b1 = RadioButton(bgroup, "Agree")
>>> b2 = RadioButton(bgroup, "Disagree")
>>> len(bgroup)
2
>>> b1
<RadioButton selectable fixed/flow widget 'Agree' state=True>
>>> b2
<RadioButton selectable fixed/flow widget 'Disagree' state=False>
>>> b2.render((15,), focus=True).text  # ... = b in Python 3
[...'( ) Disagree   ']

Signal order when the selection moves from b1 to b2, printed as the button, the signal argument and then the states of b1 and b2:

>>> def report(button, state):
...     print(button.label, state, b1.state, b2.state)
>>> keys = [connect_signal(b, name, report) for b in bgroup for name in ("change", "postchange")]
>>> b2.set_state(True)
Disagree True True False
Disagree False True True
Agree False True True
Agree True False True
property selected_button: RadioButton | None

Get the button of the group that is selected.

Returns:

the first button of group whose state is True, or None when no button of the group is selected, for example when every button was created with state=False.

>>> bgroup = []  # button group
>>> b1 = RadioButton(bgroup, "Agree")
>>> b2 = RadioButton(bgroup, "Disagree")
>>> b2.selected_button is b1
True
>>> b2.set_state(True)
>>> b1.selected_button.label
'Disagree'
set_state(state: bool | Literal['mixed'], do_callback: bool = True) → None

Set the RadioButton state.

Parameters:
  • state – True, False or “mixed”

  • do_callback – False to suppress signal from this change

If state is True all other radio buttons in the same button group will be set to False.

>>> bgroup = []  # button group
>>> b1 = RadioButton(bgroup, "Agree")
>>> b2 = RadioButton(bgroup, "Disagree")
>>> b3 = RadioButton(bgroup, "Unsure")
>>> b1.state, b2.state, b3.state
(True, False, False)
>>> b2.set_state(True)
>>> b1.state, b2.state, b3.state
(False, True, False)
>>> def relabel_button(radio_button, new_state):
...     radio_button.set_label("Think Harder!")
>>> key = connect_signal(b3, "change", relabel_button)
>>> b3
<RadioButton selectable fixed/flow widget 'Unsure' state=False>
>>> b3.set_state(True)  # this will trigger the callback
>>> b3
<RadioButton selectable fixed/flow widget 'Think Harder!' state=True>
property state: bool | Literal['mixed']

Return the state of the checkbox.

toggle_state() → None

Set state to True.

>>> bgroup = []  # button group
>>> b1 = RadioButton(bgroup, "Agree")
>>> b2 = RadioButton(bgroup, "Disagree")
>>> b1.state, b2.state
(True, False)
>>> b2.toggle_state()
>>> b1.state, b2.state
(False, True)
>>> b2.toggle_state()
>>> b1.state, b2.state
(False, True)

Special cases text widgets

SelectableIcon

class urwid.SelectableIcon(text: _TagMarkup, cursor_position: int | None = 0, align: Literal['left', 'center', 'right'] | Align = Align.LEFT, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE, layout: TextLayout | None = None)

Selectable text widget that shows a fixed-position cursor when focused.

Create a text widget that is selectable.

A cursor is displayed at a fixed location in the text when in focus, unless cursor_position is None. This widget has no special handling of keyboard or mouse input.

Parameters:
  • text – markup for this widget; see Text for description of text markup

  • cursor_position – position the cursor will appear in the text when this widget is in focus; None shows no cursor

  • align – typically 'left', 'center' or 'right'

  • wrap – typically 'space', 'any', 'clip' or 'ellipsis'

  • layout – defaults to a shared StandardTextLayout instance

get_cursor_coords(size: tuple[int] | tuple[()]) → tuple[int, int] | None

Return the position of the cursor if visible.

This method is required for widgets that display a cursor.

keypress(size: tuple[int] | tuple[()], key: str) → str

No keys are handled by this widget.

This method is required for selectable widgets.

render(size: tuple[int] | tuple[()], focus: bool = False) → TextCanvas | CompositeCanvas

Render the text content of this widget with a cursor when in focus.

>>> si = SelectableIcon("[!]")
>>> si
<SelectableIcon selectable fixed/flow widget '[!]'>
>>> si.render((4,), focus=True).cursor
(0, 0)
>>> si = SelectableIcon("((*))", 2)
>>> si.render((8,), focus=True).cursor
(2, 0)
>>> si.render((2,), focus=True).cursor
(0, 1)
>>> si.render(()).cursor
>>> si.render(()).text
[b'((*))']
>>> si.render((), focus=True).cursor
(2, 0)
>>> SelectableIcon("item", None).render((8,), focus=True).cursor

ANSIText

class urwid.ANSIText(ansi_text: str, previous_attr: AttrSpec | ParsedLine | ANSIText | None = None, *, one_line: bool = False, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE, align: Literal['left', 'center', 'right'] | Align = Align.LEFT)

A read-only widget interpreting ANSI/VT100 colour, horizontal and (optionally) vertical movement escapes.

It acts almost exactly like a read-only Text widget for the resolved, escape-free content.

Parsing is delegated to urwid.ansi_parser.AnsiParser, and layout (wrapping, alignment, packing and rendering) is delegated to an internal Text instance that this widget wraps via WidgetWrap, so this widget’s pack/render/rows behaviour is identical to Text for the same resolved characters, without needing to forward those calls by hand.

By default (one_line=False), a newline genuinely starts a new row, CSI n A/B genuinely moves the cursor up/down between rows, and a bare \r genuinely overwrites the current row in place (letting progress-bar-style redraw sequences resolve correctly) – ansi_text is treated as a single, complete block, meant to render one already-complete captured screen/block.

Pass one_line=True to restrict this widget to a single physical line of resolved text; see AnsiParser’s one_line flag for the underlying parsing semantics. In this mode, line ends and vertical cursor movement are treated as meaningless outside a single line (stripped/split rather than resolved into rows): since the underlying parser always returns a tuple[ParsedLine, ...] in this mode (line ends, VT and FF outside an OSC string are structural split points, never silently discarded), constructing an ANSIText directly from text containing an embedded line end only surfaces the first resulting line – no data is lost (the full split is always computed), but only the first line is exposed by this widget instance. Use from_lines() to turn arbitrary multi-line (or arbitrarily chunked) raw text into a complete sequence of ANSIText instances, in either mode.

>>> ANSIText("hello")
<ANSIText fixed/flow widget 'hello'>
>>> ANSIText("a\nb").text
'a\nb'
>>> ANSIText("a\nb", one_line=True).text
'a'

Build the widget from a block of ANSI/VT100-escaped text.

Parameters:
  • ansi_text – a block of text, optionally spanning several lines and containing ANSI/VT100 escape sequences. If one_line is true, only its first physical line is surfaced by this widget instance.

  • previous_attr – SGR/colour state to carry in from previously parsed text (see last_attr), or the ANSIText or ParsedLine of that text, to carry its character sets in as well.

  • one_line – restrict this widget to a single physical line of resolved text rather than resolving the whole block into multi-row output.

  • wrap – forwarded to the internal Text.

  • align – forwarded to the internal Text.

property attrib: list[tuple[Hashable, int]]

Read-only property returning the run-length encoded display attributes of this widget’s text.

property bel: int

The number of BEL characters seen (aggregated over the whole block unless one_line is true).

classmethod from_lines(chunks: Iterable[str], previous_attr: AttrSpec | ParsedLine | ANSIText | None = None, *, one_line: bool = False, **kwargs: Any) → Iterator[ANSIText]

Consume raw ANSI text chunks and yield the resulting ANSIText widget(s).

chunks (e.g. successive os.read() results from a subprocess) are not required to be pre-split on line boundaries, and each chunk may contain zero, one, or several line ends. Internally accumulates a single AnsiParser across all chunks, so a line split across two chunk boundaries – or an escape sequence split across two chunk boundaries – is still resolved correctly, and calls finalize() only once the iterable is exhausted.

If one_line is true, one ANSIText is yielded per line the shared parser completes, in order, with SGR colour state and character sets carried forward continuously: each produced ANSIText continues from the previous one, whether that previous line came from the same chunk or an earlier one.

If one_line is false (the default), this naturally reduces to yielding exactly one ANSIText once every chunk has been consumed, representing the entire accumulated multi-chunk block resolved into real multi-row output – the “normal multiline” case, reached through the same code path as the one_line=True case rather than a separate one.

Parameters:
  • chunks – an iterable of raw text chunks, not required to be aligned on line or escape-sequence boundaries.

  • previous_attr – SGR/colour state to seed the shared parser with, as if it were carried over from previously parsed text, or the ANSIText or ParsedLine of that text, to carry its character sets in as well.

  • one_line – parse in single-line-splitting mode (see the class docstring) rather than resolving the whole accumulated input into one multi-row block.

  • kwargs – forwarded to _from_parsed() (i.e. wrap/align for the internal Text).

Yields:

one or more ANSIText instances, in order, depending on one_line.

get_text() → tuple[str | bytes, list[tuple[Hashable, int]]]

Return (text, display attributes), see urwid.Text.get_text().

property last_attr: AttrSpec | None

The SGR/colour state in effect at the very end of this widget’s text.

Feed this in as previous_attr to a subsequently constructed ANSIText to continue the same colour state; feed this widget itself instead to continue the character sets (such as the DEC Special Graphics line drawing) as well.

property leds: str | None

The last keyboard-LED state requested, if any.

set_ansi_text(ansi_text: str, previous_attr: AttrSpec | ParsedLine | ANSIText | None = None) → None

Re-parse ansi_text and invalidate this widget.

Parsing uses the mode (one_line) this widget was constructed with; if one_line is true, only the first resulting line is used, see the class docstring.

Parameters:
  • ansi_text – a block of text, optionally spanning several lines and containing ANSI/VT100 escape sequences.

  • previous_attr – SGR/colour state to carry in from previously parsed text (see last_attr), or the ANSIText or ParsedLine of that text, to carry its character sets in as well.

property text: str | bytes

Read-only property returning the resolved, escape-free text of this widget (may contain \n).

property title: str | None

The last OSC window-title string seen, if any.

Decoration Widget Classes

AttrMap

class urwid.AttrMap(w: WrappedWidget, attr_map: _AttrMapLike, focus_map: _AttrMapLike = None)

A decoration that maps one set of display attributes to another.

This object will pass all function calls and variable references to the wrapped widget.

Parameters:
  • w – widget to wrap (stored as self.original_widget)

  • attr_map – attribute to apply to w, or dict of old display attribute: new display attribute mappings; the other attributes of w are placed over the attribute of None, so their ‘inherit’ colors are taken from it (see LayeredAttr), and an attribute mapped to None shows the attribute of None

  • focus_map – attribute to apply when in focus or dict of old display attribute: new display attribute mappings; if None use attr

>>> from urwid import Divider, Edit, Text
>>> AttrMap(Divider("!"), "bright")
<AttrMap flow widget <Divider flow widget '!'> attr_map={None: 'bright'}>
>>> AttrMap(Edit(), "notfocus", "focus").attr_map
{None: 'notfocus'}
>>> AttrMap(Edit(), "notfocus", "focus").focus_map
{None: 'focus'}
>>> size = (5,)
>>> am = AttrMap(Text("hi"), "greeting", "fgreet")
>>> next(am.render(size, focus=False).content())  # ... = b in Python 3
[('greeting', None, ...'hi   ')]
>>> next(am.render(size, focus=True).content())
[('fgreet', None, ...'hi   ')]
>>> am2 = AttrMap(Text(("word", "hi")), {"word": "greeting", None: "bg"})
>>> am2
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={'word': 'greeting', None: 'bg'}>
>>> next(am2.render(size).content())
[(LayeredAttr('greeting', 'bg'), None, ...'hi'), ('bg', None, ...'   ')]
property attr_map: dict[Hashable, Hashable]

Return a copy of the attribute mapping dictionary.

property focus_map: dict[Hashable, Hashable] | None

Return a copy of the focus attribute mapping dictionary, or None if unset.

get_attr_map() → dict[Hashable, Hashable]

Return a copy of the attribute mapping dictionary.

get_focus_map() → dict[Hashable, Hashable] | None

Return a copy of the focus attribute mapping dictionary, or None if unset.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas

Render wrapped widget and apply attribute. Return canvas.

set_attr_map(attr_map: _AttrMapLike) → None

Set the attribute mapping dictionary {from_attr: to_attr, …}.

A single attribute is stored as {None: attribute}, the way the constructor does.

>>> from urwid import Text
>>> w = AttrMap(Text("hi"), None)
>>> w.set_attr_map({"a": "b"})
>>> w
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={'a': 'b'}>
>>> w.set_attr_map("c")
>>> w
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={None: 'c'}>
Raises:

AttrMapError – a key or value of the mapping is not hashable.

set_focus_map(focus_map: _AttrMapLike | None) → None

Set the focus attribute mapping dictionary {from_attr: to_attr, …}.

If None this widget will use the attr mapping instead (no change when in focus).

A single attribute is stored as {None: attribute}, the way the constructor does.

>>> from urwid import Text
>>> w = AttrMap(Text("hi"), {})
>>> w.set_focus_map({"a": "b"})
>>> w
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={} focus_map={'a': 'b'}>
>>> w.set_focus_map("c")
>>> w
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={} focus_map={None: 'c'}>
>>> w.set_focus_map(None)
>>> w
<AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={}>
Raises:

AttrMapError – a key or value of the mapping is not hashable.

Padding

class urwid.Padding(w: WrappedWidget, align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', WHSettings.RELATIVE, 'fixed left', 'fixed right'], int] = Align.LEFT, width: int | Literal['pack', 'clip', WHSettings.PACK, WHSettings.CLIP] | tuple[Literal['relative', WHSettings.RELATIVE, 'fixed left', 'fixed right'], int] | None = (WHSettings.RELATIVE, 100), min_width: int | None = None, left: int = 0, right: int = 0)

Decoration that adds blank columns to the left and/or right of a wrapped widget.

Build the widget, padding w on the left and/or right.

Parameters:
  • w – a box, flow or fixed widget to pad on the left and/or right this widget is stored as self.original_widget

  • align – one of: 'left', 'center', 'right' ('relative', percentage 0=left 100=right)

  • width –

    one of:

    given width

    integer number of columns for self.original_widget

    'pack'

    try to pack self.original_widget to its ideal size

    ('relative', percentage of total width)

    make width depend on the container’s width

    'clip'

    to enable clipping mode for a fixed widget

  • min_width – the minimum number of columns for self.original_widget or None

  • left – a fixed number of columns to pad on the left

  • right – a fixed number of columns to pad on the right

Clipping Mode: (width= 'clip') In clipping mode this padding widget will behave as a flow widget and self.original_widget will be treated as a fixed widget. self.original_widget will be clipped to fit the available number of columns. For example if align is 'left' then self.original_widget may be clipped on the right.

Pack Mode: (width= 'pack') In pack mode is supported FIXED operation if it is supported by the original widget.

>>> from urwid import Divider, Text, BigText, FontRegistry
>>> from urwid.util import set_temporary_encoding
>>> size = (7,)
>>> def pr(w):
...     with set_temporary_encoding("utf-8"):
...         for t in w.render(size).text:
...             print(f"|{t.decode('utf-8')}|")
>>> pr(Padding(Text("Head"), ("relative", 20), "pack"))
| Head  |
>>> pr(Padding(Divider("-"), left=2, right=1))
|  ---- |
>>> pr(Padding(Divider("*"), "center", 3))
|  ***  |
>>> p = Padding(Text("1234"), "left", 2, None, 1, 1)
>>> p
<Padding fixed/flow widget <Text fixed/flow widget '1234'> left=1 right=1 width=2>
>>> pr(p)  # align against left
| 12    |
| 34    |
>>> p.align = "right"
>>> pr(p)  # align against right
|    12 |
|    34 |
>>> pr(Padding(Text("hi\nthere"), "right", "pack"))  # pack text first
|  hi   |
|  there|
>>> pr(Padding(BigText("1,2,3", FontRegistry["Thin 3x3"]()), width="clip"))
| ┐  ┌─┐|
| │  ┌─┘|
| ┴ ,└─ |
property align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', WHSettings.RELATIVE], int]

Return the padding alignment setting.

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the (x,y) coordinates of cursor within self._original_widget.

get_pref_col(size: tuple[()] | tuple[int] | tuple[int, int]) → int | Literal['left', 'right', Align.LEFT, Align.RIGHT] | None

Return the preferred column from self._original_widget, or None.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass keypress to self._original_widget.

left

Number of columns of padding on the left.

min_width

Minimum width of the wrapped widget, or None for no minimum.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Send mouse event if position is within self._original_widget.

move_cursor_to_coords(size: tuple[()] | tuple[int] | tuple[int, int], x: int | Literal['left', 'right', Align.LEFT, Align.RIGHT], y: int) → bool

Set the cursor position with (x,y) coordinates of self._original_widget.

Returns:

True if move succeeded, False otherwise.

pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) → tuple[int, int]

Return the size the widget would render as, for FIXED sizing.

A GIVEN width over a FIXED widget that is not FLOW takes the given width and the wrapped widget’s rows.

Raises:

PaddingError – this Padding uses CLIP, which makes it FLOW-only. Its width type does not resolve to a fixed size. Its width is GIVEN and the wrapped widget supports neither FLOW nor FIXED and has no rows(). This error is an AttributeError as well.

Warns PaddingWarning:

the wrapped widget does not support the sizing a FIXED pack needs: FLOW or FIXED for a GIVEN width, FIXED otherwise.

padding_values(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool) → tuple[int, int]

Return the number of columns to pad on the left and right.

Override this method to define custom padding behaviour.

Raises:

PaddingError – this Padding uses CLIP and no size was given.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas

Render the Padding and return the resulting canvas.

Raises:

ValueError – the Padding is empty and no size was given.

Warns PaddingWarning:

the width is GIVEN and size is too narrow for it.

right

Number of columns of padding on the right.

rows(size: tuple[int], focus: bool = False) → int

Return the rows needed for self.original_widget.

sizing() → frozenset[Sizing]

Widget sizing.

Rules: * width == CLIP: only FLOW is supported, and wrapped widget should support FIXED * width == GIVEN: FIXED is supported when the wrapped widget supports FLOW or FIXED * All other cases: use sizing of target widget

Warns PaddingWarning:

the width is GIVEN and the wrapped widget supports none of FLOW, BOX and FIXED sizing.

property width: Literal[WHSettings.CLIP, WHSettings.PACK] | int | tuple[Literal[WHSettings.RELATIVE], int]

Return the padding width.

Filler

class urwid.Filler(body: WrappedWidget, valign: Literal['top', 'middle', 'bottom'] | VAlign | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int] = VAlign.MIDDLE, height: int | Literal['pack', WHSettings.PACK] | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int] | None = WHSettings.PACK, min_height: int | None = None, top: int = 0, bottom: int = 0, *, shrink_to_fit: bool = False)

Box widget decoration that vertically aligns a flow or box widget within the available space.

Parameters:
  • body – a flow widget or box widget to be filled around (stored as self.original_widget)

  • valign – one of: 'top', 'middle', 'bottom', ('relative', percentage 0=top 100=bottom)

  • height –

    one of:

    'pack'

    if body is a flow widget

    given height

    integer number of rows for self.original_widget

    ('relative', percentage of total height)

    make height depend on container’s height

  • min_height –

    one of:

    None

    if no minimum or if body is a flow widget

    minimum height

    integer number of rows for the widget when height not fixed

  • top – a fixed number of rows to fill at the top

  • bottom – a fixed number of rows to fill at the bottom

  • shrink_to_fit – with a 'pack' height, render a body taller than the rows available as a box at those rows, instead of clipping it. It applies only to a body that also supports 'box', such as a LineBox around a Table. It has no effect for other heights.

Raises:

FillerError – valign is not a vertical alignment value, or a fixed height is combined with a valign it cannot be used with.

If body is a flow widget, then height must be 'pack' and min_height will be ignored. Sizing of the filler will be BOX/FLOW in this case.

If height is integer, min_height will be ignored and sizing of filler will be BOX/FLOW.

Filler widgets will try to satisfy height argument first by reducing the valign amount when necessary. If height still cannot be satisfied it will also be reduced.

bottom

Number of blank rows below the body.

filler_values(size: tuple[int, int] | tuple[int], focus: bool) → tuple[int, int]

Return the number of rows to pad on the top and bottom.

Override this method to define custom padding behaviour.

With shrink_to_fit, a 'pack' height taller than the rows available is cut down to those rows.

get_cursor_coords(size: tuple[int, int] | tuple[int]) → tuple[int, int] | None

Return cursor coords from self.original_widget if any.

get_pref_col(size: tuple[int, int] | tuple[int]) → int | Literal['left', 'right', Align.LEFT, Align.RIGHT] | None

Return pref_col from self.original_widget if any.

keypress(size: tuple[int, int] | tuple[int], key: str) → str | None

Pass keypress to self.original_widget.

min_height

Minimum height of the body for a relative height, or None for no minimum.

mouse_event(size: tuple[int, int] | tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass to self.original_widget.

move_cursor_to_coords(size: tuple[int, int] | tuple[int], col: int | Literal['left', 'right', Align.LEFT, Align.RIGHT], row: int) → bool

Pass to self.original_widget.

render(size: tuple[int, int] | tuple[int], focus: bool = False) → CompositeCanvas

Render self.original_widget with space above and/or below.

rows(size: tuple[int], focus: bool = False) → int

Flow pack support if FLOW sizing supported.

Raises:

FillerError – this Filler wraps a BOX widget, so it has no flow row count.

selectable() → bool

Return selectable from body.

shrink_to_fit

Render a 'pack' body taller than the rows available as a box at those rows.

sizing() → frozenset[Sizing]

Widget sizing.

Sizing BOX is always supported. Sizing FLOW is supported if: FLOW widget (a height type is PACK) or BOX widget with height GIVEN

Rules: * height == PACK: the height is taken from the wrapped widget, which therefore should support FLOW

Warns FillerWarning:

the height is PACK and the wrapped widget does not support FLOW sizing.

top

Number of blank rows above the body.

Divider

class urwid.Divider(div_char: str | bytes = ' ', top: int = 0, bottom: int = 0)

Horizontal divider widget.

Parameters:
  • div_char – character to repeat across line

  • top – number of blank lines above

  • bottom – number of blank lines below

>>> Divider()
<Divider flow widget>
>>> Divider("-")
<Divider flow widget '-'>
>>> Divider("x", 1, 2)
<Divider flow widget 'x' bottom=2 top=1>
Symbols

alias of DividerSymbols

bottom

Number of blank lines below the line.

div_char

Character repeated across the line.

render(size: tuple[int], focus: bool = False) → CompositeCanvas

Render the divider as a canvas and return it.

>>> Divider().render((10,)).text  # ... = b in Python 3
[...'          ']
>>> Divider("-", top=1).render((10,)).text
[...'          ', ...'----------']
>>> Divider("x", bottom=2).render((5,)).text
[...'xxxxx', ...'     ', ...'     ']
rows(size: tuple[int], focus: bool = False) → int

Return the number of lines that will be rendered.

>>> Divider().rows((10,))
1
>>> Divider("x", 1, 2).rows((10,))
4
top

Number of blank lines above the line.

LineBox

class urwid.LineBox(original_widget: WrappedWidget, title: _TagMarkup = '', title_align: Literal['left', 'center', 'right'] | Align = Align.CENTER, title_attr: Hashable = None, tlcorner: str = '┌', tline: str = '─', lline: str = '│', trcorner: str = '┐', blcorner: str = '└', rline: str = '│', bline: str = '─', brcorner: str = '┘')

Box widget decoration that draws a line border, with an optional title, around another widget.

Draw a line around original_widget.

Use ‘title’ to set an initial title text with will be centered on top of the box. It may be any text markup accepted by Text. A title wider than the top line is cut with an ellipsis.

Use title_attr to apply a specific attribute to the title text.

Use title_align to align the title to the ‘left’, ‘right’, or ‘center’. The default is ‘center’.

You can also override the widgets used for the lines/corners:

tline: top line bline: bottom line lline: left line rline: right line tlcorner: top left corner trcorner: top right corner blcorner: bottom left corner brcorner: bottom right corner

If empty string is specified for one of the lines/corners, then no character will be output there. If no top/bottom/left/right lines - whole lines will be omitted. This allows for seamless use of adjoining LineBoxes.

Class attribute Symbols can be used as source for standard lines:

>>> print(LineBox(Text("Some text")).render(()))
┌─────────┐
│Some text│
└─────────┘
>>> print(
...     LineBox(
...         Text("Some text"),
...         tlcorner=LineBox.Symbols.LIGHT.TOP_LEFT_ROUNDED,
...         trcorner=LineBox.Symbols.LIGHT.TOP_RIGHT_ROUNDED,
...         blcorner=LineBox.Symbols.LIGHT.BOTTOM_LEFT_ROUNDED,
...         brcorner=LineBox.Symbols.LIGHT.BOTTOM_RIGHT_ROUNDED,
...     ).render(())
... )
╭─────────╮
│Some text│
╰─────────╯
>>> print(
...     LineBox(
...         Text("Some text"),
...         tline=LineBox.Symbols.HEAVY.HORIZONTAL,
...         bline=LineBox.Symbols.HEAVY.HORIZONTAL,
...         lline=LineBox.Symbols.HEAVY.VERTICAL,
...         rline=LineBox.Symbols.HEAVY.VERTICAL,
...         tlcorner=LineBox.Symbols.HEAVY.TOP_LEFT,
...         trcorner=LineBox.Symbols.HEAVY.TOP_RIGHT,
...         blcorner=LineBox.Symbols.HEAVY.BOTTOM_LEFT,
...         brcorner=LineBox.Symbols.HEAVY.BOTTOM_RIGHT,
...     ).render(())
... )
┏━━━━━━━━━┓
┃Some text┃
┗━━━━━━━━━┛

To make Table constructions, some lineboxes need to be drawn without sides and T or CROSS symbols used for corners of cells.

Raises:

ValueError – a title with text is given while tline is empty, or title_align is not a supported alignment.

property focus: AbstractWidget | None

LineBox is partially container.

While focus position is a bit hacky (formally it’s not container and only position 0 available), focus widget is always provided by original widget.

format_title(text: _TagMarkup) → _TagMarkup

Return text padded with spaces for display in the box’s top border, or an empty string if unset.

A string stays a string; other text markup is returned as the list [" ", text, " "]. Markup with no text, such as ("attr", ""), counts as unset.

property original_widget: WrappedWidget

Return the widget wrapped by the line box.

set_title(text: _TagMarkup) → None

Set the title shown in the top line of the box.

Raises:

ValueError – this LineBox has no top line to put a title on.

SolidFill

class urwid.SolidFill(fill_char: str = ' ')

A box widget that fills an area with a single character.

Parameters:

fill_char – character to fill area with

>>> SolidFill("8")
<SolidFill box widget '8'>
fill_char

Character the area is filled with.

render(size: tuple[int, int], focus: bool = False) → SolidCanvas

Render the Fill as a canvas and return it.

>>> SolidFill().render((4, 2)).text  # ... = b in Python 3
[...'    ', ...'    ']
>>> SolidFill("#").render((5, 3)).text
[...'#####', ...'#####', ...'#####']

Pop-up Widget Classes

PopUpLauncher

class urwid.PopUpLauncher(original_widget: LauncherWidget, *, close_on_menu: bool = False, close_on_click_outside: bool = False)

Decoration that lets a wrapped widget open and close a pop-up drawn over other widgets by a PopUpTarget.

Wrap original_widget, with no pop-up open yet.

Parameters:
  • original_widget – widget that opens the pop-up.

  • close_on_menu – close the pop-up when it leaves a key for the menu command (esc by default) unhandled while it is the topmost open pop-up.

  • close_on_click_outside – close the pop-up on a press of mouse button 1, 2 or 3 outside it while it is the topmost open pop-up. The press closes the pop-up only, and does not reach the widget under it.

close_pop_up() → None

Discard the currently open pop-up widget, if any, and invalidate this widget.

create_pop_up() → AbstractWidget

Return the widget to be used for the pop-up.

Subclass must override this method. It is called once each time the pop-up is opened.

PopUpTarget renders the pop-up with the box size declared by get_pop_up_parameters(), so the widget returned here must accept an (overlay_width, overlay_height) size. Wrap a flow or fixed widget in a Filler (or in another box container) before returning it.

Raises:

NotImplementedError – the subclass does not override this method.

get_pop_up_parameters() → PopUpParametersModel

Return a dict of pop-up parameters.

For example: {'left': 0, 'top': 1, 'overlay_width': 30, 'overlay_height': 4, 'anchor_height': 1}.

Subclass must override this method. It is called each time this widget is rendered.

The optional anchor_height key makes PopUpTarget move the pop-up above the launcher, or clip its height, when it does not fit below; see CompositeCanvas.set_pop_up.

Raises:

NotImplementedError – the subclass does not override this method.

property launcher_size: tuple[int, int] | None

The (columns, rows) of the last render that was not served from the canvas cache, or None before it.

It is set before get_pop_up_parameters() is called, so inside that method it is always the size being rendered.

open_pop_up() → None

Create the pop-up widget via create_pop_up() and invalidate this widget.

property pop_up_widget: AbstractWidget | None

The open pop-up widget, or None when the pop-up is closed.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas | Canvas

Render the wrapped widget, attaching the open pop-up widget to the canvas, if any.

PopUpTarget

class urwid.PopUpTarget(original_widget: WrappedWidget)

Box widget that overlays the pop-up(s) declared by a PopUpLauncher inside it.

Pop-ups nest: a pop-up widget may itself be a PopUpLauncher with its own pop-up open, in which case it is drawn overlaid on top of the pop-up that opened it, and so on for as many levels as are open at once.

Wrap original_widget, which is rendered with any open pop-up(s) overlaid on top.

Parameters:

original_widget – box widget to wrap; typically contains a PopUpLauncher.

enter_selectable(*, reverse: bool = False) → None

Do nothing: every focus under this widget is kept when a parent container enters it.

The widget that receives keys is known only after the pop-ups were laid out at the current size, which the entry hook does not have, see Moving the Focus with Tab.

get_cursor_coords(size: tuple[int, int]) → tuple[int, int] | None

Return the cursor coordinates of the current widget.

Raises:

TypeError – the current widget has no get_cursor_coords method.

get_pref_col(size: tuple[int, int]) → int | Literal['left', 'right', Align.LEFT, Align.RIGHT] | None

Return the preferred cursor column of the current widget, or None if it has none.

Raises:

TypeError – the current widget has no get_pref_col method.

keypress(size: tuple[int, int], key: str) → str | None

Rebuild the pop-up overlay chain for size and forward the keypress to it.

A key the topmost pop-up leaves unhandled goes to the on_unhandled_key callback its launcher set, if any; see CompositeCanvas.set_pop_up.

mouse_event(size: tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Rebuild the pop-up overlay chain for size and forward the mouse event to it.

A press of mouse button 1, 2 or 3 outside the topmost pop-up goes to the on_click_outside callback its launcher set, if any. The press is then handled and does not reach the widgets under the pop-up. See CompositeCanvas.set_pop_up.

move_cursor_to_coords(size: tuple[int, int], x: int, y: int) → bool

Move the cursor of the current widget to (x, y).

Raises:

TypeError – the current widget has no move_cursor_to_coords method.

pack(size: tuple[int, int] | tuple[()] = (), focus: bool = False) → tuple[int, int]

Rebuild the pop-up overlay chain for size and return the current widget’s packed size.

render(size: tuple[int, int], focus: bool = False) → Canvas

Rebuild the pop-up overlay chain for size and render the resulting widget.

Special Widget Classes

Notifications

class urwid.Notifications(*, timeout: float | None = 5.0, max_count: int | None = None, severity_attr: Mapping[Severity, _AttrMapLike] | None = None, default: _TagMarkup | None = None, default_attr: _AttrMapLike | None = None, loop: MainLoop | None = None, align: Literal['left', 'center', 'right'] | Align = Align.LEFT, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE)

A flow widget listing messages, each shown until its timeout runs out or it is dismissed.

The oldest message is at the top. With max_count set, a new message dismisses the oldest ones over that count, so max_count=1 makes a status line. While no message is showing, the default text is shown, or nothing, in zero rows.

A message with a timeout sets an alarm on loop, or, without one, on the MainLoop running when notify() is called (see get_running_loop()). The loop redraws the screen after the alarm, so an expired message disappears with no further call.

Sends "change" once per notify(), dismiss(), clear(), expiry, or lowered max_count that changed the messages shown, once the change is complete. Its handlers receive the widget. An exception raised by a handler during an expiry is logged by the loop, as for every alarm callback.

Important

Call every method from the thread running the loop, like every other widget change. Another thread hands the message over with MainLoop.call_from_thread() instead.

>>> status = Notifications(max_count=1, default="ready")
>>> status.render((8,)).text
[b'ready   ']
>>> saved = status.notify("saved", timeout=None)
>>> status.render((8,)).text
[b'saved   ']
>>> status.dismiss(saved)
True
>>> status.render((8,)).text
[b'ready   ']

Build an empty notification list.

Parameters:
  • timeout – seconds a message stays when notify() is not given one, see timeout.

  • max_count – messages shown at once, see max_count.

  • severity_attr – attribute map applied to a message for each Severity, see severity_attr.

  • default – text markup shown while no message is showing, or None to show nothing.

  • default_attr – attribute map applied to the default text, in any form AttrMap takes.

  • loop – loop the alarms are set on. None uses the loop running at each notify().

  • align – alignment of the text of every message and of the default text.

  • wrap – wrap mode of the text of every message and of the default text.

Raises:
  • TypeError – timeout is not a number or None, max_count is not an int or None, or a key of severity_attr is not a Severity.

  • ValueError – timeout is not finite and > 0, or max_count is < 1.

  • TextError – align or wrap is not supported by the text layout.

  • TagMarkupException – default is not valid text markup.

  • AttrMapError – a key or value of an attribute map is not hashable.

clear() → None

Take every message down and remove their alarms.

property default: _TagMarkup | None

Text markup shown while no message is showing, or None to show nothing.

Raises:

TagMarkupException – the new value is not valid text markup.

property default_attr: dict[Hashable, Hashable] | None

Attribute map applied to the default text, or None.

A copy is returned.

Raises:

AttrMapError – a key or value of the new map is not hashable.

dismiss(notification: Notification) → bool

Take a message down and remove its alarm.

Returns:

whether the message was showing. It is False for a message that expired or was dismissed before.

property loop: MainLoop | None

Loop the alarms are set on, or None to use the loop running at each notify().

property max_count: int | None

Messages shown at once, or None for no limit.

A new message over the count dismisses the oldest one. Lowering it dismisses the oldest messages over the new count at once.

Raises:
  • TypeError – the new value is not an int or None.

  • ValueError – the new value is < 1.

property notifications: tuple[Notification, ...]

The messages showing, the oldest first.

notify(markup: _TagMarkup, *, severity: Severity = Severity.INFO, timeout: float | Literal[_Unset.UNSET] | None = <unset>) → Notification

Show a message below the others.

A call that raises leaves the widget as it was, unless a "change" handler raised. An exception from a handler reaches the caller after the message is showing and its alarm is set.

Parameters:
  • markup – text markup of the message.

  • severity – how serious the message is, which selects its severity_attr entry.

  • timeout – seconds until the message is dismissed, None to keep it until dismiss(), or left out to use timeout.

Returns:

the message, to pass to dismiss().

Raises:
  • TypeError – severity is not a Severity, or timeout is not a number or None.

  • ValueError – timeout is not finite and > 0.

  • TagMarkupException – markup is not valid text markup.

  • RuntimeError – the message has a timeout, there is no loop, and no loop is running. Pass loop= to post a message before the loop runs.

property severity_attr: dict[Severity, dict[Hashable, Hashable]]

Attribute map applied to a message for each Severity.

It is set from a mapping of Severity to what AttrMap takes as its attribute map, or None. A severity with no entry is not coloured. A copy is returned.

Raises:
  • TypeError – a key of the new mapping is not a Severity.

  • AttrMapError – a key or value of an attribute map is not hashable.

sizing() → frozenset[Sizing]

Return flow sizing, whatever is showing.

property timeout: float | None

Seconds a message stays when notify() is not given a timeout, or None to keep it.

A new value applies to later messages only.

Raises:
  • TypeError – the new value is not a number or None.

  • ValueError – the new value is not finite and > 0.

Notification

class urwid.Notification(widget: Text, severity: Severity, timeout: float | None)

One message shown by Notifications, as Notifications.notify() returns it.

Pass it to Notifications.dismiss() to take the message down before its timeout.

Hold the message’s text widget, its severity and its timeout.

property severity: Severity

How serious the message is.

property timeout: float | None

Seconds the message is shown before it is dismissed, or None when it stays until dismissed.

property widget: Text

The text widget showing the message.

WidgetDisable

class urwid.WidgetDisable(original_widget: WrappedWidget, *, disabled: bool = True)

Decoration widget that disables interaction with the widget it wraps.

While disabled is True, this widget is never selectable, reports no cursor, and always passes focus=False to the wrapped widget, even if it somehow does become the focus. While it is False, focus, input and the cursor pass through to the wrapped widget unchanged. The look of a disabled widget is left to the application, for example an AttrMap around it.

Parameters:
  • original_widget – the widget being disabled

  • disabled – start disabled; False starts with the wrapped widget enabled

>>> from urwid import Button
>>> w = WidgetDisable(Button("Ok"))
>>> w.selectable()
False
>>> w.disabled = False
>>> w.selectable()
True

Wrap original_widget, disabled unless disabled is False.

property disabled: bool

Whether interaction with the wrapped widget is disabled; setting it redraws the widget.

enter_selectable(*, reverse: bool = False) → None

Forward the entry hook to original_widget unless disabled: a disabled widget is never entered.

property get_cursor_coords: Callable[[tuple[()] | tuple[int] | tuple[int, int]], tuple[int, int] | None]

Return the wrapped widget’s get_cursor_coords method while enabled, if it has one.

property get_pref_col: Callable[[tuple[()] | tuple[int] | tuple[int, int]], int | None]

Return the wrapped widget’s get_pref_col method while enabled, if it has one.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Return key unhandled while disabled, otherwise pass it to the wrapped widget.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Leave the event unhandled while disabled, otherwise pass it to the wrapped widget.

property move_cursor_to_coords: Callable[[tuple[()] | tuple[int] | tuple[int, int], int, int], bool]

Return the wrapped widget’s move_cursor_to_coords method while enabled, if it has one.

pack(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → tuple[int, int]

Return the size required by the wrapped widget, passing focus=False while disabled.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas

Render the wrapped widget into a canvas, passing focus=False while disabled.

rows(size: tuple[int], focus: bool = False) → int

Return the number of rows the wrapped widget occupies, passing focus=False while disabled.

selectable() → bool

Return False while disabled, otherwise whether the wrapped widget can take the input focus.

sizing() → frozenset[Sizing]

Return the set of sizing modes supported by the wrapped widget.

WidgetPlaceholder

class urwid.WidgetPlaceholder(original_widget: WrappedWidget)

Do-nothing decoration widget that can be used for swapping between widgets.

Swaps happen without modifying the container of this widget.

This can be useful for making an interface with a number of distinct pages or for showing and hiding menu or status bars.

The widget displayed is stored as the self.original_widget property and can be changed by assigning a new widget to it.

Wrap original_widget, warning when it does not implement the Widget API.

Deprecated since version 2.4.0: Wrapping an object that does not implement the Widget API (see AbstractWidget) is deprecated. Support will be removed in version 7.0.

Container Widget Classes

Frame

class urwid.Frame(body: BodyWidget, header: HeaderWidget | None = None, footer: FooterWidget | None = None, focus_part: Literal['header', 'footer', 'body'] | AbstractWidget = 'body', *, select_navigation: bool = False, flow: bool = False, min_body_rows: int = 0, trim_first: Literal['header', 'footer'] = 'header')

Frame widget is a box widget with optional header and footer flow widgets placed above and below it.

It also accepts a flow size, when its body does: the header, the body and the footer then take all their rows. It reports FLOW sizing only with flow set, so containers lay it out as a box widget by default.

Note

The main difference between a Frame and a Pile widget defined as: Pile([(‘pack’, header), body, (‘pack’, footer)]) is that the Frame will not automatically change focus up and down in response to keystrokes.

Sends "focus changed" after focus_part changed, also when removing the header or the footer in focus moves the focus to the body. Its handlers receive the frame, the old and the new focus part.

Initialize a new Frame instance.

Parameters:
  • body – a box widget for the body of the frame, and a flow widget as well for a flow size

  • header – a flow widget for above the body (or None)

  • footer – a flow widget for below the body (or None)

  • focus_part – ‘header’, ‘footer’ or ‘body’

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands, see Moving the Focus with Tab.

  • flow – report FLOW sizing as well when the body reports it, see flow.

  • min_body_rows – rows the body keeps on a short screen, see frame_top_bottom().

  • trim_first – 'header' or 'footer', the part trimmed first while the body is in focus, see frame_top_bottom().

Raises:
  • TypeError – min_body_rows is not an integer.

  • ValueError – focus_part is not one of the three frame parts. min_body_rows is negative. trim_first is not 'header' or 'footer'.

Deprecated since version 2.4.0: Passing a header, body or footer that does not implement the Widget API is deprecated. Support will be removed in version 7.0.

property body: BodyWidget

Return the body widget.

property contents: MutableMapping[Literal['header', 'footer', 'body'], tuple[BodyWidget | HeaderWidget | FooterWidget, None]]

Return a dict-like object similar to the following.

{
    "body": (body_widget, None),
    "header": (header_widget, None),  # if frame has a header
    "footer": (footer_widget, None),  # if frame has a footer
}

This object may be used to read or update the contents of the Frame.

The values are similar to the list-like .contents objects used in other containers with (Widget, options) tuples, but are constrained to keys for each of the three usual parts of a Frame. When other keys are used a KeyError will be raised.

Currently, all options are None, but using the options() method to create the options value is recommended for forwards compatibility.

flow

Whether this Frame reports FLOW sizing as well, when its body reports it; see sizing().

property focus: BodyWidget | HeaderWidget | FooterWidget

Return the child Widget in focus: the body, header or footer widget. Read-only.

property focus_part: Literal['header', 'footer', 'body']

Return which part of the frame is in focus.

Setting it to another part redraws the frame and sends "focus changed". Unlike the focus_position setter, it does not check that the frame has that part.

property focus_position: Literal['header', 'footer', 'body']

Return which part of the frame is in focus: ‘body’, ‘header’ or ‘footer’.

Returns:

one of ‘header’, ‘footer’ or ‘body’.

property footer: FooterWidget | None

Return the footer widget, or None if there is no footer.

frame_top_bottom(size: tuple[int, int], focus: bool) → tuple[tuple[int, int], tuple[int, int]]

Calculate the number of rows for the header and footer.

The parts take rows in this order, each one at most the rows it reports, and the body also takes the rest:

  • header in focus: the header, min_body_rows of the body, the footer;

  • footer in focus: the footer, min_body_rows of the body, the header;

  • body in focus: min_body_rows of the body but at least 1, the part trim_first does not name, the part it names.

With the default min_body_rows=0 and trim_first='header', the part in focus is shown first, and with the body or the footer in focus the header is trimmed before the footer.

>>> from urwid import Edit, ListBox, Pile, Text
>>> frame = Frame(ListBox([Text("entry")]), header=Edit("path: "), footer=Pile([Text("flag")] * 3))
>>> frame.frame_top_bottom((10, 4), True)
((0, 3), (1, 3))
>>> frame.min_body_rows = 2
>>> frame.trim_first = "footer"
>>> frame.frame_top_bottom((10, 4), True)
((1, 1), (1, 3))
Parameters:
  • size – See Widget.render() for details

  • focus – True if this widget is in focus

Returns:

(head rows, foot rows),(orig head, orig foot) orig head/foot are from rows() calls.

get_cursor_coords(size: tuple[int, int] | tuple[int]) → tuple[int, int] | None

Return the cursor coordinates of the focus widget.

property header: HeaderWidget | None

Return the header widget, or None if there is no header.

keypress(size: tuple[int, int] | tuple[int], key: str) → str | None

Pass keypress to widget in focus.

With select_navigation, unhandled 'next selectable' and 'prev selectable' commands may cause a focus change.

min_body_rows

Rows the body takes in a box size right after the part in focus; see frame_top_bottom().

mouse_event(size: tuple[int, int] | tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass mouse event to appropriate part of frame. Focus may be changed on button 1 press.

options() → None

There are currently no options for Frame contents.

Return None as a placeholder for future options.

render(size: tuple[int, int] | tuple[int], focus: bool = False) → CompositeCanvas

Render the Frame and return the resulting canvas.

At a flow size every part is rendered in full.

Raises:

RuntimeError – the header or footer renders a different number of rows than it reported.

rows(size: tuple[int], focus: bool = False) → int

Return the total rows of the header, the body and the footer at the width in size.

It is used for a flow size, which shows every part in full. The body has to accept a flow size.

>>> from urwid import ListBox, Text
>>> Frame(ListBox([Text("one"), Text("two")]), header=Text("head"), footer=Text("foot")).rows((10,))
4
sizing() → frozenset[Sizing]

Return BOX, and FLOW as well when flow is set and the body reports FLOW.

trim_first

The part that gives up its rows first, 'header' or 'footer', while the body is in focus.

ListBox

class urwid.ListBox(body: ListWalker[_K, AbstractFlowWidget], *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None, focus_when_empty: bool = True, edge: Literal['return', 'stop'] = 'return', wheel: Literal['focus', 'view'] = 'focus', flow: bool = False)
class urwid.ListBox(body: Iterable[AbstractFlowWidget], *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None, focus_when_empty: bool = True, edge: Literal['return', 'stop'] = 'return', wheel: Literal['focus', 'view'] = 'focus', flow: bool = False)

Vertically stacked list of widgets.

Create the ListBox widget.

Parameters:
  • body – a ListWalker subclass such as SimpleFocusListWalker that contains widgets to be displayed inside the list box

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands, see Moving the Focus with Tab.

  • unfocused_focus_attr – attribute map applied to the focus child while the list box renders without focus, in any form AttrMap takes; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied to the focus child while the list box renders without focus, instead of unfocused_focus_attr; see unfocused_focus_attr_func.

  • focus_when_empty – whether the ListBox accepts the input focus while its body is empty; False lets a container move the focus past an empty ListBox.

  • edge – what up at the first row and down at the last row do; see edge.

  • wheel – what a notch of the vertical mouse wheel moves; see wheel.

  • flow – report FLOW sizing as well, so that a container can lay the ListBox out with the rows of all its widgets; see sizing().

Deprecated since version 4.0.2: Passing a body that is not a ListWalker but has a get_focus method is deprecated. Support will be removed in version 7.0.

property body: ListWalker[_K, AbstractFlowWidget]

A ListWalker subclass such as SimpleFocusListWalker that contains widgets.

The widgets are to be displayed inside the list box.

Deprecated since version 4.0.2: Assigning a body that is not a ListWalker but has a get_focus method is deprecated. Support will be removed in version 7.0.

calculate_visible(size: tuple[int, int], focus: bool = False) → VisibleInfo | tuple[None, None, None]

Return the widgets that would be displayed in the ListBox given the current size and focus.

see Widget.render() for parameter details

Returns:

(middle, top, bottom) or (None, None, None)

middle

(row offset*(when +ve) or *inset*(when -ve), *focus widget, focus position, focus rows, cursor coords or None)

top

(# lines to trim off top, list of (widget, position, rows) tuples above focus in order from bottom to top)

bottom

(# lines to trim off bottom, list of (widget, position, rows) tuples below focus in order from top to bottom)

change_focus(size: tuple[int, int], position: _K, offset_inset: int = 0, coming_from: Literal['above', 'below'] | None = None, cursor_coords: tuple[int | str, int] | tuple[int | str] | None = None, snap_rows: int | None = None) → None

Change the current focus widget.

This is used internally by methods that know the widget’s size.

See also set_focus().

Parameters:
  • size – see Widget.render() for details

  • position – a position compatible with self._body.set_focus()

  • offset_inset – either the number of rows between the top of the listbox and the start of the focus widget (+ve value) or the number of lines of the focus widget hidden off the top edge of the listbox (-ve value) or 0 if the top edge of the focus widget is aligned with the top edge of the listbox (default if unspecified)

  • coming_from – either ‘above’, ‘below’ or unspecified None

  • cursor_coords – (x, y) tuple indicating the desired column and row for the cursor, a (x,) tuple indicating only the column for the cursor, or unspecified

  • snap_rows – the maximum number of extra rows to scroll when trying to “snap” a selectable focus into the view

Raises:
  • TypeError – the body does not implement set_focus.

  • ListBoxError – offset_inset leaves no row of the target visible, or cursor_coords names a row outside the target widget.

  • ValueError – the cursor row is unspecified and coming_from is neither 'above' nor 'below'.

property contents: ListBoxContentsProto[_K]

An object that allows reading widgets from the ListBox’s list walker.

Widgets are read as a (widget, options) tuple. None is currently the only value for options.

Warning

This object may not be used to set or iterate over contents.

You must use the list walker stored as body to perform manipulation and iteration, if supported.

property edge: Literal['return', 'stop']

What up at the first row and down at the last row do.

  • "return": the key is returned unhandled, so a parent container such as Pile moves the focus out of the list.

  • "stop": the key is consumed and the focus stays in the list.

An empty list box returns the key in both modes, since it has no row to keep the focus on.

ends_visible(size: tuple[int, int], focus: bool = False) → list[Literal['top', 'bottom']]

Return a list that may contain 'top' and/or 'bottom'.

i.e. this function will return one of: [], ['top'], ['bottom'] or ['top', 'bottom'].

convenience function for checking whether the top and bottom of the list are visible

flow

Whether this ListBox reports FLOW sizing as well, when the body allows it; see sizing().

property focus: AbstractFlowWidget | None

The child widget in focus or None when ListBox is empty.

Return the widget in focus according to our list walker.

focus_next_match(predicate: Callable[[AbstractFlowWidget], bool], *, reverse: bool = False, wrap: bool = True) → _K | None

Move the focus to the next widget for which predicate returns true.

The body is walked lazily from the position next to the focus in the search direction, so a match before the end of the body is found without reading the rest of it. A wrapped search reads the other side of the focus whole, and checks the widget in focus last, so a single match is found again.

>>> import urwid
>>> listbox = urwid.ListBox([urwid.Text(word) for word in ("apple", "pear", "plum", "peach")])
>>> listbox.focus_next_match(lambda widget: widget.text.startswith("p"))
1
>>> listbox.focus_next_match(lambda widget: widget.text.startswith("p"), reverse=True)
3
>>> listbox.focus_next_match(lambda widget: widget.text == "apple", wrap=False) is None
True
>>> listbox.focus_position
3
Parameters:
  • predicate – called with each candidate widget.

  • reverse – search towards the top of the body.

  • wrap – continue from the other end of the body once this end is reached. Only a body that ends, or declares wrap_around, has an end to stop at; one that cycles without declaring it is searched once round from the focus either way.

Returns:

the new focus position, or None when nothing matches and the focus stays where it was.

property focus_position: _K

Return the position of the child widget in focus.

The valid values for this position depend on the list walker in use.

Raises:

IndexError – the ListBox is empty.

get_cursor_coords(size: tuple[int] | tuple[int, int]) → tuple[int, int] | None

See Widget.get_cursor_coords() for details.

get_first_visible_pos(size: tuple[int, int], focus: bool = False) → int

Return the index of the first visible item.

get_focus_offset_inset(size: tuple[int, int]) → tuple[int, int]

Return (offset rows, inset rows) for focus widget.

Raises:

ListBoxError – the stored inset fraction is invalid or exceeds the rows of the focus widget.

get_scrollpos(size: tuple[int, int] | None = None, focus: bool = False) → int

Return the current scrolling position.

get_visible_amount(size: tuple[int, int], focus: bool = False) → int

Return the number of items currently visible.

keypress(size: tuple[int] | tuple[int, int], key: str) → str | None

Move selection through the list elements scrolling when necessary.

Keystrokes are first passed to widget in focus in case that widget can handle them. A list box with no rows shows nothing and returns every key unhandled.

Keystrokes handled by this widget are:

up

up one line (or widget)

down

down one line (or widget)

page up

move cursor up one listbox length (or widget)

page down

move cursor down one listbox length (or widget)

With edge "stop", up at the first row and down at the last row are consumed.

With select_navigation, unhandled 'next selectable' and 'prev selectable' commands move the focus to the next or previous selectable widget.

make_cursor_visible(size: tuple[int, int]) → None

Shift the focus widget so that its cursor is visible.

mouse_event(size: tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass the event to the contained widgets.

May change focus on button 1 press. A vertical wheel notch the widget under the pointer does not handle moves what wheel selects.

Deprecated since version 2.4.0: Support for a contained widget that does not implement mouse_event is deprecated. Support will be removed in version 7.0.

options() → None

There are currently no options for ListBox contents.

Return None as a placeholder for future options.

render(size: tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas | SolidCanvas

Render ListBox and return canvas.

see Widget.render() for details

A flow size renders every widget of the body, with the rows rows() reports.

Raises:

ListBoxError – the rendered contents do not fit the reported rows, a child widget renders a different number of rows or cursor coordinates than it calculated, or the list walker returns a next position pointing at itself.

require_relative_scroll(size: tuple[int, int], focus: bool = False) → bool

Widget require relative scroll due to performance limitations of real lines count calculation.

rows(size: tuple[int], focus: bool = False) → int

Return the rows of all widgets in the body at the width in size.

The focus widget is measured in focus and the others out of focus, as a render measures them, whatever focus is. A child whose row count depends on its focus breaks the box layout as well, which a render reports as a ListBoxError.

>>> from urwid import Text
>>> ListBox([Text("one two"), Text("three")]).rows((5,))
3
Raises:

ListBoxError – the body does not support the scrolling protocol, or it wraps around.

rows_max(size: tuple[int, int] | None = None, focus: bool = False) → int

Scrollable protocol for sized iterable and not wrapped around contents.

scroll_to_bottom() → None

Move the focus to the last position of the body and show it at the bottom, as end does.

The last position gets the focus whether its widget is selectable or not, unlike focus_last_selectable(). An empty body is left as it is.

Raises:

TypeError – the body does not implement positions.

scroll_to_top() → None

Move the focus to the first position of the body and show it at the top, as home does.

The first position gets the focus whether its widget is selectable or not, unlike focus_first_selectable(). An empty body is left as it is.

Raises:

TypeError – the body does not implement positions.

selectable() → bool

Return whether the ListBox accepts the input focus.

With focus_when_empty False it does not while its body is empty.

set_focus(position: _K, coming_from: Literal['above', 'below'] | None = None) → None

Set the focus position and try to keep the old focus in view.

Without coming_from, preserve the target widget’s cursor and nested focus. With coming_from, move its cursor from the indicated edge.

Parameters:
  • position – a position compatible with self._body.set_focus()

  • coming_from – set to ‘above’ or ‘below’ if you know that old position is above or below the new position.

Raises:
  • ListBoxError – coming_from is not 'above', 'below' or None.

  • TypeError – the body does not implement set_focus.

  • IndexError – the ListBox is empty.

set_focus_valign(valign: Literal['top', 'middle', 'bottom'] | VAlign | tuple[Literal['relative', WHSettings.RELATIVE], int]) → None

Set the focus widget’s display offset and inset.

Parameters:

valign – one of: ‘top’, ‘middle’, ‘bottom’ (‘relative’, percentage 0=top 100=bottom)

set_scrollpos(position: SupportsInt) → None

Scroll the view so that row position of the body is its first row.

The view keeps the size of the last render, and position is limited to the rows that keep it filled. The focus stays where it is while at least one of its rows is in the new view. Otherwise it moves to the selectable widget in the view nearest to the edge the old focus left by:

  • the top edge, when the view moved down past the old focus;

  • the bottom edge, when the view moved up past it.

When the view holds no selectable widget, the widget at that edge takes the focus, as with the cursor keys.

When the focus widget shows a cursor, a render in focus still moves the view to keep the cursor visible.

>>> from urwid import Text
>>> listbox = ListBox([Text(str(number)) for number in range(10)])
>>> canvas = listbox.render((5, 3))
>>> listbox.set_scrollpos(4)
>>> listbox.get_scrollpos(), listbox.focus_position
(4, 4)
>>> listbox.set_scrollpos(3)
>>> listbox.get_scrollpos(), listbox.focus_position
(3, 4)
Raises:

ListBoxError – the body does not support the scrolling protocol, or it wraps around.

shift_focus(size: tuple[int, int], offset_inset: int) → None

Move the location of the current focus relative to the top.

This is used internally by methods that know the widget’s size.

See also set_focus_valign().

Parameters:
  • size – see Widget.render() for details

  • offset_inset – either the number of rows between the top of the listbox and the start of the focus widget (+ve value) or the number of lines of the focus widget hidden off the top edge of the listbox (-ve value) or 0 if the top edge of the focus widget is aligned with the top edge of the listbox.

Raises:

ListBoxError – offset_inset falls outside the listbox rows or the rows of the focus widget.

sizing() → frozenset[Sizing]

Return BOX, and FLOW as well when flow is set and the body allows it.

The body allows it when it reports its length, or an estimate of it, and does not wrap around. The whole body is measured on each flow layout, so this is meant for lists short enough for a dialog.

update_pref_col_from_focus(size: tuple[int, int]) → None

Update self.pref_col from the focus widget.

property wheel: Literal['focus', 'view']

What a notch of the vertical mouse wheel moves.

It applies when the widget under the pointer does not handle the notch.

  • "focus": the focus moves one row up or down, as up and down do.

  • "view": the view scrolls one row, and the focus moves as set_scrollpos() moves it. When the cursor of the selectable widget the focus would move to keeps the view from moving one row, the widget at the edge takes the focus instead. When the cursor of the focus widget keeps the view from moving, the notch moves the focus as in "focus".

At the ends of the list the event is returned unhandled in both modes.

TreeListBox

class urwid.TreeListBox(body: ListWalker[_K, AbstractFlowWidget], *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None, focus_when_empty: bool = True, edge: Literal['return', 'stop'] = 'return', wheel: Literal['focus', 'view'] = 'focus', flow: bool = False)
class urwid.TreeListBox(body: Iterable[AbstractFlowWidget], *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None, focus_when_empty: bool = True, edge: Literal['return', 'stop'] = 'return', wheel: Literal['focus', 'view'] = 'focus', flow: bool = False)

A ListBox with special handling for navigation and collapsing of TreeWidgets.

Create the ListBox widget.

Parameters:
  • body – a ListWalker subclass such as SimpleFocusListWalker that contains widgets to be displayed inside the list box

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands, see Moving the Focus with Tab.

  • unfocused_focus_attr – attribute map applied to the focus child while the list box renders without focus, in any form AttrMap takes; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied to the focus child while the list box renders without focus, instead of unfocused_focus_attr; see unfocused_focus_attr_func.

  • focus_when_empty – whether the ListBox accepts the input focus while its body is empty; False lets a container move the focus past an empty ListBox.

  • edge – what up at the first row and down at the last row do; see edge.

  • wheel – what a notch of the vertical mouse wheel moves; see wheel.

  • flow – report FLOW sizing as well, so that a container can lay the ListBox out with the rows of all its widgets; see sizing().

Deprecated since version 4.0.2: Passing a body that is not a ListWalker but has a get_focus method is deprecated. Support will be removed in version 7.0.

collapse_focus_parent(size: tuple[int, int]) → None

Collapse parent directory.

focus_end(size: tuple[int, int]) → None

Move focus to far bottom.

focus_first_child() → None

Move focus to the first child of the widget in focus, if it is expanded.

focus_home(size: tuple[int, int]) → None

Move focus to very top.

focus_last_child() → None

Move focus to the last child of the widget in focus, if it is expanded.

focus_next_sibling() → None

Move focus to the next sibling of the node in focus, if there is one.

focus_prev_sibling() → None

Move focus to the previous sibling of the node in focus, if there is one.

keypress(size: tuple[int] | tuple[int, int], key: str) → str | None

Handle a keypress, passing any unhandled key to unhandled_input().

move_focus_to_parent(size: tuple[int, int]) → None

Move focus to parent of widget in focus.

unhandled_input(size: tuple[int, int], data: str) → str | None

Handle macro-navigation keys.

Columns

class urwid.Columns(widget_list: Iterable[AbstractBoxWidget | AbstractFlowWidget | tuple[Literal['pack', WHSettings.PACK], AbstractFlowWidget | AbstractFixedWidget] | tuple[int, AbstractBoxWidget | AbstractFlowWidget] | tuple[Literal['given', WHSettings.GIVEN], int, AbstractBoxWidget | AbstractFlowWidget] | tuple[Literal['weight', WHSettings.WEIGHT], int | float, AbstractBoxWidget | AbstractFlowWidget]], dividechars: int = 0, focus_column: int | AbstractWidget | None = None, min_width: int = 1, box_columns: Iterable[int] | None = None, *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None)

Widgets arranged horizontally in columns from left to right.

Sends "focus changed" after focus_position changed, also through a change of contents. Its handlers receive the container, the old and the new focus position, which is None while it is empty.

Initialize a new Columns instance.

Parameters:
  • widget_list – iterable of flow or box widgets

  • dividechars – number of blank characters between columns

  • focus_column – index into widget_list of column in focus or focused widget instance, if None the first selectable widget will be chosen, skipping columns of width 0, and of weight 0 when min_width is 0, unless no other column is selectable.

  • min_width – minimum width for each column which is not calling widget.pack() in widget_list.

  • box_columns – a list of column indexes containing box widgets whose height is set to the maximum of the rows required by columns not listed in box_columns.

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands, see Moving the Focus with Tab.

  • unfocused_focus_attr – attribute map applied to the focus child while the columns renders without focus, in any form AttrMap takes; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied to the focus child while the columns renders without focus, instead of unfocused_focus_attr; see unfocused_focus_attr_func.

Raises:
  • ColumnsError – an item of widget_list is not a widget or a valid (width, widget) pair.

  • ValueError – focus_column is a widget that is not in widget_list.

Warns ColumnsWarning:

an item of widget_list is not a widget. A 'pack' column is listed in box_columns.

widget_list may also contain tuples such as:

(given_width, widget)

make this column given_width screen columns wide, where given_width is an int

('given', given_width, widget)

the same as (given_width, widget), in the expanded form that repeats the contents options. 'fixed' is accepted as an older name for 'given'.

('pack', widget)

call pack() to calculate the width of this column

('weight', weight, widget)

give this column a relative weight (number) to calculate its width from th screen columns remaining

Widgets not in a tuple are the same as ('weight', 1, widget)

If the Columns widget is treated as a box widget then all children are treated as box widgets, and box_columns is ignored.

If the Columns widget is treated as a flow widget then the rows are calculated as the largest rows() returned from all columns except the ones listed in box_columns. The box widgets in box_columns will be displayed with this calculated number of rows, filling the full height.

column_widths(size: tuple[int] | tuple[int, int], focus: bool = False) → list[int]

Return a list of column widths.

A width of 0 hides the corresponding column completely, and keyboard navigation skips it. A subclass may override this method to lay the columns out itself; every sized layout uses its result.

property contents: MonitoredFocusList[tuple[AbstractFlowWidget | AbstractFixedWidget, tuple[Literal[WHSettings.PACK], None, bool]] | tuple[AbstractBoxWidget | AbstractFlowWidget, tuple[Literal[WHSettings.GIVEN], int, bool] | tuple[Literal[WHSettings.WEIGHT], int | float, bool]]]

Return the contents of this Columns as a list of (widget, options) tuples.

This list may be modified like a normal list and the Columns widget will update automatically.

See also

Create new options tuples with the options() method

property dividechars: int

Number of blank characters between two visible columns.

property focus: AbstractWidget | None

The child widget in focus or None when Columns is empty.

property focus_position: int

Return the index of the child widget in focus.

Raises IndexError if read when Columns is empty, or when set to an invalid index.

Raises:

IndexError – the Columns is empty.

get_column_sizes(size: tuple[int, int] | tuple[int] | tuple[()], focus: bool = False) → tuple[tuple[int, ...], tuple[int, ...], tuple[tuple[int, int] | tuple[int] | tuple[()], ...]]

Get column widths, heights and render size parameters.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

Warns ColumnsWarning:

a column holds an object that is not a widget. A FLOW render is requested and a BOX-only column is not listed in box_columns.

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the cursor coordinates from the focus widget.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

get_pref_col(size: tuple[()] | tuple[int] | tuple[int, int]) → Literal['left', 'right', Align.LEFT, Align.RIGHT] | int | None

Return the pref col from the column in focus.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass keypress to the focus column.

A hidden focus column, one with a width of 0, gets no keys: the key is left unhandled by it. Unhandled left and right keys may cause a focus change. The focus moves only to columns the last layout drew, since a column the user cannot see takes no input: a column left 0 wide or not laid out for lack of room is skipped until a resize shows it. With select_navigation, so do unhandled 'next selectable' and 'prev selectable' commands.

Parameters:
  • size – Widget size correct for the supported sizing

  • key – a single keystroke value

Returns:

None if the key was handled, otherwise the key left unhandled, as the focus column returned it.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

property min_width: int

Minimum width of a weighted column.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Send event to appropriate column. May change focus on button 1 press.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

Deprecated since version 2.4.0: Support for a column that does not implement mouse_event is deprecated. Support will be removed in version 7.0.

move_cursor_to_coords(size: tuple[()] | tuple[int] | tuple[int, int], col: int | Literal['left', 'right', Align.LEFT, Align.RIGHT], row: int) → bool

Choose a selectable column to focus based on the coords.

see Widget.move_cursor_coords() for details

A hidden column, one with a width of 0, is never chosen.

Raises:

ValueError – the column sizes cannot be computed for size, for example size is () and the children cannot be sized as a fixed widget.

static options(width_type: Literal['pack', WHSettings.PACK], width_amount: int | float | None = 1, box_widget: bool = False) → tuple[Literal[WHSettings.PACK], None, bool]
static options(width_type: Literal['given', WHSettings.GIVEN], width_amount: int = 1, box_widget: bool = False) → tuple[Literal[WHSettings.GIVEN], int, bool]
static options(width_type: Literal['weight', WHSettings.WEIGHT] = WHSettings.WEIGHT, width_amount: int | float = 1, box_widget: bool = False) → tuple[Literal[WHSettings.WEIGHT], int | float, bool]
static options(width_type: Literal['pack', 'given', 'weight', WHSettings.PACK, WHSettings.GIVEN, WHSettings.WEIGHT] = WHSettings.WEIGHT, width_amount: int | float | None = 1, box_widget: bool = False) → tuple[Literal[WHSettings.PACK], None, bool] | tuple[Literal[WHSettings.GIVEN], int, bool] | tuple[Literal[WHSettings.WEIGHT], int | float, bool]

Return a new options tuple for use in a Columns .contents list.

This sets an entry’s width type: one of the following:

'pack'

Call the widget’s Widget.pack() method to determine how wide this column should be. width_amount is ignored.

'given'

Make column exactly width_amount screen-columns wide.

'weight'

Allocate the remaining space to this column by using width_amount as a weight value.

Parameters:
  • width_type – 'pack', 'given' or 'weight'

  • width_amount – None for 'pack', a number of screen columns for 'given' or a weight value (number) for 'weight'

  • box_widget – set to True if this widget is to be treated as a box widget when the Columns widget itself is treated as a flow widget.

Raises:

ColumnsError – width_type and width_amount are not a valid combination.

pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) → tuple[int, int]

Get packed size for widget.

Raises:

ColumnsError – size is () and the children cannot be sized as a fixed widget.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → SolidCanvas | CompositeCanvas

Render columns and return canvas.

Parameters:
  • size – see Widget.render() for details

  • focus – True if this widget is in focus

Raises:

ColumnsError – the Columns has no visible column to render, or size is () and the children cannot be sized as a fixed widget.

rows(size: tuple[int], focus: bool = False) → int

Return the number of rows required by the columns.

This only makes sense if contents contains flow widgets.

see Widget.rows() for details

selectable() → bool

Return whether any child widget is selectable.

Asked on every call rather than cached, since a child such as WidgetPlaceholder may change its selectability after it was added.

sizing() → frozenset[Sizing]

Sizing supported by widget.

Returns:

Calculated widget sizing

Warns ColumnsWarning:

a column’s width type does not suit its widget’s sizing, or no sizing fits all columns and the BOX/FLOW fallback is used.

Due to the nature of container with mutable contents, this method cannot be cached.

Rules: * WEIGHT BOX -> BOX * GIVEN BOX -> Can be included in FIXED and FLOW depends on the other columns * PACK BOX -> Unsupported * BOX-only widget without box_columns disallow FLOW render

  • WEIGHT FLOW -> FLOW

  • GIVEN FLOW -> FIXED (known width and widget knows its height) + FLOW (historic)

  • PACK FLOW -> FLOW (widget fit in provided size)

  • WEIGHT FIXED -> Need also FLOW or/and BOX to properly render due to width calculation

  • GIVEN FIXED -> Unsupported

  • PACK FIXED -> FIXED (widget knows its size)

Backward compatibility rules: * GIVEN BOX -> Allow BOX

BOX can be only if ALL widgets support BOX. FIXED can be only if no BOX without “box_columns” flag and no strict FLOW.

>>> from urwid import BigText, Edit, SolidFill, Text, Thin3x3Font
>>> font = Thin3x3Font()

# BOX-only widget >>> Columns((SolidFill(“#”),)) <Columns box widget (1 item)>

# BOX-only widget with “get height from max” >>> Columns((SolidFill(“#”),), box_columns=(0,)) <Columns box widget (1 item)>

# FLOW-only >>> Columns((Edit(),)) <Columns selectable flow widget (1 item)>

# FLOW allowed by “box_columns” >>> Columns((Edit(), SolidFill(“#”)), box_columns=(1,)) <Columns selectable flow widget (2 items) focus_column=0>

# FLOW/FIXED >>> Columns((Text(“T”),)) <Columns fixed/flow widget (1 item)>

# GIVEN BOX only -> BOX only >>> Columns(((5, SolidFill(“#”)),), box_columns=(0,)) <Columns box widget (1 item)>

# No FLOW - BOX only >>> Columns(((5, SolidFill(“#”)), SolidFill(“*”)), box_columns=(0, 1)) <Columns box widget (2 items) focus_column=0>

# FIXED only -> FIXED (and FLOW via drop/expand) >>> Columns(((WHSettings.PACK, BigText(“1”, font)),)) <Columns fixed/flow widget (1 item)>

# Invalid sizing combination -> use fallback settings (and produce warning) >>> Columns(((WHSettings.PACK, SolidFill(“#”)),)) <Columns box/flow widget (1 item)>

# Special case: empty columns widget sizing is impossible to calculate >>> Columns(()) <Columns box/flow widget ()>

Pile

class urwid.Pile(widget_list: Iterable[AbstractWidget | tuple[Literal['pack', WHSettings.PACK], AbstractFlowWidget | AbstractFixedWidget] | tuple[int, AbstractBoxWidget] | tuple[Literal['given', WHSettings.GIVEN], int, AbstractBoxWidget] | tuple[Literal['weight', WHSettings.WEIGHT], int | float, AbstractBoxWidget | AbstractFlowWidget]], focus_item: AbstractWidget | int | None = None, *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None)

A pile of widgets stacked vertically from top to bottom.

Sends "focus changed" after focus_position changed, also through a change of contents. Its handlers receive the container, the old and the new focus position, which is None while it is empty.

Build the pile from a list of child widgets.

Parameters:
  • widget_list – child widgets

  • focus_item – child widget that gets the focus initially. Chooses the first selectable widget if unset, skipping rows of height or weight 0 unless no other row is selectable.

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands, see Moving the Focus with Tab.

  • unfocused_focus_attr – attribute map applied to the focus child while the pile renders without focus, in any form AttrMap takes; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied to the focus child while the pile renders without focus, instead of unfocused_focus_attr; see unfocused_focus_attr_func.

Raises:

PileError – an item of widget_list is not a widget or a valid (height, widget) pair.

Warns PileWarning:

an item of widget_list is not a widget.

widget_list may also contain tuples such as:

(given_height, widget)

always treat widget as a box widget and give it given_height rows, where given_height is an int

('given', given_height, widget)

the same as (given_height, widget), in the expanded form that repeats the contents options. 'fixed' is accepted as an older name for 'given'.

('pack', widget)

allow widget to calculate its own height by calling its rows() method, i.e. treat it as a flow widget.

('weight', weight, widget)

if the pile is treated as a box widget then treat widget as a box widget with a height based on its relative weight value, otherwise treat the same as ('pack', widget).

Widgets not in a tuple are the same as ('weight', 1, widget)`

Note

If the Pile is treated as a box widget there must be at least one 'weight' tuple in widget_list.

property contents: MonitoredFocusList[tuple[AbstractFlowWidget | AbstractFixedWidget, tuple[Literal[WHSettings.PACK], None]] | tuple[AbstractBoxWidget, tuple[Literal[WHSettings.GIVEN], int]] | tuple[AbstractBoxWidget | AbstractFlowWidget, tuple[Literal[WHSettings.WEIGHT], int | float]]]

The contents of this Pile as a list of (widget, options) tuples.

options currently may be one of

('pack', None)

allow widget to calculate its own height by calling its rows method, i.e. treat it as a flow widget.

('given', n)

Always treat widget as a box widget with a given height of n rows.

('weight', w)

If the Pile itself is treated as a box widget then the value w will be used as a relative weight for assigning rows to this box widget. If the Pile is being treated as a flow widget then this is the same as ('pack', None) and the w value is ignored.

If the Pile itself is treated as a box widget then at least one widget must have a ('weight', w) options value, or the Pile will not be able to grow to fill the required number of rows.

This list may be modified like a normal list and the Pile widget will updated automatically.

See also

Create new options tuples with the options() method

property focus: AbstractWidget | None

The child widget in focus or None when Pile is empty.

property focus_position: int

Index of child widget in focus.

Raises IndexError if read when Pile is empty, or when set to an invalid index.

Raises:

IndexError – the Pile is empty.

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the cursor coordinates of the focus widget.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

get_item_rows(size: tuple[int] | tuple[int, int], focus: bool) → list[int]

Return the number of rows used by each widget in self.contents.

The result is the heights part of get_rows_sizes().

get_item_size(size: tuple[()] | tuple[int] | tuple[int, int], i: int, focus: bool, item_rows: list[int] | None = None) → tuple[()] | tuple[int] | tuple[int, int]

Return a size appropriate for passing to self.contents[i][0].render.

Raises:

PileError – the item uses a height rule that needs size information the caller did not provide, or a height rule that is not supported.

Deprecated since version 4.1.4: Not used by the urwid code base. Use get_rows_sizes() instead. This API will be removed in version 7.0.

get_pref_col(size: tuple[()] | tuple[int] | tuple[int, int]) → int | Literal['left', 'right', Align.LEFT, Align.RIGHT] | None

Return the preferred column for the cursor, or None.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

get_rows_sizes(size: tuple[int, int] | tuple[int] | tuple[()], focus: bool = False) → tuple[tuple[int, ...], tuple[int, ...], tuple[tuple[int, int] | tuple[int] | tuple[()], ...]]

Get rows widths, heights and render size parameters.

Records the rows this layout hides, which the focus moves skip.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

Warns PileWarning:

an item is not a widget, or its widget does not support the sizing its height type needs.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass the keypress to the widget in focus.

A hidden focus row, one with a height of 0, gets no keys. Unhandled up and down keys may cause a focus change. With select_navigation, so do unhandled 'next selectable' and 'prev selectable' commands.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass the event to the contained widget.

May change focus on button 1 press.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

Deprecated since version 2.4.0: Support for a child that does not implement mouse_event is deprecated. Support will be removed in version 7.0.

move_cursor_to_coords(size: tuple[()] | tuple[int] | tuple[int, int], col: int | Literal['left', 'right', Align.LEFT, Align.RIGHT], row: int) → bool

Capture pref col and set new focus.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

static options(height_type: Literal['pack', WHSettings.PACK], height_amount: int | float | None = 1) → tuple[Literal[WHSettings.PACK], None]
static options(height_type: Literal['given', WHSettings.GIVEN], height_amount: int = 1) → tuple[Literal[WHSettings.GIVEN], int]
static options(height_type: Literal['weight', WHSettings.WEIGHT] = WHSettings.WEIGHT, height_amount: int | float = 1) → tuple[Literal[WHSettings.WEIGHT], int | float]
static options(height_type: Literal['pack', 'given', 'weight'] | WHSettings = WHSettings.WEIGHT, height_amount: int | float | None = 1) → tuple[Literal[WHSettings.PACK], None] | tuple[Literal[WHSettings.GIVEN], int] | tuple[Literal[WHSettings.WEIGHT], int | float]

Return a new options tuple for use in a Pile’s contents list.

Parameters:
  • height_type – 'pack', 'given' or 'weight'

  • height_amount – None for 'pack', a number of rows for 'given' or a weight value (number) for 'weight'

Raises:

PileError – height_type and height_amount are not a valid combination.

pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) → tuple[int, int]

Get packed size for widget.

Raises:

PileError – size is () and the children cannot be sized as a fixed widget.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → SolidCanvas | CompositeCanvas

Render the Pile and return the resulting canvas.

Raises:
  • ValueError – the Pile is empty and no size was given.

  • PileError – size is () and the children cannot be sized as a fixed widget.

rows(size: tuple[int], focus: bool = False) → int

Return the total number of rows occupied by all of the pile’s widgets.

selectable() → bool

Return whether any child widget is selectable.

Asked on every call rather than cached, since a child such as WidgetPlaceholder may change its selectability after it was added.

sizing() → frozenset[Sizing]

Sizing supported by widget.

Returns:

Calculated widget sizing

Warns PileWarning:

an item’s height type does not suit its widget’s sizing.

Due to the nature of container with mutable contents, this method cannot be cached.

Rules: * WEIGHT BOX -> BOX * GIVEN BOX -> FLOW (height is known) & BOX (can be shrinked/padded) * PACK BOX -> Unsupported

  • WEIGHT FLOW -> FLOW

  • GIVEN FLOW -> Unsupported

  • PACK FLOW -> FLOW

  • WEIGHT FIXED -> Need also FLOW or/and BOX to properly render due to width calculation

  • GIVEN FIXED -> Unsupported

  • PACK FIXED -> FIXED (widget knows its size)

>>> from urwid import BigText, ProgressBar, SolidFill, Text, Thin3x3Font
>>> font = Thin3x3Font()

# BOX-only widget >>> Pile((SolidFill(“#”),)) <Pile box widget (1 item)>

# GIVEN BOX -> BOX/FLOW >>> Pile(((10, SolidFill(“#”)),)) <Pile box/flow widget (1 item)>

# FLOW-only >>> Pile((ProgressBar(None, None),)) <Pile flow widget (1 item)>

# FIXED -> FIXED >>> Pile(((WHSettings.PACK, BigText(“0”, font)),)) <Pile fixed widget (1 item)>

# FLOW/FIXED -> FLOW/FIXED >>> Pile(((WHSettings.PACK, Text(“text”)),)) <Pile fixed/flow widget (1 item)>

# FLOW + FIXED widgets -> FLOW/FIXED >>> Pile((ProgressBar(None, None), (WHSettings.PACK, BigText(“0”, font)))) <Pile fixed/flow widget (2 items) focus_item=0>

# GIVEN BOX + FIXED widgets -> BOX/FLOW/FIXED (GIVEN BOX allows overriding its height & allows any width) >>> Pile(((10, SolidFill(“#”)), (WHSettings.PACK, BigText(“0”, font)))) <Pile widget (2 items) focus_item=0>

# Invalid sizing combination -> use fallback settings (and produce warning) >>> Pile(((WHSettings.WEIGHT, 1, BigText(“0”, font)),)) <Pile box/flow widget (1 item)>

# Special case: empty pile widget sizing is impossible to calculate >>> Pile(()) <Pile box/flow widget ()>

GridFlow

class urwid.GridFlow(cells: Iterable[AbstractFlowWidget], cell_width: int, h_sep: int, v_sep: int, align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', WHSettings.RELATIVE], int], focus: int | AbstractFlowWidget | None = None, *, select_navigation: bool = False, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None)

Flow widget that renders all the widgets it contains the same width.

Arranges them from left to right and top to bottom.

Sends "focus changed" after focus_position changed, also through a change of contents. Its handlers receive the container, the old and the new focus position, which is None while it is empty.

Create the GridFlow widget.

Parameters:
  • cells – iterable of flow widgets to display

  • cell_width – column width for each cell

  • h_sep – blank columns between each cell horizontally

  • v_sep – blank rows between cells vertically (if more than one row is required to display all the cells)

  • align – horizontal alignment of cells, one of: ‘left’, ‘center’, ‘right’, (‘relative’, percentage 0=left 100=right)

  • focus – widget index or widget instance to focus on

  • select_navigation – handle the 'next selectable' and 'prev selectable' commands over the cells in their contents order, see Moving the Focus with Tab.

  • unfocused_focus_attr – attribute map applied to the focus child while the grid renders without focus, in any form AttrMap takes; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied to the focus child while the grid renders without focus, instead of unfocused_focus_attr; see unfocused_focus_attr_func.

align

Alignment of each row of cells.

property base_widget: GridFlow

Return self: GridFlow is a container, not a decoration.

property cell_width: int

The width of each cell in the GridFlow.

Setting this value affects all cells.

property contents: MonitoredFocusList[tuple[AbstractFlowWidget, tuple[Literal[WHSettings.GIVEN], int]]]

The contents of this GridFlow as a list of (widget, options) tuples.

options is currently a tuple in the form (‘given’, number). number is the number of screen columns to allocate to this cell. ‘given’ is the only type accepted at this time.

This list may be modified like a normal list and the GridFlow widget will update automatically.

See also

Create new options tuples with the options() method.

enter_selectable(*, reverse: bool = False) → None

Start at the first selectable cell, or at the last one when reverse is set.

It is the entry hook a parent container calls, and acts only when select_navigation is set, see Moving the Focus with Tab.

Parameters:

reverse – start at the last selectable cell instead of the first one.

property focus: AbstractFlowWidget | None

The child widget in focus or None when GridFlow is empty.

property focus_position: int

Index of child widget in focus.

Raises IndexError if read when GridFlow is empty, or when set to an invalid index.

Raises:

IndexError – the GridFlow is empty.

generate_display_widget(size: tuple[int] | tuple[()]) → Divider | Pile

Actually generate display widget (ignoring cache).

get_cursor_coords(size: tuple[int] | tuple[()]) → tuple[int, int] | None

Get cursor from display widget, or None if the widget in focus shows no cursor.

get_display_widget(size: tuple[int] | tuple[()]) → Divider | Pile

Arrange the cells into columns (and possibly a pile) for display, input or to calculate rows.

Also updates the display widget.

get_pref_col(size: tuple[int] | tuple[()]) → int | None

Return pref col from display widget, or None if the widget in focus has no preferred column.

h_sep

Number of blank columns between two cells.

keypress(size: tuple[int] | tuple[()], key: str) → str | None

Pass keypress to display widget for handling.

Captures focus changes. With select_navigation, unhandled 'next selectable' and 'prev selectable' commands move the focus to the next or previous selectable cell.

mouse_event(size: tuple[int] | tuple[()], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Handle a mouse event, updating the focus based on the resulting display widget.

Returns:

True when a cell handled the event or the focus moved to another cell, otherwise the result of the display widget, so an event such as the mouse wheel stays unhandled for a parent like ListBox.

move_cursor_to_coords(size: tuple[int] | tuple[()], col: int, row: int) → bool

Set the widget in focus based on the col + row.

options(width_type: Literal['given', WHSettings.GIVEN] = WHSettings.GIVEN, width_amount: int | None = None) → tuple[Literal[WHSettings.GIVEN], int]

Return a new options tuple for use in a GridFlow’s .contents list.

Parameters:
  • width_type – ‘given’ is the only value accepted

  • width_amount – None to use the default cell_width for this GridFlow

Raises:

GridFlowError – width_type is not GIVEN.

pack(size: tuple[int] | tuple[()] = (), focus: bool = False) → tuple[int, int]

Return the number of screen columns and rows this widget requires.

render(size: tuple[int] | tuple[()], focus: bool = False) → Canvas

Render this widget’s current display widget at the given size.

rows(size: tuple[int], focus: bool = False) → int

Return the number of rows this widget requires for the given size.

selectable() → bool

Return whether the internal Pile (or Divider) is selectable.

sizing() → frozenset[Sizing]

Widget sizing.

..note:: Empty widget sizing is limited to the FLOW due to no data for width.

v_sep

Number of blank rows between two rows of cells.

Table

class urwid.Table(columns: Sequence[TableColumn], rows: Iterable[Sequence[_Cell] | TableRow] = (), *, header: bool = True, header_attr: _AttrMapLike | None = None, footer: Sequence[_Cell] | None = None, footer_attr: _AttrMapLike | None = None, header_drop: int | None = None, footer_drop: int | None = None, dividechars: int = 1, attr: _AttrMapLike | None = None, focus_attr: _AttrMapLike | None = None, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: Callable[[TableRow], _AttrMapLike | None] | None = None, max_rows: int | None = None, row_attr: Callable[[int, Any], _AttrMapLike | None] | None = None, select_navigation: bool = False, wheel: Literal['focus', 'view'] = 'focus', scrollbar: bool | None = False, **scrollbar_options: Unpack[_ScrollBarOptions])

Rows of cells in columns of shared widths, between a fixed header and footer, and with a focused row.

The rows scroll in a ListBox. The header stays on top, the footer stays at the bottom, and neither takes the focus. It is a box and flow widget, and a fixed widget as well when no column is weighted.

A box size that does not fit the whole table drops the header or the footer by header_drop and footer_drop. A part with a threshold is dropped while keeping it leaves the body fewer rows than the threshold. The part with the larger threshold is dropped first; on equal thresholds the footer goes first. A part that is kept but does not fit is cut at the bottom. The header keeps its rows first, so the footer is cut first.

Sends "focus changed" after focus_position changed, also through a change of body, with the old and the new position, which is None while the table is empty. A row replaced in place at the focus sends nothing. Sends "activate" with the focus position when the focused row leaves the activate command (enter and space by default) unhandled. A handler whose result is true consumes the key; otherwise the key is returned, as Overlay does for "menu". Every handler runs, whatever the earlier ones returned. A mouse click moves the focus and reaches the cell only.

Create the table.

Parameters:
  • columns – one specification per column; their number is the number of cells of every row

  • rows – the initial rows, as TableRow objects of layout or sequences of cells

  • header – draw the column labels above the rows

  • header_attr – attribute map of the header

  • footer – the cells of a row drawn below the rows, see footer; None draws no footer

  • footer_attr – attribute map of a footer built from cells

  • header_drop – in a box size whose rows do not all fit, drop the header while keeping it leaves the body fewer rows than this; None never drops it

  • footer_drop – the same threshold for the footer

  • dividechars – blank screen columns between two visible columns

  • attr – attribute map of a row without its own TableRow.attr

  • focus_attr – attribute map of the focused row without its own TableRow.focus_attr

  • unfocused_focus_attr – attribute map applied to the focused row while the table renders without focus, over the row’s own map, as ListBox.unfocused_focus_attr does

  • unfocused_focus_attr_func – function that gives that map for the focused row, instead of unfocused_focus_attr; see unfocused_focus_attr_func

  • max_rows – the most rows of body a flow size shows; None shows every row

  • row_attr – row_attr(index, data) called each time a row renders, see row_attr

  • select_navigation – let the 'next selectable' and 'prev selectable' commands (tab and shift tab) move across the cells and then to the next or previous row

  • wheel – what a notch of the mouse wheel moves; see wheel

  • scrollbar – True draws a scrollbar beside the rows, None only when the rows do not fit, False never; while it is on, its column counts in the natural width

  • scrollbar_options – the ScrollBar parameters with a scrollbar_ prefix, such as scrollbar_thumb_char, scrollbar_end_markers, scrollbar_side and scrollbar_attr; ignored while scrollbar is False

Raises:
  • TableError – a column specification, dividechars, max_rows, a drop threshold, a row or the footer is invalid.

  • TypeError – a keyword argument is neither a parameter of the table nor one of scrollbar_options, also while scrollbar is False.

  • ValueError – ScrollBar rejects the value of a scrollbar_ parameter.

property body: _TableRows

The rows of the table, below the header.

A sequence of cells added to it becomes a TableRow of layout, so body[i] is that row. A change is validated before it is applied, and a TableError leaves the rows unchanged. Replace every row with table.body[:] = rows.

property focus: TableRow | None

The row in focus, or None when the table is empty.

property focus_position: int

The index of the row in focus.

Raises:

IndexError – the table is empty, or the position set is not an index of a row.

property footer: TableRow | None

The row below the rows, such as totals or a summary, or None.

It shares the column widths, and its cells count in the PACK widths. Set it to a sequence of cells, which becomes a row mapped by footer_attr alone, to a TableRow of layout, which keeps its own maps and may be a span row, or to None. A text cell of the footer re-measures its column when its text changes, as in any row.

Raises:

TableError – a row is of another layout or is in body, or the cells do not fit the columns.

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the cursor coordinates of the focused row, below the header.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass the key to the rows; send "activate" for an activate command they leave unhandled.

Returns:

None if the rows or an "activate" handler handled the key, otherwise the key. A handler handles the key by returning a true value.

property layout: TableLayout

The layout of the columns, which builds and measures the rows.

property max_rows: int | None

The most rows of body a flow size shows; None shows every row.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass the event to the rows; the header and the footer take no mouse events.

pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) → tuple[int, int]

Return the natural width and the rows at it for (), as Widget.pack() does otherwise.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → Canvas

Render the header, the rows and the footer, top to bottom.

property row_attr: Callable[[int, Any], _AttrMapLike | None] | None

row_attr(index, data), called each time a row renders, or None.

A result other than None is the row’s attribute map, in any form AttrMap takes, over TableRow.attr. A focused row with a focus map uses that instead. A change of the rows calls it again for the rows from the changed position on; a data object changed in place is seen only when the row is assigned again (body[i] = body[i]).

rows(size: tuple[int], focus: bool = False) → int

Return the header, body and footer rows, at most max_rows of body.

Without max_rows every row is measured.

selectable() → bool

Return whether the table has a row to focus.

set_focus(position: int, coming_from: Literal['above', 'below'] | None = None) → None

Focus the row at position, as ListBox.set_focus() does.

A row on screen from the old focus is brought just into view. Without coming_from:

  • a row on screen keeps its cursor and nested focus;

  • a row off screen is centred and keeps its nested focus.

With coming_from:

  • a row on screen takes the cursor from that edge, at the preferred column;

  • a row off screen is placed at that edge and keeps its nested focus.

Parameters:
  • position – the index of the row

  • coming_from – "above" or "below" when the old focus is on that side of the new one

Raises:
  • IndexError – position is not an index of a row.

  • ListBoxError – coming_from is not "above", "below" or None.

sizing() → frozenset[Sizing]

Return BOX and FLOW, and FIXED as well when no column is weighted.

property unfocused_focus_attr: dict[Hashable, Hashable] | None

Attribute map applied to the focused row while the table renders without focus, see ListBox.

property unfocused_focus_attr_func: Callable[[TableRow], _AttrMapLike | None] | None

Function that gives unfocused_focus_attr for the focused row.

It is called with the TableRow, and its map is applied over the row’s own map. See ListBox.unfocused_focus_attr_func.

property wheel: Literal['focus', 'view']

What a notch of the mouse wheel over the rows moves, see ListBox.wheel.

TableRow

class urwid.TableRow(layout: TableLayout, cells: Sequence[_Cell], *, data: Any = None, attr: _AttrMapLike | None = None, focus_attr: _AttrMapLike | None = None, selectable: bool = True, span: bool = False)

One row of cells laid out by a TableLayout.

A row is selectable unless built with selectable=False, whatever its cells, so a row of plain text can take the focus in a Table.

The attribute map applied to the row’s canvas is, in this order:

  • focus_attr, or the table’s focus_attr, when the row renders in focus;

  • the result of the table’s row_attr callable, when it returns something other than None;

  • attr, or the table’s attr.

The attr of a TableColumn maps each cell of that column first, except in a span row, in the header of a Table and in a footer the table built from cells. A row map given as a dict then also maps the cells’ and the columns’ attributes. The attribute a row map gives for None becomes the base layer under them.

Build the row and register it with layout.

Parameters:
  • layout – the layout whose widths the row uses

  • cells – one widget or text markup per column; a short row is padded with empty text cells. Text markup becomes a text cell with the column’s align and wrap. The sequence is copied.

  • data – any object the caller keeps with the row, such as the record it shows

  • attr – attribute map of the row, in any form AttrMap takes

  • focus_attr – attribute map of the row while it renders in focus

  • selectable – whether the row takes the focus

  • span – one cell drawn across every column, which the column widths do not measure

Raises:

TableError – cells is a string rather than a sequence of cells, there are more cells than columns, a span row does not have exactly one cell, or a cell widget does not support FLOW sizing.

property attr: dict[Hashable, Hashable] | None

Attribute map of the row, or None; set in any form AttrMap takes.

property cells: tuple[Widget, ...]

The cell widgets, including the text cells made from text markup.

property focus_attr: dict[Hashable, Hashable] | None

Attribute map of the row while it renders in focus, or None.

property index: int | None

The position of the row in its table’s rows, or None outside a table.

property layout: TableLayout

The layout whose widths the row uses.

render(size: tuple[int], focus: bool = False) → Canvas

Render the cells and apply the row’s attribute map.

selectable() → bool

Return whether the row takes the focus, as set when it was built or by set_selectable().

set_selectable(selectable: bool) → None

Set whether the row takes the focus.

sizing() → frozenset[Sizing]

Return FLOW: a row is laid out at the width it is given.

property span: bool

Whether the row is one cell drawn across every column.

TableColumn

class urwid.TableColumn(label: _TagMarkup = '', width: Literal[WHSettings.PACK] | int | tuple[Literal[WHSettings.WEIGHT], int | float] = WHSettings.PACK, min_width: int = 1, align: Literal[Align.LEFT, Align.CENTER, Align.RIGHT] = Align.LEFT, wrap: Literal[WrapMode.SPACE, WrapMode.CLIP, WrapMode.ELLIPSIS] = WrapMode.CLIP, attr: _AttrMapLike | None = None)

Specification of one table column.

The width is one of:

  • WHSettings.PACK: as wide as the widest cell that can be measured, and the label;

  • a number of screen columns, where 0 hides the column;

  • (WHSettings.WEIGHT, weight): a share of the room left by the other columns.

align and wrap apply to the cells given as text; a cell given as a widget keeps its own. attr maps every cell of the column; TableRow gives the order in which it and the row maps apply. A TableLayout validates the specification when it takes it.

Create new instance of TableColumn(label, width, min_width, align, wrap, attr)

align: Literal[Align.LEFT, Align.CENTER, Align.RIGHT]

Alias for field number 3

attr: _AttrMapLike | None

Attribute map of the column’s cells; the header label, a footer built from cells and a span row do not use it.

label: _TagMarkup

Header text, drawn by Table with header=True.

min_width: int

The narrowest a PACK or WEIGHT column becomes before the table hides a column instead.

width: Literal[WHSettings.PACK] | int | tuple[Literal[WHSettings.WEIGHT], int | float]

Alias for field number 1

wrap: Literal[WrapMode.SPACE, WrapMode.CLIP, WrapMode.ELLIPSIS]

Alias for field number 4

TableLayout

class urwid.widget.TableLayout(columns: Sequence[TableColumn], *, dividechars: int = 1)

Column widths shared by every row built through the layout, with or without a Table.

Cells are measured once, when their row is built. A text cell the layout made measures itself again when its text changes. Another widget whose width changed needs invalidate_widths().

>>> from urwid import Pile
>>> layout = TableLayout([TableColumn(), TableColumn(align="right")])
>>> pile = Pile([layout.row(["name", "1"]), layout.row(["n", "22"])])
>>> [line.decode() for line in pile.render((10,)).text]
['name  1   ', 'n    22   ']

Validate the column specifications.

Parameters:
  • columns – one specification per column

  • dividechars – blank screen columns between two visible columns

Raises:

TableError – a specification or dividechars is invalid; the message names the column index.

property columns: tuple[TableColumn, ...]

The column specifications.

property dividechars: int

Blank screen columns between two visible columns.

invalidate_widths() → None

Measure every cell of every registered row again.

Call it after a cell widget given by the caller changed its packed width. Text cells the layout made from text markup do not need it.

natural_width() → int

Return the smallest width at which no column shrinks or is hidden.

It is the sum of the natural widths of the visible columns and their dividers, where a weighted column counts at its minimum.

row(cells: Sequence[_Cell], *, data: Any = None, attr: _AttrMapLike | None = None, focus_attr: _AttrMapLike | None = None, selectable: bool = True, span: bool = False) → TableRow

Build a row of this layout; see TableRow for the arguments.

Raises:

TableError – the row does not fit the columns, see TableRow.

widths(maxcol: int) → tuple[int, ...]

Return the width of each column at maxcol screen columns.

The widths and a divider between every two visible columns fit in maxcol. A width of 0 hides the column. When the natural widths do not fit, the weighted columns are at their minimum first, then the PACK columns shrink in proportion to how far they are above their minimum, and as the last resort the rightmost visible column is hidden. A given width never shrinks.

Raises:

TableError – maxcol is negative.

Tabs

class urwid.Tabs(tabs: Iterable[tuple[_TagMarkup | AbstractWidget, AbstractWidget]] = (), *, selected: int = 0, bar_position: Literal['top', 'bottom'] | VAlign = VAlign.TOP, bar_selectable: bool = False, attr_map: _AttrMapLike | None = None, selected_map: _AttrMapLike | None = None, focus_map: _AttrMapLike | None = None, unfocused_focus_attr: _AttrMapLike | None = None, unfocused_focus_attr_func: _UnfocusedFocusAttrFunc | None = None, dividechars: int = 1, overflow_markers: tuple[str, str] | None = (TabsSymbols.ARROW_ASCII_LEFT, TabsSymbols.ARROW_ASCII_RIGHT), underline: tuple[str, str] | None = None)

A row of tab labels over (or under) the body of the selected tab.

The selected tab is the focus of the container: focus_position is its index and focus its body. Only the selected body is rendered, and keys go to it. A button 1 press on a label selects its tab. The 'next tab' and 'previous tab' commands select the neighbour, wrapping around at the ends; no key is mapped to them by default, see CommandMap.

With bar_selectable=True the label row can take the keyboard focus: the command towards the bar ('cursor up' for a top bar), unhandled by the body, moves the focus to it. There 'cursor left' and 'cursor right' move a bar cursor without selecting, 'activate' selects the tab under it, and the command towards the body returns the focus there. focus_position stays the selected tab while the bar has the keyboard focus, so get_focus_widgets() names the body then. With a single tab there is nothing to switch to: the bar does not take the focus and the tab commands are returned.

Labels are display-only: each one is rendered as a fixed widget, trimmed to one row, and never gets input, so a label should have text. When the labels do not fit, the bar shows the selected label (the label under the bar cursor while the bar has the focus) centred, or its first column when it is wider than the bar. Each edge with labels hidden past it shows a marker of overflow_markers, and a click on it selects the nearest hidden tab on that side.

With underline set, a line row between the labels and the body draws its first glyph across the width and its second one along the selected label, which joins that label to its body. The line takes attr_map, and its part along the selected label selected_map. A click on the line acts as a click on the label row next to it, an overflow marker included.

Sends "focus changed" after focus_position changed, also through a change of contents. Its handlers receive the container, the old and the new index, which is None while it is empty. It reports the index, not the body: deleting the selected tab when another one follows it keeps the index, so the next tab becomes selected without a signal. An application that follows the selected body compares focus before and after its own contents changes.

>>> tabs = Tabs([("Log", Text("log body")), ("Edit", Text("edit body"))])
>>> tabs.render((12,)).text
[b'Log Edit    ', b'log body    ']
>>> tabs.focus_position = 1
>>> tabs.render((12,)).text
[b'Log Edit    ', b'edit body   ']

Build the tabs from (label, body) pairs.

Parameters:
  • tabs – one (label, body) pair per tab, in order. A label is a widget, or markup made into a widget by options().

  • selected – index of the tab selected first; ignored when tabs is empty.

  • bar_position – 'top' or 'bottom', where the label row is drawn.

  • bar_selectable – let the label row take the keyboard focus when there are two or more tabs.

  • attr_map – attribute map applied to every label, in any form AttrMap takes.

  • selected_map – attribute map applied to the label of the selected tab instead of attr_map.

  • focus_map – attribute map applied to the label under the bar cursor while the bar has the focus, instead of the other two.

  • unfocused_focus_attr – attribute map applied over the selected label while the tabs render without focus; see unfocused_focus_attr.

  • unfocused_focus_attr_func – function that gives the map applied over the selected label while the tabs render without focus, instead of unfocused_focus_attr; it is called with the selected label, see unfocused_focus_attr_func.

  • dividechars – blank screen columns between two labels.

  • overflow_markers – text drawn at the left and the right edge of the label row while labels are hidden past that edge, such as (Tabs.Symbols.ARROW_TRIANGLE_LEFT, Tabs.Symbols.ARROW_TRIANGLE_RIGHT); None draws none, and an empty string draws none on that side.

  • underline – the glyphs of a line row between the labels and the body: the one drawn across the width and the one drawn along the selected label, such as (Tabs.Symbols.LINE_LIGHT, Tabs.Symbols.LINE_HEAVY); None draws no line.

Raises:
  • TypeError – a body is not a widget.

  • TagMarkupException – a label is neither a widget nor valid text markup.

  • AttrMapError – a key or value of attr_map, selected_map, focus_map or unfocused_focus_attr is not hashable.

  • ValueError – a label is not a fixed widget, a body is neither a flow nor a box widget, bar_position is not 'top' or 'bottom', dividechars is not an int >= 0, overflow_markers is not None or a pair of one-line strings, or underline is not None or a pair of one-column strings.

  • IndexError – selected is not an index of tabs.

Symbols

alias of TabsSymbols

property attr_map: dict[Hashable, Hashable] | None

Attribute map applied to every label, or None.

A copy is returned.

Raises:

AttrMapError – a key or value of the new map is not hashable.

property bar_position: VAlign

Where the label row is drawn, VAlign.TOP or VAlign.BOTTOM.

Raises:

ValueError – the new value is not 'top' or 'bottom'.

property bar_selectable: bool

Whether the label row can take the keyboard focus.

Setting it to false returns the focus to the body.

property contents: MonitoredFocusList[tuple[AbstractWidget, AbstractWidget]]

The tabs as a list of (body, label) pairs, body first as in the other containers.

The list may be modified like a normal list and the widget is updated. options() makes a label widget from markup.

property dividechars: int

Blank screen columns between two labels.

Raises:

ValueError – the new value is not an int >= 0.

enter_selectable(*, reverse: bool = False) → None

Put the keyboard focus on the selected body, or on the bar when the body is not selectable.

It is the entry hook a parent container calls when it moves its focus here.

Parameters:

reverse – passed to the body’s own entry hook.

property focus: AbstractWidget | None

The body of the selected tab, or None when there are no tabs.

property focus_map: dict[Hashable, Hashable] | None

Attribute map applied to the label under the bar cursor while the bar has the focus.

None applies none. A copy is returned.

Raises:

AttrMapError – a key or value of the new map is not hashable.

property focus_position: int

Index of the selected tab.

Raises:

IndexError – there are no tabs.

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the bar cursor while the bar has the focus, else the cursor of the selected body if it is shown.

Raises:

TabsError – size is (), or a flow size and the selected body is not a flow widget.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass the key to the selected body, or to the bar while it has the focus.

A body with no rows shown gets no keys. The tab commands switch tabs when neither the bar nor the body used the key. “Towards the bar” is 'cursor up' for a top bar and 'cursor down' for a bottom bar.

no tabs -> return key
bar focused?
+- yes: left/right -> move the bar cursor; at an edge return key
|       activate -> select the tab under the bar cursor
|       towards the body, body selectable and shown -> focus the body
|       anything else -> tab commands below
+- no:  body selectable and shown -> body.keypress(); handled -> done
        towards the bar and the bar can take the focus -> focus the bar on the selected tab
        anything else -> tab commands below
tab commands, two or more tabs -> select the neighbour, wrapping around
otherwise -> return key
Raises:

TabsError – size is (), or a flow size and the selected body is not a flow widget.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Handle a mouse event on the bar or on the body.

A button 1 press on a label, or on the line next to it, selects its tab, and events on the body rows go to the body.

With a selectable bar the press also moves the keyboard focus to the bar. A press on the body while the bar has the focus returns the focus to the body.

Raises:

TabsError – size is (), or a flow size and the selected body is not a flow widget.

static options(label: _LabelT) → _LabelT
static options(label: _TagMarkup) → Text

Return the label widget for a contents entry.

A widget is returned as given, markup as a Text that clips at the bar edge.

Raises:

TagMarkupException – label is neither a widget nor valid text markup.

>>> Tabs.options("Log").render(()).text
[b'Log']
property overflow_markers: tuple[str, str] | None

Text drawn at the left and the right edge of the label row while labels are hidden past that edge.

None draws none, and an empty string draws none on that side.

Raises:

ValueError – the new value is not None or a pair of one-line strings.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → Canvas

Render the bar and the selected body.

Raises:

TabsError – size is (), or a flow size and the selected body is not a flow widget.

rows(size: tuple[int], focus: bool = False) → int

Return the bar rows plus the rows of the selected body.

It is 0 when there are no tabs.

Raises:

TabsError – the selected body is not a flow widget.

selectable() → bool

Return whether the tabs take keys.

With two or more tabs they do, so a parent passes them the tab commands. A single tab follows its body.

property selected_map: dict[Hashable, Hashable] | None

Attribute map applied to the selected label instead of attr_map, or None.

A copy is returned.

Raises:

AttrMapError – a key or value of the new map is not hashable.

sizing() → frozenset[Sizing]

Return box sizing, plus flow sizing when every body is a flow widget.

Asked on every call, since the contents may change.

property underline: tuple[str, str] | None

Glyphs of the line row between the labels and the body: across the width, and along the selected label.

None draws no line row.

Raises:

ValueError – the new value is not None or a pair of one-column strings.

class urwid.widget.tabs.TabsSymbols(*values)

Common symbols for Tabs: overflow markers in left and right pairs, and underline glyphs.

The ARROW_ and GUILLEMET_ members are overflow markers, the LINE_ members underline glyphs. Only the ASCII members are also drawn on terminals without UTF-8, where the others are drawn as ?.

Note

The default terminal fonts of GNOME, KDE Plasma and Xfce on Ubuntu, Fedora and Arch, and of Windows Terminal, show every member. The default fonts of the Linux virtual console are:

  • Ubuntu: Fixed 8x16.

  • Fedora and openSUSE: eurlatgr.

  • Arch: the built-in 8x16 font of the kernel.

The default fonts of the Linux virtual console and of the classic Windows console show these non-ASCII members:

Member

Symbol

Ubuntu

Fedora

Arch

Windows

ARROW_TRIANGLE_LEFT

◀

yes

no

yes

no

ARROW_TRIANGLE_RIGHT

▶

yes

no

yes

no

ARROW_SMALL_TRIANGLE_LEFT

◂

yes

no

no

no

ARROW_SMALL_TRIANGLE_RIGHT

▸

yes

no

no

no

ARROW_WHITE_TRIANGLE_LEFT

◁

no

no

no

no

ARROW_WHITE_TRIANGLE_RIGHT

▷

no

no

no

no

ARROW_LEFT

←

yes

yes

yes

yes

ARROW_RIGHT

→

yes

yes

yes

yes

GUILLEMET_LEFT

«

yes

yes

yes

yes

GUILLEMET_RIGHT

»

yes

yes

yes

yes

LINE_LIGHT

─

yes

yes

yes

yes

LINE_HEAVY

━

yes

yes

no

no

LINE_DOUBLE

═

yes

yes

yes

yes

A member marked no needs a console font with wider coverage.

BoxAdapter

class urwid.BoxAdapter(box_widget: WrappedWidget, height: int)

Adapter for using a box widget where a flow widget would usually go.

Create a flow widget that contains a box widget.

Parameters:
  • box_widget – box widget to wrap

  • height – number of rows for box widget

Raises:

BoxAdapterError – box_widget is not a BOX widget.

>>> from urwid import SolidFill
>>> BoxAdapter(SolidFill("x"), 5)  # 5-rows of x's
<BoxAdapter flow widget <SolidFill box widget 'x'> height=5>
get_cursor_coords(size: tuple[int]) → tuple[int, int] | None

Return the wrapped box widget’s cursor coordinates for size at the adapter’s height.

get_pref_col(size: tuple[int]) → int | None

Return the wrapped box widget’s preferred cursor column for size at the adapter’s height.

height

Number of rows given to the box widget.

keypress(size: tuple[int], key: str) → str | None

Forward the keypress to the wrapped box widget at size and the adapter’s height.

mouse_event(size: tuple[int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Forward the mouse event to the wrapped box widget at size and the adapter’s height.

move_cursor_to_coords(size: tuple[int], col: int, row: int) → bool

Move the cursor of the wrapped box widget to (col, row) for size at the adapter’s height.

render(size: tuple[int], focus: bool = False) → CompositeCanvas

Render the wrapped box widget at size and the adapter’s height.

rows(size: tuple[int], focus: bool = False) → int

Return the predetermined height (behave like a flow widget).

>>> from urwid import SolidFill
>>> BoxAdapter(SolidFill("x"), 5).rows((20,))
5
sizing() → frozenset[Sizing]

Return the sizing modes this widget supports, which is always just FLOW.

Overlay

class urwid.Overlay(top_w: TopWidget, bottom_w: BottomWidget, align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', 'fixed left', 'fixed right', WHSettings.RELATIVE], int], width: Literal['pack', WHSettings.PACK] | int | tuple[Literal['relative', WHSettings.RELATIVE], int] | None, valign: Literal['top', 'middle', 'bottom'] | VAlign | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int], height: Literal['pack', WHSettings.PACK] | int | tuple[Literal['relative', WHSettings.RELATIVE], int] | None, min_width: int | None = None, min_height: int | None = None, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0, *, shrink_to_fit: bool = False)

Overlay contains two widgets and renders one on top of the other.

Top widget can be Box, Flow or Fixed. Bottom widget should be Box.

Sends "menu" when the top widget leaves a key for the menu command (esc by default) unhandled. Its handlers receive the overlay. A handler returning True consumes the key. Otherwise keypress() returns the key, as it does without a handler.

Sends "click outside" on a press of mouse button 1, 2 or 3 outside the top widget. Its handlers receive the overlay. A handler returning True consumes the press. Otherwise mouse_event() leaves the press unhandled, as it does without a handler.

Build the widget overlaying top_w on bottom_w.

Parameters:
  • top_w – a flow, box or fixed widget to overlay “on top”.

  • bottom_w – a box widget to appear “below” previous widget.

  • align – alignment, one of 'left', 'center', 'right' or ('relative', percentage 0=left 100=right)

  • width –

    width type, one of:

    'pack'

    if top_w is a fixed widget

    given width

    integer number of columns wide

    ('relative', percentage of total width)

    make top_w width related to container width

  • valign – alignment mode, one of 'top', 'middle', 'bottom' or ('relative', percentage 0=top 100=bottom)

  • height –

    one of:

    'pack'

    if top_w is a flow or fixed widget

    given height

    integer number of rows high

    ('relative', percentage of total height)

    make top_w height related to container height

  • min_width – the minimum number of columns for top_w when width is not fixed.

  • min_height – minimum number of rows for top_w when height is not fixed.

  • left – a fixed number of columns to add on the left.

  • right – a fixed number of columns to add on the right.

  • top – a fixed number of rows to add on the top.

  • bottom – a fixed number of rows to add on the bottom.

  • shrink_to_fit – with a 'pack' height, render a top_w taller than the rows available as a box at those rows, instead of cutting it off at the bottom. It applies only to a top_w that also supports 'box', such as a LineBox around a Table. It has no effect for other heights or for a 'pack' width.

Overlay widgets behave similarly to Padding and Filler widgets when determining the size and position of top_w. bottom_w is always rendered the full size available “below” top_w.

Deprecated since version 2.4.0: Passing a top_w or bottom_w that does not implement the Widget API is deprecated. Support will be removed in version 7.0.

property align: Align | tuple[Literal[WHSettings.RELATIVE], int]

Return the simplified horizontal alignment of the top widget.

bottom

Number of rows of padding below the top widget.

property bottom_w: BottomWidget

Widget appearing “below” top_w.

calculate_padding_filler(size: tuple[int, int], focus: bool) → tuple[int, int, int, int]

Return (padding left, right, filler top, bottom).

With shrink_to_fit, a 'pack' height taller than the rows available is cut down to those rows.

Raises:

OverlayError – the top widget is FIXED but reports no height.

property contents: MutableSequence[tuple[TopWidget | BottomWidget, OverlayOptions]]

A list-like object holding the overlay’s widgets and their options.

It is similar to:

[(bottom_w, bottom_options)),
 (top_w, top_options)]

This object may be used to read or update top and bottom widgets and top widgets’s options, but no widgets may be added or removed.

top_options takes the form (align_type, align_amount, width_type, width_amount, min_width, left, right, valign_type, valign_amount, height_type, height_amount, min_height, top, bottom)

bottom_options is always (‘left’, None, ‘relative’, 100, None, 0, 0, ‘top’, None, ‘relative’, 100, None, 0, 0) which means that bottom widget always covers the full area of the Overlay. writing a different value for bottom_options raises an OverlayError.

property focus: TopWidget

Return the top widget, which always has the focus.

property focus_position: Literal[1]

Return the top widget position (currently always 1).

get_cursor_coords(size: tuple[()] | tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return cursor coords from top_w, if any.

property height: int | Literal[WHSettings.FLOW, WHSettings.PACK] | tuple[Literal[WHSettings.RELATIVE], int] | tuple[Literal[WHSettings.WEIGHT], int | float]

Return the simplified height of the top widget.

keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) → str | None

Pass keypress to top_w.

A menu command key the top widget returns is sent as "menu".

Returns:

None when the top widget or a "menu" handler handled the key, otherwise the key the top widget returned.

left

Number of columns of padding on the left of the top widget.

min_height

Minimum height of the top widget for a relative height, or None for no minimum.

min_width

Minimum width of the top widget for a relative width, or None for no minimum.

mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Pass event to top_w.

A press of mouse button 1, 2 or 3 outside top_w is sent as "click outside". Any other event outside top_w is ignored.

Returns:

whether top_w or a "click outside" handler handled the event.

static options(align_type: Literal['left', 'center', 'right', 'relative', WHSettings.RELATIVE] | Align, align_amount: int | None, width_type: Literal['clip', 'pack', 'relative', 'given'] | WHSettings, width_amount: int | None, valign_type: Literal['top', 'middle', 'bottom', 'relative', WHSettings.RELATIVE] | VAlign, valign_amount: int | None, height_type: Literal['flow', 'pack', 'relative', 'given'] | WHSettings, height_amount: int | None, min_width: int | None = None, min_height: int | None = None, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0) → OverlayOptions

Return a new options tuple for use in this Overlay’s .contents mapping.

This is the common container API to create options for replacing the top widget of this Overlay. It is provided for completeness but is not necessarily the easiest way to change the overlay parameters. See also set_overlay_parameters()

Raises:

ValueError – align or valign names an unknown alignment type.

pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) → tuple[int, int]

Return the size the widget would render as, for FIXED sizing.

Raises:

OverlayError – a FIXED render is requested but the overlay parameters do not resolve to a fixed size.

render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) → CompositeCanvas

Render top_w overlayed on bottom_w.

The layout comes from calculate_padding_filler() and top_w_size(), overrides included.

right

Number of columns of padding on the right of the top widget.

rows(size: tuple[int], focus: bool = False) → int

Widget rows amount for FLOW sizing.

Raises:

OverlayError – the overlay parameters do not resolve to a row count for the given size.

selectable() → bool

Return selectable from top_w.

set_overlay_parameters(align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', 'fixed left', 'fixed right', WHSettings.RELATIVE], int], width: Literal['pack', WHSettings.PACK] | int | tuple[Literal['relative', 'fixed left', 'fixed right', WHSettings.RELATIVE], int] | None, valign: Literal['top', 'middle', 'bottom'] | VAlign | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int], height: Literal['pack', WHSettings.PACK] | int | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int] | None, min_width: int | None = None, min_height: int | None = None, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0) → None

Adjust the overlay size and position parameters.

See __init__() for a description of the parameters.

Raises:

OverlayError – valign is not a vertical alignment value.

shrink_to_fit

Render a 'pack' top widget taller than the rows available as a box at those rows.

sizing() → frozenset[Sizing]

Actual widget sizing.

Returns:

Sizing information depends on the top widget sizing and sizing parameters.

Rules: * BOX sizing is always supported provided by the bottom widget * FLOW sizing is supported if top widget has: * * PACK height type and FLOW supported by the TOP widget * * BOX supported by TOP widget AND height amount AND height type GIVEN of min_height * FIXED sizing is supported if top widget has: * * PACK width type and FIXED supported by the TOP widget * * width amount and GIVEN width or min_width AND: * * * FLOW supported by the TOP widget AND PACK height type * * * BOX supported by the TOP widget AND height_amount and GIVEN height or min height

Warns OverlayWarning:

the top widget does not support the sizing its width or height type needs.

top

Number of rows of padding above the top widget.

property top_w: TopWidget

Widget overlaid “on top” of bottom_w.

top_w_size(size: tuple[int, int], left: int, right: int, top: int, bottom: int) → tuple[()] | tuple[int] | tuple[int, int]

Return the size to pass to top_w.

A 'pack' height gives a flow size. With shrink_to_fit it gives a box size of the rows between the fillers instead, when the top widget is taller than those rows in or out of focus.

property valign: VAlign | tuple[Literal[WHSettings.RELATIVE], int]

Return the simplified vertical alignment of the top widget.

property width: Literal[WHSettings.CLIP, WHSettings.PACK] | int | tuple[Literal[WHSettings.RELATIVE], int] | tuple[Literal[WHSettings.WEIGHT], int | float]

Return the simplified width of the top widget.

OverlayStack

class urwid.OverlayStack(base: Widget, *, close_on_menu: bool = False, close_on_click_outside: bool = False)

A box widget showing dialogs on top of a base widget.

Each dialog is an Overlay over the one below. open() puts a widget on top and close() takes it down again with a result. Only the top dialog receives keys and mouse presses. The widgets below keep their own focus, so closing a dialog returns the input to where it was. The stack adds no frame: wrap a dialog in a LineBox or an AttrMap before opening it.

The base is a box widget, usually the application’s Frame, and the stack is meant to be the MainLoop.widget.

Sends "closed" once per closed dialog, after the dialog is removed and after its on_close callback. Its handlers receive the stack, the widget that was closed and the result.

Important

Call every method from the thread running the loop, like every other widget change. Another thread hands the call over with MainLoop.call_from_thread().

>>> from urwid import SolidFill, Text
>>> stack = OverlayStack(SolidFill("."))
>>> stack.open(Text("hi"))
>>> stack.render((10, 3)).text
[b'..........', b'.hi      .', b'..........']
>>> stack.close()
True
>>> stack.close()
False

Build a stack with no dialog open.

Parameters:
property base: Widget

The box widget shown under every dialog.

Setting it replaces the widget under the lowest dialog and keeps the dialogs open.

close(result: Any = None, *, widget: Widget | None = None) → bool

Close the top dialog, or widget and every dialog above it.

Each dialog above widget is closed first, the top one first, with the result None. Per dialog, in order: it is removed, its on_close is called, and "closed" is sent. A callback may therefore open or close dialogs itself.

widget given and not open, or nothing open -> return False
loop:
    remove the top dialog, show the one below
    on_close(result if it is widget else None), then "closed"
    it was widget, or a callback closed widget -> return True
    otherwise (widget still open, maybe under dialogs a callback opened) -> loop
Parameters:
  • result – passed to on_close and to the "closed" handlers of the dialog closed last.

  • widget – the dialog to close, as passed to open(). None closes the top dialog.

Returns:

whether a dialog was closed. It is False when no dialog is open, or when widget is not open in this stack.

property close_on_click_outside: bool

Whether a dialog opened from now on closes on a press of mouse button 1, 2 or 3 outside it.

The press closes the dialog only, and does not reach the widget under it. A change does not affect the dialogs already open.

property close_on_menu: bool

Whether a dialog opened from now on closes on a key for the menu command (esc by default).

The key closes the dialog only when the dialog leaves it unhandled. A change does not affect the dialogs already open.

property depth: int

The number of open dialogs.

property levels: tuple[Widget, ...]

The widgets of the open dialogs, as passed to open(), the lowest first.

open(widget: Widget, *, align: Literal['left', 'center', 'right'] | Align | tuple[Literal['relative', 'fixed left', 'fixed right', WHSettings.RELATIVE], int] = Align.CENTER, width: _Width | None = None, valign: Literal['top', 'middle', 'bottom'] | VAlign | tuple[Literal['relative', 'fixed top', 'fixed bottom', WHSettings.RELATIVE], int] = VAlign.MIDDLE, height: _Width | None = None, min_width: int | None = None, min_height: int | None = None, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0, shrink_to_fit: bool = False, close_on_menu: bool | None = None, close_on_click_outside: bool | None = None, on_close: Callable[[Any], object] | None = None) → None

Open widget as a dialog on top of the stack.

The layout options are those of Overlay, and are passed to it unchanged. A width or height of None is chosen from widget.sizing():

  • width is "pack" for a widget that is fixed only, and ("relative", 80) otherwise.

  • height is "pack" for a flow widget, ("relative", 80) for a box widget that is not a flow widget, and "pack" for a widget that is fixed only.

Nothing is opened when an exception is raised.

Parameters:
  • widget – the dialog, a box, flow or fixed widget.

  • align – horizontal alignment, as for Overlay.

  • width – width, as for Overlay, or None to choose it from the sizing.

  • valign – vertical alignment, as for Overlay.

  • height – height, as for Overlay, or None to choose it from the sizing.

  • min_width – minimum number of columns for a relative width.

  • min_height – minimum number of rows for a relative height.

  • left – columns of padding on the left of the dialog.

  • right – columns of padding on the right of the dialog.

  • top – rows of padding above the dialog.

  • bottom – rows of padding below the dialog.

  • shrink_to_fit – with a "pack" height, render a dialog that also supports "box" and is taller than the rows available as a box at those rows, as for Overlay.

  • close_on_menu – close this dialog on a key for the menu command it leaves unhandled. None uses close_on_menu.

  • close_on_click_outside – close this dialog on a press of mouse button 1, 2 or 3 outside it. None uses close_on_click_outside.

  • on_close – called with the result when the dialog is closed, before "closed" is sent.

Raises:
  • ValueError – widget is already open in this stack. Also raised when align or valign is a string that names no alignment.

  • OverlayError – a layout option is not valid for Overlay.

sizing() → frozenset[Sizing]

Return box sizing, whatever is open.

Graphic Widget Classes

BarGraph

class urwid.BarGraph(attlist: Sequence[str | tuple[str, str]], hatt: list[str] | None = None, satt: Mapping[tuple[int, int], str] | None = None)

Box widget that renders a bar graph from a sequence of data values.

Deprecated since version 4.0.10: Overriding get_data() in a subclass, or assigning get_data on an instance, is supported for backwards compatibility only. Call set_data() instead, so that the rendered canvas can be cached. This API will be removed in version 7.0.

Create a bar graph with the passed display characteristics.

See set_segment_attributes() for a description of the parameters.

calculate_bar_widths(size: tuple[int, int], bardata: Sequence[Sequence[float | int]]) → list[int]

Return a list of bar widths, one for each bar in data.

If self.bar_width is None this implementation will stretch the bars across the available space specified by maxcol.

calculate_display(size: tuple[int, int]) → list[tuple[int, list[tuple[int | tuple[int, int] | tuple[int, int, int], int]]]]

Calculate display data.

hlines_display(disp: list[tuple[int, list[tuple[int | tuple[int, int] | tuple[int, int, int], int]]]], top: float, hlines: Sequence[float | int], maxrow: int) → list[tuple[int, list[tuple[int | tuple[int, int] | tuple[int, int, int], int]]]]

Add hlines to display structure represented as bar_type tuple values.

The tuple is (bg, 0-5) where bg is the segment that has the hline on it, and 0-5 is the hline graphic to use where 0 is a regular underscore and 1-5 are the UTF-8 horizontal scan line characters.

render(size: tuple[int, int], focus: bool = False) → CompositeCanvas

Render BarGraph.

Raises:

BarGraphError – a graph character does not render as a single row.

selectable() → Literal[False]

Return False.

set_bar_width(width: int | None) → None

Set a preferred bar width for calculate_bar_widths to use.

Parameters:

width – width of bar or None for automatic width adjustment

Raises:

ValueError – width is not positive.

set_data(bardata: Sequence[Sequence[float | int]], top: float, hlines: Sequence[float | int] | None = None) → None

Store bar data, bargraph top and horizontal line positions.

Parameters:
  • bardata – a list of bar values.

  • top – maximum value for segments within bardata

  • hlines – None or a bar value marking horizontal line positions

bar values are [ segment1, segment2, … ] lists where top is the maximal value corresponding to the top of the bar graph and segment1, segment2, … are the values for the top of each segment of this bar. Simple bar graphs will only have one segment in each bar value.

Eg: if top is 100 and there is a bar value of [ 80, 30 ] then the top of this bar will be at 80% of full height of the graph and it will have a second segment that starts at 30%.

set_segment_attributes(attlist: Sequence[str | tuple[str, str]], hatt: list[str] | None = None, satt: Mapping[tuple[int, int], str] | None = None) → None

Set the display attributes to use for the bar graph’s segments.

Parameters:
  • attlist – list containing display attribute or (display attribute, character) tuple for background, first segment, and optionally following segments. ie. len(attlist) == num segments+1 character defaults to ‘ ‘ if not specified.

  • hatt – list containing attributes for horizontal lines. First element is for lines on background, second is for lines on first segment, third is for lines on second segment etc.

  • satt – dictionary containing attributes for smoothed transitions of bars in UTF-8 display mode. The values are in the form:

Raises:

BarGraphError –

attlist has fewer than two entries, or satt is not a mapping of valid (fg, bg) index pairs with fg above bg.

(fg,bg) : attr

fg and bg are integers where 0 is the graph background, 1 is the first segment, 2 is the second, … fg > bg in all values. attr is an attribute with a foreground corresponding to fg and a background corresponding to bg.

If satt is not None and the bar graph is being displayed in a terminal using the UTF-8 encoding then the character cell that is shared between the segments specified will be smoothed with using the UTF-8 vertical eighth characters.

eg: set_segment_attributes( [‘no’, (‘unsure’,”?”), ‘yes’] ) will use the attribute ‘no’ for the background (the area from the top of the graph to the top of the bar), question marks with the attribute ‘unsure’ will be used for the topmost segment of the bar, and the attribute ‘yes’ will be used for the bottom segment of the bar.

smooth_display(disp: list[tuple[int, list[tuple[int, int]]]]) → list[tuple[int, list[tuple[int | tuple[int, int] | tuple[int, int, int], int]]]]

Smooth (col, row*8) display into (col, row) display using UTF vertical eighth characters.

The characters are represented as bar_type tuple values (fg, bg, 1-7) where fg is the lower segment, bg is the upper segment and 1-7 is the vertical eighth character to use.

Raises:

BarGraphError – the smoothed rows do not add up to the graph height.

use_smoothed() → bool

Return whether smoothed (sub-character resolution) rendering can be used.

GraphVScale

class urwid.GraphVScale(labels: Sequence[tuple[float | int, _TagMarkup]], top: float)

Box widget that renders a vertical scale of labels for a BarGraph.

Build a vertical scale for the BarGraph widget, which can correspond to the BarGraph’s horizontal lines.

Parameters:
  • labels – a sequence of (label position, label markup) pairs, where the position satisfies 0 < position < top and gives the y position of the label, and the markup is the text markup for that label

  • top – top y position

render(size: tuple[int, int], focus: bool = False) → SolidCanvas | CompositeCanvas

Render GraphVScale.

selectable() → Literal[False]

Return False.

set_scale(labels: Sequence[tuple[float | int, _TagMarkup]], top: float) → None

Replace the labels and the top y position of the scale.

Parameters:
  • labels – a sequence of (label position, label markup) pairs, where the position satisfies 0 < position < top and gives the y position of the label, and the markup is the text markup for that label

  • top – top y position

ProgressBar

class urwid.ProgressBar(normal: Hashable, complete: Hashable, current: float = 0, done: int = 100, satt: Hashable = None)

Flow widget that displays a completion percentage as a bar of Unicode block characters.

Build the progress bar with the given display attributes and range.

Parameters:
  • normal – display attribute for incomplete part of progress bar

  • complete – display attribute for complete part of progress bar

  • current – current progress

  • done – progress amount at 100%

  • satt – display attribute for smoothed part of bar where the foreground of satt corresponds to the normal part and the background corresponds to the complete part. Smoothing requires UTF-8 encoding. If satt is None then no smoothing will be done.

>>> from urwid import LineBox
>>> pb = ProgressBar("a", "b")
>>> pb
<ProgressBar flow widget>
>>> print(pb.get_text())
0 %
>>> pb.set_completion(34.42)
>>> print(pb.get_text())
34 %
>>> class CustomProgressBar(ProgressBar):
...     def get_text(self):
...         return "Foobar"
>>> cpb = CustomProgressBar("a", "b")
>>> print(cpb.get_text())
Foobar
>>> for x in range(101):
...     cpb.set_completion(x)
...     s = cpb.render((10,))
>>> cpb2 = CustomProgressBar("a", "b", satt="c")
>>> for x in range(101):
...     cpb2.set_completion(x)
...     s = cpb2.render((10,))
>>> pb = ProgressBar("a", "b", satt="c")
>>> pb.set_completion(34.56)
>>> print(LineBox(pb).render((20,)))
┌──────────────────┐
│      ▏34 %       │
└──────────────────┘
property done: int

Return the progress amount that represents 100%.

get_text() → str

Return the progress bar percentage text.

You can override this method to display custom text.

render(size: tuple[int], focus: bool = False) → TextCanvas

Render the progress bar.

rows(size: tuple[int], focus: bool = False) → int

Return the number of rows the progress bar occupies, always 1.

set_completion(current: float) → None
Parameters:

current – current progress

Spinner

class urwid.Spinner(frames: SpinnerFrames | Sequence[str] = SpinnerFrames.LINE, *, interval: float = 0.1, idle: _TagMarkup | None = None, loop: MainLoop | None = None, align: Literal['left', 'center', 'right'] | Align = Align.LEFT, wrap: Literal['space', 'any', 'clip', 'ellipsis'] | WrapMode = WrapMode.SPACE)

A text widget showing its frames in turn while some work runs.

start() shows the first frame and sets an alarm that shows the next one every interval seconds. The frame is chosen from the time elapsed since start(), so a late alarm skips frames instead of slowing the spinner down. stop() removes the alarm and shows the idle text, which is also shown before the first frame. step() shows the next frame by hand, for an application that drives the spinner from its own tick.

start(), step() and stop() replace the text, so put a label in its own Text next to the spinner, for example in a Columns. Every frame and the default idle text have one width, so a "pack" cell keeps its width.

The alarm is set on loop, or, without one, on the MainLoop running when start() is called (see get_running_loop()). The pending alarm keeps a running spinner alive: stop a spinner that leaves the screen.

Important

Call every method from the thread running the loop, like every other widget change. Another thread hands the call over with MainLoop.call_from_thread().

>>> spinner = Spinner(Spinner.Frames.LINE)
>>> spinner.render((3,)).text
[b'   ']
>>> spinner.step()
>>> spinner.render((3,)).text
[b'-  ']
>>> spinner.step()
>>> spinner.render((3,)).text
[b'\\  ']
>>> spinner.stop()
>>> spinner.render((3,)).text
[b'   ']

Build a stopped spinner showing the idle text.

Parameters:
  • frames – frames shown in turn, a Spinner.Frames member or a sequence of strings. Frames narrower than the widest are padded with spaces on the right.

  • interval – seconds each frame is shown while running, see interval.

  • idle – text markup shown while stopped, see idle.

  • loop – loop the alarm is set on. None uses the loop running at each start().

  • align – alignment of the text.

  • wrap – wrap mode of the text.

Raises:
  • TypeError – frames is not a Spinner.Frames member or a sequence of strings, or interval is not a number.

  • ValueError – frames is empty, a frame is not printable or is zero columns wide, or interval is not finite and > 0.

  • TextError – align or wrap is not supported by the text layout.

  • TagMarkupException – idle is not valid text markup.

Frames

alias of SpinnerFrames

property frames: tuple[str, ...]

The frames shown in turn, each padded to one width.

property idle: _TagMarkup | None

Text markup shown while stopped, or None for spaces as wide as a frame.

A new value shows at once while the idle text shows, and otherwise from the next stop().

Raises:

TagMarkupException – the new value is not valid text markup.

property interval: float

Seconds each frame is shown while running.

A new value applies at once: a running spinner keeps the frame showing and shows the next one after the new interval.

Raises:
  • TypeError – the new value is not a number.

  • ValueError – the new value is not finite and > 0.

  • Exception – the loop failed to set the new alarm; the spinner is then stopped.

property loop: MainLoop | None

Loop the alarm is set on, or None to use the loop running at each start().

property running: bool

Whether the frames are changing on an alarm, between start() and stop().

start() → None

Show frames in turn on an alarm, until stop().

A spinner started from the idle text shows the first frame. One started after step() goes on from the frame showing. A running spinner is left as it is. A call that raises leaves the spinner as it was.

Raises:

RuntimeError – there is no loop and no loop is running. Pass loop= to start a spinner before the loop runs.

step() → None

Show the next frame.

On a stopped spinner the first call shows the first frame. On a running one the frames after it keep the shift by one.

stop() → None

Remove the alarm and show the idle text.

A later start() begins at the first frame. A stopped spinner only shows the idle text again.

class urwid.widget.spinner.SpinnerFrames(*values)

Common frame sets for Spinner, also reachable as Spinner.Frames.

The frames of each member have one width. On a terminal without UTF-8:

  • LINE and DOTS are ASCII and are drawn as they are.

  • Every other member is drawn as its set_ascii_fallbacks() replacement, or as ? without one.

Note

The default terminal fonts of GNOME, KDE Plasma and Xfce on Ubuntu, Fedora and Arch, and of Windows Terminal, show every member. The default fonts of the Linux virtual console are:

  • Ubuntu: Fixed 8x16.

  • Fedora and openSUSE: eurlatgr.

  • Arch: the built-in 8x16 font of the kernel.

The default fonts of the Linux virtual console and of the classic Windows console show none of the non-ASCII members, so use LINE or DOTS there unless the console font has wider coverage.

BigText

class urwid.BigText(markup: _TagMarkup, font: Font)

Fixed widget that renders text using a large Font.

Build the widget from markup rendered with a large font.

Parameters:
  • markup – same as Text widget markup

  • font – instance of a Font class

get_text() → tuple[str, list[tuple[Hashable, int]]]

Return (text, attributes).

pack(size: tuple[()] | None = (), focus: bool = False) → tuple[int, int]

Return the (cols, rows) size needed to render this widget’s text in its font.

render(size: tuple[()], focus: bool = False) → CompositeCanvas

Render this widget’s text as a canvas joining each character’s font glyph.

set_font(font: Font) → None

Replace the font used to render this widget’s text.

set_text(markup: _TagMarkup) → None

Replace the displayed text and attributes, decomposing markup into them.

urwid.get_all_fonts() → list[tuple[str, FontRegistry]]

List of (font name, font class) tuples.

Terminal

class urwid.Terminal(command: tuple[str | bytes | os.PathLike[str] | os.PathLike[bytes], ...] | list[str | bytes | os.PathLike[str] | os.PathLike[bytes]] | Callable[[], Any] | None, env: Mapping[str, str] | Iterable[tuple[str, str]] | None = None, main_loop: _WatchLoop | None = None, escape_sequence: str | None = None, encoding: str = 'utf-8')

Widget that runs a child process (or callable) and renders its terminal output.

Initialize a terminal emulator widget.

command is the command to execute inside the terminal, provided as a list of the command followed by its arguments. If ‘command’ is None, the command is the current user’s shell. You can also provide a callable instead of a command, which will be executed in the subprocess.

env can be used to pass custom environment variables. If omitted, os.environ is used.

main_loop is the loop to watch the pty on: an event loop or a MainLoop, whose remove_watch_file must accept the handle its watch_file returned. Left out, the terminal watches the pty on the started MainLoop it is rendered under, and while none runs it reads the pty on every render. The watch moves at the first render under another loop, or under no loop, and is removed by terminate().

escape_sequence is the urwid key symbol which should be used to break out of the terminal widget. If it’s not specified, ctrl a is used.

encoding specifies the encoding that is being used when local keypresses in Unicode are encoded into raw bytes. UTF-8 is used by default. Set this to the encoding of your terminal if you need to transmit characters to the spawned process in non-UTF8 encoding. Applies to Python 3.x only.

Note

If you notice your Terminal instance is not printing unicode glyphs correctly, make sure the global encoding for urwid is set to utf8 with urwid.set_encoding("utf8"). See Encodings Supported for more details.

add_watch() → None

Register the pty with a loop, so feed() runs on new child output.

The loop is main_loop, or without one the started MainLoop running in the caller’s context. Nothing is registered while a watch is set, or when there is no such loop.

beep() → None

Emit a “beep” signal for anything listening on this widget.

change_focus(has_focus: bool) → None

Ignore SIGINT if this widget has focus.

feed() → None

Read pending output from the pty and feed it to the terminal, terminating the child on EOF.

flush_responses() → None

Write every buffered response to the pty and clear the buffer.

get_cursor_coords(size: tuple[int, int]) → tuple[int, int] | None

Return the cursor coordinates for this terminal.

keypress(size: tuple[int, int], key: str) → str | None

Translate key into the byte sequence the child process expects and write it to the pty.

Returns:

None when the widget used key, or key when it did not: the child has exited, the key is handed down to the parent outside a key grab, or the linux terminal type the child is given has no byte form for it, as for a cursor or function key with shift, meta or ctrl.

leds(which: Literal['clear', 'scroll_lock', 'num_lock', 'caps_lock']) → None

Emit a “leds” signal asking the display to set keyboard LED state which.

mouse_event(size: tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Report a mouse event to the child in the tracking mode and the encoding the child turned on.

The report bytes are the ones xterm sends for the same event.

Returns:

True when the child tracks the mouse, also for an event its tracking mode does not report. Such events are a drag under mode 1000, the release of a wheel button, and under X10 compatibility mode (9) anything but a press of buttons 1 to 3. False when the event is left to the parent, in these cases:

  • the child has exited;

  • the child does not track the mouse, or tracks it in highlight mode (1001), which is not implemented;

  • the view is scrolled back from the screen the child sees;

  • the event is not a press, a release or a drag.

remove_watch() → None

Unregister the pty from the loop add_watch() registered it on, when it did.

render(size: tuple[int, int], focus: bool = False) → TermCanvas

Resize the terminal to size, read any pending child output, and return the terminal canvas.

respond(string: str) → None

Respond to the underlying application with ‘string’.

set_termsize(width: int, height: int) → None

Tell the pty the terminal window is now width by height characters.

set_title(title: str) → None

Emit a “title” signal with the terminal’s new title.

spawn() → None

Fork the child process running command and connect it to a new pty.

terminate() → None

Stop watching the pty, end the child process and close the pty master.

Signals escalate from SIGHUP to SIGKILL until the child exits. A child that may not be signalled, such as a setuid program, only gets the hangup from closing the master. It is then waited for at most one second, and left unreaped if it keeps running.

touch_term(width: int, height: int) → None

Spawn the child process if needed, then resize the pty and terminal canvas to width by height.

wait_and_feed(timeout: float = 1.0) → None

Block up to timeout seconds for pty output, then read and process whatever has arrived.

Scroll Widget Classes

Scrollable

class urwid.Scrollable(widget: WrappedScrollWidget, force_forward_keypress: bool = False, *, delegate_selectable: bool = False)

Box widget decoration that makes a fixed or flow widget vertically scrollable.

Box widget that makes a fixed or flow widget vertically scrollable.

Note

Focusable widgets are handled, including switching focus, but possibly not intuitively, depending on the arrangement of widgets.

When switching focus to a widget that is ouside of the visible part of the original widget, the canvas scrolls up/down to the focused widget.

It would be better to scroll until the next focusable widget is in sight first. But for that to work we must somehow obtain a list of focusable rows in the original canvas.

Parameters:
  • widget – fixed or flow widget to scroll.

  • force_forward_keypress – pass every key to widget first, even when it shows no cursor.

  • delegate_selectable – take the input focus only when widget is selectable. A widget that is not, such as a Text, then cannot be scrolled by keys.

Raises:

ValueError – widget is neither a FIXED nor a FLOW widget.

get_scrollpos(size: tuple[int, int] | None = None, focus: bool = False) → int

Return the current scrolling position.

Lower limit is 0, upper limit is the maximum number of rows with the given maxcol minus maxrow.

..note::

The returned value may be too low or too high if the position has changed but the widget wasn’t rendered yet.

keypress(size: tuple[int, int], key: str) → str | None

Handle scrolling keys, forwarding everything else to the wrapped widget when appropriate.

mouse_event(size: tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Forward a mouse event to the wrapped widget, adjusting the row for the current scroll position.

render(size: tuple[int, int], focus: bool = False) → CompositeCanvas

Render the wrapped widget, trimmed to the current scroll position.

rows_max(size: tuple[int, int] | None = None, focus: bool = False) → int

Return the number of rows for size.

If size is not given, the currently rendered number of rows is returned.

Raises:

ScrollableError – the wrapped widget is neither a FLOW nor a FIXED widget.

selectable() → bool

Return whether the widget accepts the input focus.

Always True, unless delegate_selectable is set: then what the wrapped widget returns.

set_scrollpos(position: SupportsInt) → None

Set scrolling position.

If position is positive it is interpreted as lines from the top. If position is negative it is interpreted as lines from the bottom.

Values that are too high or too low values are automatically adjusted during rendering.

sizing() → frozenset[Sizing]

Return that this widget is always a box widget.

ScrollBar

class urwid.ScrollBar(widget: WrappedScrollableWidget, thumb_char: str = ScrollbarSymbols.FULL_BLOCK, trough_char: str = ' ', side: Literal['left', 'right'] = 'right', width: int = 1, *, visible: bool | None = None, scroll_by_focus: bool = False, delegate_selectable: bool = False, end_markers: tuple[str, str] | None = None, attr: Hashable = None, focus_attr: Hashable = None, thumb_attr: Hashable = None, thumb_focus_attr: Hashable = None, marker_dim_attr: Hashable = None)

Widget decoration that adds a scrollbar alongside a scrollable wrapped widget.

Create a widget that adds a scrollbar to widget.

Parameters:
  • widget –

    box widget that has, or wraps a widget that has, the following methods:

    • get_scrollpos takes the arguments size and focus and returns the index of the first visible row.

    • set_scrollpos (optional, on the same widget as get_scrollpos; needed for mouse click and drag support on the scrollbar) takes the index of the first visible row. The scrollbar does not use ListBox.set_scrollpos(); see scroll_by_focus instead.

    • rows_max takes size and focus and returns the total number of rows widget can render.

  • thumb_char – character used for the scrollbar handle.

  • trough_char – character used for the space above and below the handle.

  • side – side the scrollbar is drawn on, 'left' or 'right'.

  • width – number of columns the scrollbar uses, at least 1.

  • visible – when the scrollbar is drawn: None draws it only when widget needs scrolling, True always, so the wrapped widget always gets the same width, and False never, so the wrapped widget gets the full width and is still scrolled by keys and wheel.

  • scroll_by_focus – let a click or drag on the scrollbar scroll a ListBox by moving its focus to the widget at the top of the new view. The focus then changes without passing through the ListBox keypress or mouse_event, so this is off by default for code that tracks the focus there.

  • delegate_selectable – take the input focus only when widget is selectable; a ListBox created with focus_when_empty=False is not while it is empty. Off by default, so a wrapped widget that is not selectable can still be scrolled by keys.

  • end_markers – characters drawn in the first and the last scrollbar row, or None for no markers, such as (ScrollBar.Symbols.ARROW_TRIANGLE_UP, ScrollBar.Symbols.ARROW_TRIANGLE_DOWN). They are drawn only when the scrollbar has at least 4 rows, so the thumb has 2 trough rows to move in, and a click on one scrolls one step towards its end, as the mouse wheel does.

  • attr – display attribute of the trough and the end markers.

  • focus_attr – display attribute of the trough and the end markers when rendered in focus; None uses attr.

  • thumb_attr – display attribute of the thumb; None uses attr.

  • thumb_focus_attr – display attribute of the thumb when rendered in focus; None uses thumb_attr when it is set, and focus_attr otherwise.

  • marker_dim_attr – display attribute of an end marker whose end of the content is visible; None draws it like the other marker. An end counts as visible only when the first or last row of the content is on screen. A custom widget scrolled by items rather than rows, through SupportsRelativeScroll, cannot report cut-off rows, so for it an end counts once its first or last item is on screen.

Raises:
  • ValueError – widget is not a BOX widget, or end_markers is not a pair.

  • TypeError – widget does not wrap anything that supports the scrolling protocol.

Symbols

alias of ScrollbarSymbols

get_cursor_coords(size: tuple[int] | tuple[int, int]) → tuple[int, int] | None

Return the cursor of the wrapped widget, moved past the scrollbar columns when they are on the left.

Returns:

None when the wrapped widget has no get_cursor_coords or no cursor.

keypress(size: tuple[int] | tuple[int, int], key: str) → str | None

Forward a keypress to the wrapped widget, at the size it is rendered at within size.

mouse_event(size: tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) → bool | None

Handle a mouse event, dispatching it to the scrollbar thumb/trough or to the wrapped widget.

A press of button 1 on the scrollbar, and the drag that follows it, move the thumb top to the pointer row. So does its release on displays that report the button of a release, such as curses, which send no drags. A press of button 1 on an end marker scrolls one step towards that end, as the mouse wheel does, and the drag and release that follow it do nothing. Events in the wrapped widget’s columns go to it. On the scrollbar, the vertical mouse wheel of a widget the scrollbar does not scroll through set_scrollpos (such as ListBox) is passed on, together with the drags and releases described below; other events there are not.

A drag or release goes where its press went: a drag started on the scrollbar keeps moving the thumb over the wrapped widget’s columns, and a drag started on the wrapped widget stays with it over the scrollbar. A drag or release whose press was not seen here, such as the end of a drag and drop started in another widget, goes to the wrapped widget. Events outside this widget never reach it, so a release there is not seen; the next press here starts over.

render(size: tuple[int] | tuple[int, int], focus: bool = False) → Canvas

Render the wrapped widget together with the scrollbar, when scrolling is needed.

A flow size renders the wrapped widget at its flow rows, so it never needs scrolling.

rows(size: tuple[int], focus: bool = False) → int

Return the rows of the wrapped widget at the width it is rendered at.

With visible True the scrollbar always takes its columns, so the wrapped widget gets the rest.

Raises:

AttributeError – the wrapped widget has no rows(), as a Scrollable has none.

property scroll_by_focus: bool

Whether a click or drag on the scrollbar scrolls a ListBox by moving its focus.

property scrollbar_side: Literal['left', 'right']

Where to display the scrollbar; must be ‘left’ or ‘right’.

property scrollbar_width: int

Columns the scrollbar uses.

property scrolling_base_widget: SupportsScroll

Nearest original_widget that is compatible with the scrolling API.

Raises:

ScrollableError – no wrapped widget supports the scrolling protocol.

selectable() → bool

Return whether the widget accepts the input focus.

Always True, unless delegate_selectable is set: then what the wrapped widget returns.

sizing() → frozenset[Sizing]

Return BOX, and FLOW as well for a wrapped ListBox with ListBox.flow set.

The ListBox may be wrapped in decorations such as AttrMap, as long as they report FLOW too.

property visible: bool | None

When the scrollbar is drawn: None when scrolling is needed, True always, False never.

class urwid.widget.scrollable.ScrollbarSymbols(*values)

Common symbols suitable for scrollbar.

The thumb and trough symbols come first, and the ARROW_* pairs for the end markers follow them.

Only these members are also drawn on terminals without UTF-8, where the others are drawn as ?:

Note

The default terminal fonts of GNOME, KDE Plasma and Xfce on Ubuntu, Fedora and Arch, and of Windows Terminal, show every member. The default fonts of the Linux virtual console are:

  • Ubuntu: Fixed 8x16.

  • Fedora and openSUSE: eurlatgr.

  • Arch: the built-in 8x16 font of the kernel.

The default fonts of the Linux virtual console and of the classic Windows console show these non-ASCII members:

Member

Symbol

Ubuntu

Fedora

Arch

Windows

FULL_BLOCK

█

yes

yes

yes

yes

DARK_SHADE

▓

no

yes

yes

yes

MEDIUM_SHADE

▒

yes

yes

yes

yes

LITE_SHADE

░

yes

yes

yes

yes

DRAWING_LIGHT

│

yes

yes

yes

yes

DRAWING_LIGHT_2_DASH

╎

yes

no

no

no

DRAWING_LIGHT_3_DASH

┆

yes

no

no

no

DRAWING_LIGHT_4_DASH

┊

yes

no

no

no

DRAWING_HEAVY

┃

yes

yes

no

no

DRAWING_HEAVY_2_DASH

╏

yes

no

no

no

DRAWING_HEAVY_3_DASH

┇

yes

no

no

no

DRAWING_HEAVY_4_DASH

┋

yes

no

no

no

DRAWING_DOUBLE

║

yes

yes

yes

yes

BLACK_VERTICAL_RECTANGLE

▮

yes

no

no

no

MIDDLE_DOT

·

yes

yes

yes

yes

WHITE_VERTICAL_RECTANGLE

▯

no

no

no

no

BLACK_SQUARE

■

yes

yes

yes

yes

WHITE_SQUARE

□

no

no

no

yes

BULLET

yes

yes

yes

yes

BLACK_CIRCLE

●

yes

yes

no

yes

WHITE_CIRCLE

○

no

no

yes

yes

ARROW_TRIANGLE_UP

▲

yes

no

yes

yes

ARROW_TRIANGLE_DOWN

▼

yes

no

yes

yes

ARROW_SMALL_TRIANGLE_UP

▴

yes

no

no

no

ARROW_SMALL_TRIANGLE_DOWN

▾

yes

no

no

no

ARROW_WHITE_TRIANGLE_UP

△

no

no

no

no

ARROW_WHITE_TRIANGLE_DOWN

▽

no

no

no

no

ARROW_UP

↑

yes

yes

yes

yes

ARROW_DOWN

↓

yes

yes

yes

yes

ARROW_HEAVY_UP

⬆

no

no

no

no

ARROW_HEAVY_DOWN

⬇

no

no

no

no

ARROW_MODIFIER_UP

U+02C4

no

no

no

no

ARROW_MODIFIER_DOWN

U+02C5

no

no

no

no

A member marked no needs a console font with wider coverage.

ARROW_ASCII_DOWN = 'v'
ARROW_ASCII_UP = '^'
ARROW_DOWN = '↓'
ARROW_HEAVY_DOWN = '⬇'
ARROW_HEAVY_UP = '⬆'
ARROW_MODIFIER_DOWN = '˅'
ARROW_MODIFIER_UP = '˄'
ARROW_SMALL_TRIANGLE_DOWN = '▾'
ARROW_SMALL_TRIANGLE_UP = '▴'
ARROW_TRIANGLE_DOWN = '▼'
ARROW_TRIANGLE_UP = '▲'
ARROW_UP = '↑'
ARROW_WHITE_TRIANGLE_DOWN = '▽'
ARROW_WHITE_TRIANGLE_UP = '△'
BLACK_CIRCLE = '●'
BLACK_SQUARE = '■'
BLACK_VERTICAL_RECTANGLE = '▮'
BULLET = '•'
DARK_SHADE = '▓'
DRAWING_DOUBLE = '║'
DRAWING_HEAVY = '┃'
DRAWING_HEAVY_2_DASH = '╏'
DRAWING_HEAVY_3_DASH = '┇'
DRAWING_HEAVY_4_DASH = '┋'
DRAWING_LIGHT = '│'
DRAWING_LIGHT_2_DASH = '╎'
DRAWING_LIGHT_3_DASH = '┆'
DRAWING_LIGHT_4_DASH = '┊'
FULL_BLOCK = '█'
LITE_SHADE = '░'
MEDIUM_SHADE = '▒'
MIDDLE_DOT = '·'
WHITE_CIRCLE = '○'
WHITE_SQUARE = '□'
WHITE_VERTICAL_RECTANGLE = '▯'