Widget Classes¶
Widget Base Classes¶
Widget¶
- class urwid.Widget¶
Widget base class.
- _selectable = False¶
The default
selectable()method returns this value.
- _command_map = urwid.command_map¶
A shared
CommandMapinstance. May be redefined in subclasses or widget instances.
- rows(size, focus=False)¶
Note
This method is not implemented in
Widgetbut must be implemented by any flow widget. Seesizing().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
WidgetmetaclassWidgetMetathat causes the result of this function to be retrieved from any canvas cached byCanvasCache, so if your widget has been rendered you may not receive calls to this function. The class variableignore_focusmay be defined and set toTrueif 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
Widgetbut must be implemented by any widget that may return cursor coordinates as part of the canvas thatrender()returns.- Parameters:
size – See
Widget.render()for details.- Returns:
(col, row) if this widget has a cursor,
Noneotherwise
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
Noneif no cursor is displayed.The
ListBoxwidget 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
Widgetbut 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
ListBoxandPilewidgets call this method on a widget losing focus and use the value returned to callmove_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
Widgetbut may be implemented by a subclass. Not implementing this method is equivalent to having a method that always returnsFalse.- 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:
Trueif the position was set successfully anywhere on row,Falseotherwise
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 accessingfocus_positionon 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:
size – See
Widget.render()for detailskey – a single keystroke value; see Keyboard Input
- Returns:
Noneif 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 Inputbutton – a
MouseButtonvalue, usually as a plainint. 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
Trueif this widget or one of its children is in focus
- Returns:
Trueif the event was handled by this widget,Falseotherwise- 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.
Textwidgets have implemented this method. You can useText.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
Trueif this widget or one of its children is in focus
- Returns:
A
Canvassubclass instance containing the rendered content of this widget- Raises:
NotImplementedError – the subclass does not implement rendering.
Textwidgets return aTextCanvas(arbitrary text and display attributes),SolidFillwidgets return aSolidCanvas(a single character repeated across the whole surface) and container widgets return aCompositeCanvas(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
WidgetmetaclassWidgetMetathat causes the result of this method to be cached byCanvasCache. Later calls will automatically look up the value in the cache first.As a small optimization the class variable
ignore_focusmay be defined and set toTrueif 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:
Trueif this widget contains something the user might want to interact with,Falseotherwise.
This default implementation returns
_selectable. Subclasses may leave it as is if they are not selectable, or, if they are always selectable, set the_selectableclass variable toTrue.If this method returns
Truethen theWidget.keypress()method must be implemented.Returning
Falsedoes 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 theirsizeparameter, and therows()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 thepack()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_navigationcalls this when it moves its focus to this widget, see Moving the Focus with Tab. The hook goes to the widget thatkeypress()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
rendermethod. 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.mockobject.>>> 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_navigationcalls this when it moves its focus to this widget, see Moving the Focus with Tab. The hook goes to the widget thatkeypress()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
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
StandardTextLayoutinstanceselectable –
Truemakes 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 –
Noneor the (text, display attributes) tuple returned fromget_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 aLayeredAttrof 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
TextLayoutinstance 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 –
Noneor()for unlimited screen columns (like FIXED sizing) or (maxcol,) to specify a maximum column sizefocus – 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
StandardTextLayoutinstance.- 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
StandardTextLayoutinstance
>>> 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
Textfor 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
StandardTextLayoutinstance.- 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 withmultiline=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 whenvalidationchanges. It receives the old and the new value as arguments."completion_cycle"is sent when a completion cycle starts, moves or ends, seetext_completer. It receives the new value ofcompletion_cycleas an argument.
Create the Edit widget.
- Parameters:
caption – markup for caption preceding edit_text, see
Textfor 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 italign – typically ‘left’, ‘center’ or ‘right’
wrap – typically ‘space’, ‘any’ or ‘clip’
allow_tab –
True: tab is inserted,False: return itedit_pos – initial position for cursor, None:end of edit_text
layout – defaults to a shared
StandardTextLayoutinstancemask – 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 charactermax_length – the longest
edit_textallowed, seemax_lengthplaceholder – markup drawn after the caption while
edit_textis empty, seeplaceholderplaceholder_grow – whether a placeholder taller than the empty edit adds rows, see
placeholder_growvalidator – called with
edit_textto check it, seevalidatorvalidation_attr – attribute map applied for each
Severity, seevalidation_attrvalidation_focus_attr – attribute map applied for each
Severitywhile focused, seevalidation_focus_attrtext_completer – called with
edit_textandedit_posfor completions, seetext_completerundo_limit – the number of changes
undo()can take back, seeundo_limitkill_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,Noneotherwise.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 whattext_completerreturned, 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
Nonewhen 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_textlonger thanmax_lengthinserts nothing and is returned, asIntEditreturns a digit past its limit. A tab is inserted whole or not at all.The readline delete commands described in
CommandMapand'yank'go throughset_edit_text(). A subclass whoseset_edit_text()raises for some text has to handle those commands itself once an application binds them, or the exception leaveskeypress().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 ofset_edit_text()orinsert_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, seetext_completer. What it raises for the text of'undo','redo'or'yank', seeundo().
- property kill_buffer: MutableSequence[str | bytes] | None¶
The list that the readline word and line delete commands put the deleted text into, or
Nonefor 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 withoutmultiline, a letter in anIntEdit, text that makesedit_textlonger thanmax_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
TextLayoutinstance used to lay out and wrap this widget’s text.
- property max_length: int | None¶
The longest
edit_textallowed, orNonefor 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, seekeypress();set_edit_text()andinsert_text()raiseValueErrorinstead. Setting it negative or below the length ofedit_textraisesValueErrortoo.
- 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_textis empty, orNonefor none.It is drawn focused or not, and it is never masked. It is not part of the text:
get_text(),edit_text,max_lengthandpack()ignore it. It is drawn aftercaptionand its attributes, not after the caption part ofget_text(). The cursor stays where it is without a placeholder, so withalign="left"it is on its first column. Withalign="center"oralign="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
placeholderlaid out on more rows than the empty edit adds rows.Falsecuts the placeholder to the rows of the empty edit, so the height does not change on the first key.Trueadds rows while the edit is empty.Nonegrows amultilineedit 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;
Falsewhen there is nothing to redo, or the text to restore is longer thanmax_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
placeholderthat 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 byset_edit_pos();Nonekeeps the current position, clipped to the new text. An override passesNoneon 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 –
validatorreturned something else than the types it lists; the text is changed already.
Called from elsewhere than a key,
undo()orredo(), it clears the undo history, seeundo_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
Severityinvalidation, orNonewhen 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, orNonefor 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_tabis 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
validatorruns 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 leaveskeypress(). 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 throughset_edit_text(), so signals andvalidatorrun as for any change.- Returns:
whether the text changed;
Falsewhen there is nothing to undo, or the text to restore is longer thanmax_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, throughset_edit_text()orinsert_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, callundo()andredo().>>> 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
validatoronedit_textnow and return the newvalidation.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 ownvalidate()for something else.- Raises:
TypeError – the validator returned something else than the types
validatorlists;validationis left as it was.
- property validation: tuple[ValidationMessage, ...]¶
Findings of
validatorfor the currentedit_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
Severityto whatAttrMaptakes as its attribute map, orNone. 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
severitywhile it is focused.It is set as
validation_attr. A severity with no entry here uses itsvalidation_attrentry when focused. A copy is returned.
- property validator: Callable[[str], ValidationMessage | str | Iterable[ValidationMessage] | None] | None¶
Callable that checks the whole
edit_text, orNonefor no check.It is called with the edit text and returns its findings:
Noneor an empty iterable when the text is valid;a
str, which is an error message;an iterable of
ValidationMessage.
It is called once by each
set_edit_text()call, after"postchange", when it is assigned, and byvalidate(), never while rendering. A subclass whoseset_edit_text()does not call this class’s method callsEdit.validate(self)itself, which avalidate()of its own does not replace. The result isvalidation. Keys, focus,"submit"and"cancel"do not depend on it. An application readsvalidationto 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 thatvalue()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
ParentNodebut definesget_first_childis 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_iconis set. Withconnector_linesset, the lines fromget_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_linesis not set, for the root, and whenindent_colsis 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 anindent_colsof 1 the connector has no room for it.
- line_vertical: str = '│'¶
The one-column line passing a node that has a sibling further down.
- 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:
LIGHT_VERTICAL,LIGHT_TEE,LIGHT_CORNERandLIGHT_HORIZONTALare drawn with the DEC special graphics character set.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 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
BULLETyes
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
noneeds 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,stror integraldecimal.Decimal, does not consist of digits valid for base, is negative without allow_negative, or has more digits thansys.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,strordecimal.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
preserveSignificanceanddecimalSeparatorarguments are deprecated, use thepreserve_significanceanddecimal_separatorkeyword 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¶
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,
Nonefor the default one (keyword only, seeCheckBox.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.statestill holds the old one."postchange"is sent after the state changes. The callback gets the old state.stateholds 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
CheckBoxandRadioButton.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
BULLETyes
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_SIGNU+2212
yes
yes
no
yes
EN_DASHU+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
noneeds 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
DropDown¶
- class urwid.DropDown(options: Iterable[_TagMarkup], *, selected: int = 0, max_visible_items: int = 10, attr_map: _AttrMapLike = None, focus_map: _AttrMapLike = None)¶
Drop-down choice: shows the selected option, and on activation opens a list of all options below it.
The list opens with the selected option focused. It moves above the widget when it does not fit below, and when it fits on neither side it takes the side with more rows and scrolls. enter, space or a click chooses the focused option and closes the list. esc, or a click outside the list, closes it without a change.
With a single option there is nothing to choose, so the widget is read-only: it is not selectable, and activating it opens no list.
Important
DropDown requires
MainLoop(..., pop_ups=True), or anotherPopUpTargetabove it. The list is a pop-up, and without a target activating the widget shows nothing.Signals supported:
'change', sent ascallback(drop_down, new_index)beforeselectedchanges, so the callback still reads the previous value there.>>> drop_down = DropDown(["red", "green", "blue"], selected=1) >>> drop_down.render(()).text [b'green v'] >>> drop_down.selected = 2 >>> drop_down.render((10,)).text [b'blue v']
Create a drop-down over options, with the option at index selected shown.
- Parameters:
options – markup of each option, in list order; see
Textfor markup.selected – index of the option shown first.
max_visible_items – most options the open list shows at once; it scrolls through the rest.
attr_map – display attribute of each option in the open list, as for
AttrMap.focus_map – display attribute of the focused option in the open list.
- Raises:
ValueError – options is empty, or max_visible_items is less than 1.
IndexError – selected is not an index of options.
- create_pop_up() ScrollBar[ListBox[int]]¶
Return the list of all options, with the selected one focused; it scrolls when they do not all fit.
- drop_down_indicator: ClassVar[Text] = <Text fixed/flow widget 'v'>¶
Widget drawn after the selected option; a subclass replaces it to change the indicator.
- get_pop_up_parameters() PopUpParametersModel¶
Return the list position directly below this widget, anchored to it so it can move above it.
- keypress(size: tuple[()] | tuple[int] | tuple[int, int], key: str) str | None¶
Open the list on the
activatecommand.When the widget is not selectable, the list does not open and the key is returned unhandled.
- mouse_event(size: tuple[()] | tuple[int] | tuple[int, int], event: str, button: int, col: int, row: int, focus: bool) bool¶
Open the list on a button 1 press.
When the widget is not selectable, the list does not open and the press is not handled.
- property options: tuple[_TagMarkup, ...]¶
Markup of each option, in list order.
- pack(size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) tuple[int, int]¶
Return the size of this widget; with no size, as wide as the widest option.
- render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) CompositeCanvas | Canvas¶
Render the selected option and the indicator, with the list attached when it is open.
- selectable() bool¶
Return whether there is more than one option to choose from.
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
Textfor description of text markupcursor_position – position the cursor will appear in the text when this widget is in focus;
Noneshows no cursoralign – typically
'left','center'or'right'wrap – typically
'space','any','clip'or'ellipsis'layout – defaults to a shared
StandardTextLayoutinstance
- 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
Textwidget 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 internalTextinstance that this widget wraps viaWidgetWrap, so this widget’spack/render/rowsbehaviour is identical toTextfor 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/Bgenuinely moves the cursor up/down between rows, and a bare\rgenuinely overwrites the current row in place (letting progress-bar-style redraw sequences resolve correctly) –ansi_textis treated as a single, complete block, meant to render one already-complete captured screen/block.Pass
one_line=Trueto restrict this widget to a single physical line of resolved text; seeAnsiParser’sone_lineflag 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 atuple[ParsedLine, ...]in this mode (line ends, VT and FF outside an OSC string are structural split points, never silently discarded), constructing anANSITextdirectly 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. Usefrom_lines()to turn arbitrary multi-line (or arbitrarily chunked) raw text into a complete sequence ofANSITextinstances, 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_lineis 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 theANSITextorParsedLineof 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_lineis 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
ANSITextwidget(s).chunks(e.g. successiveos.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 singleAnsiParseracross all chunks, so a line split across two chunk boundaries – or an escape sequence split across two chunk boundaries – is still resolved correctly, and callsfinalize()only once the iterable is exhausted.If
one_lineis true, oneANSITextis yielded per line the shared parser completes, in order, with SGR colour state and character sets carried forward continuously: each producedANSITextcontinues from the previous one, whether that previous line came from the same chunk or an earlier one.If
one_lineis false (the default), this naturally reduces to yielding exactly oneANSITextonce 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 theone_line=Truecase 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
ANSITextorParsedLineof 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/alignfor the internalText).
- Yields:
one or more
ANSITextinstances, in order, depending onone_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_attrto a subsequently constructedANSITextto 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_textand invalidate this widget.Parsing uses the mode (
one_line) this widget was constructed with; ifone_lineis true, only the first resulting line is used, see the class docstring.
- 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 (seeLayeredAttr), and an attribute mapped toNoneshows the attribute ofNonefocus_map – attribute to apply when in focus or dict of old display attribute: new display attribute mappings; if
Noneuse 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
Noneleft – 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
Nonefor 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 norows(). This error is anAttributeErroras 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
CLIPand 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:
Noneif 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 aLineBoxaround aTable. 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
Nonefor 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
textpadded 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
menucommand (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.
PopUpTargetrenders the pop-up with the box size declared byget_pop_up_parameters(), so the widget returned here must accept an (overlay_width, overlay_height) size. Wrap a flow or fixed widget in aFiller(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_heightkey makesPopUpTargetmove the pop-up above the launcher, or clip its height, when it does not fit below; seeCompositeCanvas.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
Nonebefore 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
Nonewhen 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
PopUpLauncherinside it.Pop-ups nest: a pop-up widget may itself be a
PopUpLauncherwith 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_coordsmethod.
- 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_colmethod.
- 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_keycallback its launcher set, if any; seeCompositeCanvas.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_outsidecallback its launcher set, if any. The press is then handled and does not reach the widgets under the pop-up. SeeCompositeCanvas.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_coordsmethod.
- 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.
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_countset, a new message dismisses the oldest ones over that count, somax_count=1makes a status line. While no message is showing, thedefaulttext is shown, or nothing, in zero rows.A message with a timeout sets an alarm on
loop, or, without one, on theMainLooprunning whennotify()is called (seeget_running_loop()). The loop redraws the screen after the alarm, so an expired message disappears with no further call.Sends
"change"once pernotify(),dismiss(),clear(), expiry, or loweredmax_countthat 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, seetimeout.max_count – messages shown at once, see
max_count.severity_attr – attribute map applied to a message for each
Severity, seeseverity_attr.default – text markup shown while no message is showing, or
Noneto show nothing.default_attr – attribute map applied to the default text, in any form
AttrMaptakes.loop – loop the alarms are set on.
Noneuses the loop running at eachnotify().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 orNone, or a key of severity_attr is not aSeverity.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
Noneto 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
Falsefor a message that expired or was dismissed before.
- property loop: MainLoop | None¶
Loop the alarms are set on, or
Noneto use the loop running at eachnotify().
- property max_count: int | None¶
Messages shown at once, or
Nonefor 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_attrentry.timeout – seconds until the message is dismissed,
Noneto keep it untildismiss(), or left out to usetimeout.
- Returns:
the message, to pass to
dismiss().- Raises:
TypeError – severity is not a
Severity, or timeout is not a number orNone.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. Passloop=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
Severityto whatAttrMaptakes as its attribute map, orNone. 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.
Notification¶
- class urwid.Notification(widget: Text, severity: Severity, timeout: float | None)¶
One message shown by
Notifications, asNotifications.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 timeout: float | None¶
Seconds the message is shown before it is dismissed, or
Nonewhen it stays until dismissed.
WidgetDisable¶
- class urwid.WidgetDisable(original_widget: WrappedWidget, *, disabled: bool = True)¶
Decoration widget that disables interaction with the widget it wraps.
While
disabledisTrue, this widget is never selectable, reports no cursor, and always passesfocus=Falseto the wrapped widget, even if it somehow does become the focus. While it isFalse, 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 anAttrMaparound it.- Parameters:
original_widget – the widget being disabled
disabled – start disabled;
Falsestarts 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_widgetunless 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=Falsewhile disabled.
- render(size: tuple[()] | tuple[int] | tuple[int, int], focus: bool = False) CompositeCanvas¶
Render the wrapped widget into a canvas, passing
focus=Falsewhile disabled.
- rows(size: tuple[int], focus: bool = False) int¶
Return the number of rows the wrapped widget occupies, passing
focus=Falsewhile disabled.
- selectable() bool¶
Return
Falsewhile 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
flowset, so containers lay it out as a box widget by default.Note
The main difference between a Frame and a
Pilewidget 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"afterfocus_partchanged, 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, seeframe_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 aKeyErrorwill be raised.Currently, all options are None, but using the
options()method to create the options value is recommended for forwards compatibility.
- property focus: BodyWidget | HeaderWidget | FooterWidget¶
Return the child
Widgetin 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 thefocus_positionsetter, 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’.
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_rowsof the body, the footer;footer in focus: the footer,
min_body_rowsof the body, the header;body in focus:
min_body_rowsof the body but at least 1, the parttrim_firstdoes not name, the part it names.
With the default
min_body_rows=0andtrim_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 detailsfocus –
Trueif 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
flowis 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
SimpleFocusListWalkerthat contains widgets to be displayed inside the list boxselect_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
AttrMaptakes; seeunfocused_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;
Falselets 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
ListWalkerbut has aget_focusmethod is deprecated. Support will be removed in version 7.0.- property body: ListWalker[_K, AbstractFlowWidget]¶
A ListWalker subclass such as
SimpleFocusListWalkerthat 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
ListWalkerbut has aget_focusmethod 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 detailsposition – 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
bodyto 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 asPilemoves 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
- 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
Nonewhen 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
wheelselects.Deprecated since version 2.4.0: Support for a contained widget that does not implement
mouse_eventis 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 detailsA 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_emptyFalseit 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'orNone.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 detailsoffset_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
0if 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
flowis 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 asset_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
SimpleFocusListWalkerthat contains widgets to be displayed inside the list boxselect_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
AttrMaptakes; seeunfocused_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;
Falselets 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
ListWalkerbut has aget_focusmethod 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"afterfocus_positionchanged, also through a change ofcontents. Its handlers receive the container, the old and the new focus position, which isNonewhile 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
Nonethe 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
AttrMaptakes; seeunfocused_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
contentsoptions.'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
IndexErrorif 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:
Noneif 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_eventis 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 detailsA 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 –
Nonefor'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 detailsfocus –
Trueif 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
contentscontains 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
WidgetPlaceholdermay 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"afterfocus_positionchanged, also through a change ofcontents. Its handlers receive the container, the old and the new focus position, which isNonewhile 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
AttrMaptakes; seeunfocused_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
contentsoptions.'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
rowsmethod, 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
IndexErrorif 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_eventis 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
contentslist.- Parameters:
height_type –
'pack','given'or'weight'height_amount –
Nonefor'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
WidgetPlaceholdermay 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"afterfocus_positionchanged, also through a change ofcontents. Its handlers receive the container, the old and the new focus position, which isNonewhile 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
AttrMaptakes; seeunfocused_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 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_navigationis 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
IndexErrorif 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:
Truewhen 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 likeListBox.
- 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"afterfocus_positionchanged, also through a change ofbody, with the old and the new position, which isNonewhile 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 theactivatecommand (enter and space by default) unhandled. A handler whose result is true consumes the key; otherwise the key is returned, asOverlaydoes 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
TableRowobjects oflayoutor sequences of cellsheader – 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;Nonedraws no footerfooter_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;
Nonenever drops itfooter_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.attrfocus_attr – attribute map of the focused row without its own
TableRow.focus_attrunfocused_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_attrdoesunfocused_focus_attr_func – function that gives that map for the focused row, instead of unfocused_focus_attr; see
unfocused_focus_attr_funcmax_rows – the most rows of body a flow size shows;
Noneshows every rowrow_attr –
row_attr(index, data)called each time a row renders, seerow_attrselect_navigation – let the
'next selectable'and'prev selectable'commands (tab and shift tab) move across the cells and then to the next or previous rowwheel – what a notch of the mouse wheel moves; see
wheelscrollbar –
Truedraws a scrollbar beside the rows,Noneonly when the rows do not fit,Falsenever; while it is on, its column counts in the natural widthscrollbar_options – the
ScrollBarparameters with ascrollbar_prefix, such asscrollbar_thumb_char,scrollbar_end_markers,scrollbar_sideandscrollbar_attr; ignored while scrollbar isFalse
- 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 –
ScrollBarrejects the value of ascrollbar_parameter.
- property body: _TableRows¶
The rows of the table, below the header.
A sequence of cells added to it becomes a
TableRowoflayout, sobody[i]is that row. A change is validated before it is applied, and aTableErrorleaves the rows unchanged. Replace every row withtable.body[:] = rows.
- 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.
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_attralone, to aTableRowoflayout, which keeps its own maps and may be a span row, or toNone. 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 anactivatecommand they leave unhandled.- Returns:
Noneif 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;
Noneshows 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
(), asWidget.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, orNone.A result other than
Noneis the row’s attribute map, in any formAttrMaptakes, overTableRow.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_rowsof body.Without
max_rowsevery 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"orNone.
- 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_attrfor the focused row.It is called with the
TableRow, and its map is applied over the row’s own map. SeeListBox.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 aTable.The attribute map applied to the row’s canvas is, in this order:
focus_attr, or the table’sfocus_attr, when the row renders in focus;the result of the table’s
row_attrcallable, when it returns something other thanNone;attr, or the table’sattr.
The
attrof aTableColumnmaps each cell of that column first, except in a span row, in the header of aTableand 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 forNonebecomes 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
alignandwrap. 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
AttrMaptakesfocus_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 formAttrMaptakes.
- 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
Noneoutside 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;
TableRowgives the order in which it and the row maps apply. ATableLayoutvalidates 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.
- 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
TableRowfor 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_positionis its index andfocusits 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, seeCommandMap.With
bar_selectable=Truethe 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_positionstays the selected tab while the bar has the keyboard focus, soget_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
underlineset, 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 takesattr_map, and its part along the selected labelselected_map. A click on the line acts as a click on the label row next to it, an overflow marker included.Sends
"focus changed"afterfocus_positionchanged, also through a change ofcontents. Its handlers receive the container, the old and the new index, which isNonewhile 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 comparesfocusbefore 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
AttrMaptakes.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);Nonedraws 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);Nonedraws 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 notNoneor a pair of one-line strings, or underline is notNoneor 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.TOPorVAlign.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
Nonewhen 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.
Noneapplies 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
contentsentry.A widget is returned as given, markup as a
Textthat 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.
Nonedraws none, and an empty string draws none on that side.- Raises:
ValueError – the new value is not
Noneor 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, orNone.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.
Nonedraws no line row.- Raises:
ValueError – the new value is not
Noneor 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_andGUILLEMET_members are overflow markers, theLINE_members underline glyphs. Only theASCIImembers 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
noneeds 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 themenucommand (esc by default) unhandled. Its handlers receive the overlay. A handler returningTrueconsumes the key. Otherwisekeypress()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 returningTrueconsumes the press. Otherwisemouse_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 aLineBoxaround aTable. It has no effect for other heights or for a'pack'width.
Overlay widgets behave similarly to
PaddingandFillerwidgets 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
menucommand key the top widget returns is sent as"menu".- Returns:
Nonewhen 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
Nonefor no minimum.
- min_width¶
Minimum width of the top widget for a relative width, or
Nonefor 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()andtop_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. Withshrink_to_fitit 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
Overlayover the one below.open()puts a widget on top andclose()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 aLineBoxor anAttrMapbefore opening it.The base is a box widget, usually the application’s
Frame, and the stack is meant to be theMainLoop.widget.Sends
"closed"once per closed dialog, after the dialog is removed and after itson_closecallback. 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:
base – box widget shown under every dialog, see
base.close_on_menu – default of
open()’s close_on_menu, seeclose_on_menu.close_on_click_outside – default of
open()’s close_on_click_outside, seeclose_on_click_outside.
- 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, itson_closeis 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_closeand to the"closed"handlers of the dialog closed last.widget – the dialog to close, as passed to
open().Nonecloses the top dialog.
- Returns:
whether a dialog was closed. It is
Falsewhen 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.
Whether a dialog opened from now on closes on a key for the
menucommand (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 ofNoneis chosen fromwidget.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, orNoneto choose it from the sizing.valign – vertical alignment, as for
Overlay.height – height, as for
Overlay, orNoneto 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 forOverlay.close_on_menu – close this dialog on a key for the
menucommand it leaves unhandled.Noneusesclose_on_menu.close_on_click_outside – close this dialog on a press of mouse button 1, 2 or 3 outside it.
Noneusesclose_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 assigningget_dataon an instance, is supported for backwards compatibility only. Callset_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 withfgabovebg.(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 satisfies0 < position < topand gives the y position of the label, and the markup is the text markup for that labeltop – 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 satisfies0 < position < topand gives the y position of the label, and the markup is the text markup for that labeltop – 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
Nonethen 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 everyintervalseconds. The frame is chosen from the time elapsed sincestart(), so a late alarm skips frames instead of slowing the spinner down.stop()removes the alarm and shows theidletext, 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()andstop()replace the text, so put a label in its ownTextnext to the spinner, for example in aColumns. 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 theMainLooprunning whenstart()is called (seeget_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.Framesmember 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.
Noneuses the loop running at eachstart().align – alignment of the text.
wrap – wrap mode of the text.
- Raises:
TypeError – frames is not a
Spinner.Framesmember 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
Nonefor 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
Noneto use the loop running at eachstart().
- 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
loopand no loop is running. Passloop=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.
- class urwid.widget.spinner.SpinnerFrames(*values)¶
Common frame sets for
Spinner, also reachable asSpinner.Frames.The frames of each member have one width. On a terminal without UTF-8:
LINEandDOTSare 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
LINEorDOTSthere 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.
commandis 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.envcan be used to pass custom environment variables. If omitted, os.environ is used.main_loopis the loop to watch the pty on: an event loop or aMainLoop, whose remove_watch_file must accept the handle its watch_file returned. Left out, the terminal watches the pty on the startedMainLoopit 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 byterminate().escape_sequenceis the urwid key symbol which should be used to break out of the terminal widget. If it’s not specified,ctrl ais used.encodingspecifies 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
utf8withurwid.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 startedMainLooprunning 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:
Nonewhen 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 thelinuxterminal 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:
Truewhen 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.Falsewhen 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
commandand 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, unlessdelegate_selectableis 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:
Nonedraws it only when widget needs scrolling,Truealways, so the wrapped widget always gets the same width, andFalsenever, 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
ListBoxby moving its focus to the widget at the top of the new view. The focus then changes without passing through the ListBoxkeypressormouse_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
ListBoxcreated withfocus_when_empty=Falseis 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
Nonefor 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;
Noneuses attr.thumb_attr – display attribute of the thumb;
Noneuses attr.thumb_focus_attr – display attribute of the thumb when rendered in focus;
Noneuses 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;
Nonedraws 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, throughSupportsRelativeScroll, 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:
Nonewhen the wrapped widget has noget_cursor_coordsor 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
visibleTruethe scrollbar always takes its columns, so the wrapped widget gets the rest.- Raises:
AttributeError – the wrapped widget has no
rows(), as aScrollablehas none.
- property scroll_by_focus: bool¶
Whether a click or drag on the scrollbar scrolls a
ListBoxby 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, unlessdelegate_selectableis set: then what the wrapped widget returns.
- sizing() frozenset[Sizing]¶
Return BOX, and FLOW as well for a wrapped
ListBoxwithListBox.flowset.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:
Nonewhen scrolling is needed,Truealways,Falsenever.
- 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
?:ASCII:
ARROW_ASCII_UPandARROW_ASCII_DOWN.DEC special graphics:
MEDIUM_SHADE,DRAWING_LIGHT,BLACK_VERTICAL_RECTANGLEandMIDDLE_DOT.
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
BULLETyes
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_UPU+02C4
no
no
no
no
ARROW_MODIFIER_DOWNU+02C5
no
no
no
no
A member marked
noneeds 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 = '▯'¶