urwid 2.6.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of urwid might be problematic. Click here for more details.
- urwid/__init__.py +333 -0
- urwid/canvas.py +1413 -0
- urwid/command_map.py +137 -0
- urwid/container.py +59 -0
- urwid/decoration.py +65 -0
- urwid/display/__init__.py +97 -0
- urwid/display/_posix_raw_display.py +413 -0
- urwid/display/_raw_display_base.py +914 -0
- urwid/display/_web.css +12 -0
- urwid/display/_web.js +462 -0
- urwid/display/_win32.py +171 -0
- urwid/display/_win32_raw_display.py +269 -0
- urwid/display/common.py +1219 -0
- urwid/display/curses.py +690 -0
- urwid/display/escape.py +624 -0
- urwid/display/html_fragment.py +251 -0
- urwid/display/lcd.py +518 -0
- urwid/display/raw.py +37 -0
- urwid/display/web.py +636 -0
- urwid/event_loop/__init__.py +55 -0
- urwid/event_loop/abstract_loop.py +175 -0
- urwid/event_loop/asyncio_loop.py +231 -0
- urwid/event_loop/glib_loop.py +294 -0
- urwid/event_loop/main_loop.py +721 -0
- urwid/event_loop/select_loop.py +230 -0
- urwid/event_loop/tornado_loop.py +206 -0
- urwid/event_loop/trio_loop.py +302 -0
- urwid/event_loop/twisted_loop.py +269 -0
- urwid/event_loop/zmq_loop.py +275 -0
- urwid/font.py +695 -0
- urwid/graphics.py +96 -0
- urwid/highlight.css +19 -0
- urwid/listbox.py +1899 -0
- urwid/monitored_list.py +522 -0
- urwid/numedit.py +376 -0
- urwid/signals.py +330 -0
- urwid/split_repr.py +130 -0
- urwid/str_util.py +358 -0
- urwid/text_layout.py +632 -0
- urwid/treetools.py +515 -0
- urwid/util.py +557 -0
- urwid/version.py +16 -0
- urwid/vterm.py +1806 -0
- urwid/widget/__init__.py +181 -0
- urwid/widget/attr_map.py +161 -0
- urwid/widget/attr_wrap.py +140 -0
- urwid/widget/bar_graph.py +649 -0
- urwid/widget/big_text.py +77 -0
- urwid/widget/box_adapter.py +126 -0
- urwid/widget/columns.py +1145 -0
- urwid/widget/constants.py +574 -0
- urwid/widget/container.py +227 -0
- urwid/widget/divider.py +110 -0
- urwid/widget/edit.py +718 -0
- urwid/widget/filler.py +403 -0
- urwid/widget/frame.py +539 -0
- urwid/widget/grid_flow.py +539 -0
- urwid/widget/line_box.py +194 -0
- urwid/widget/overlay.py +829 -0
- urwid/widget/padding.py +597 -0
- urwid/widget/pile.py +971 -0
- urwid/widget/popup.py +170 -0
- urwid/widget/progress_bar.py +141 -0
- urwid/widget/scrollable.py +597 -0
- urwid/widget/solid_fill.py +44 -0
- urwid/widget/text.py +354 -0
- urwid/widget/widget.py +852 -0
- urwid/widget/widget_decoration.py +166 -0
- urwid/widget/wimp.py +792 -0
- urwid/wimp.py +23 -0
- urwid-2.6.0.post0.dist-info/COPYING +504 -0
- urwid-2.6.0.post0.dist-info/METADATA +332 -0
- urwid-2.6.0.post0.dist-info/RECORD +75 -0
- urwid-2.6.0.post0.dist-info/WHEEL +5 -0
- urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import enum
|
|
5
|
+
import typing
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
from .constants import Sizing, WHSettings
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Iterable, Iterator
|
|
12
|
+
|
|
13
|
+
from .widget import Widget
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _ContainerElementSizingFlag(enum.IntFlag):
|
|
17
|
+
NONE = 0
|
|
18
|
+
BOX = enum.auto()
|
|
19
|
+
FLOW = enum.auto()
|
|
20
|
+
FIXED = enum.auto()
|
|
21
|
+
WH_WEIGHT = enum.auto()
|
|
22
|
+
WH_PACK = enum.auto()
|
|
23
|
+
WH_GIVEN = enum.auto()
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def reverse_flag(self) -> tuple[frozenset(Sizing), WHSettings | None]:
|
|
27
|
+
"""Get flag in public API format."""
|
|
28
|
+
sizing: set[Sizing] = set()
|
|
29
|
+
|
|
30
|
+
if self & self.BOX:
|
|
31
|
+
sizing.add(Sizing.BOX)
|
|
32
|
+
if self & self.FLOW:
|
|
33
|
+
sizing.add(Sizing.FLOW)
|
|
34
|
+
if self & self.FIXED:
|
|
35
|
+
sizing.add(Sizing.FIXED)
|
|
36
|
+
|
|
37
|
+
if self & self.WH_WEIGHT:
|
|
38
|
+
return frozenset(sizing), WHSettings.WEIGHT
|
|
39
|
+
if self & self.WH_PACK:
|
|
40
|
+
return frozenset(sizing), WHSettings.PACK
|
|
41
|
+
if self & self.WH_GIVEN:
|
|
42
|
+
return frozenset(sizing), WHSettings.GIVEN
|
|
43
|
+
return frozenset(sizing), None
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def log_string(self) -> str:
|
|
47
|
+
"""Get desctiprion in public API format."""
|
|
48
|
+
sizing, render = self.reverse_flag
|
|
49
|
+
render_string = f" {render.upper()}" if render else ""
|
|
50
|
+
return "|".join(sorted(mode.upper() for mode in sizing)) + render_string
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class WidgetContainerMixin:
|
|
54
|
+
"""
|
|
55
|
+
Mixin class for widget containers implementing common container methods
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __getitem__(self, position) -> Widget:
|
|
59
|
+
"""
|
|
60
|
+
Container short-cut for self.contents[position][0].base_widget
|
|
61
|
+
which means "give me the child widget at position without any
|
|
62
|
+
widget decorations".
|
|
63
|
+
|
|
64
|
+
This allows for concise traversal of nested container widgets
|
|
65
|
+
such as:
|
|
66
|
+
|
|
67
|
+
my_widget[position0][position1][position2] ...
|
|
68
|
+
"""
|
|
69
|
+
return self.contents[position][0].base_widget
|
|
70
|
+
|
|
71
|
+
def get_focus_path(self) -> list[int | str]:
|
|
72
|
+
"""
|
|
73
|
+
Return the .focus_position values starting from this container
|
|
74
|
+
and proceeding along each child widget until reaching a leaf
|
|
75
|
+
(non-container) widget.
|
|
76
|
+
"""
|
|
77
|
+
out = []
|
|
78
|
+
w = self
|
|
79
|
+
while True:
|
|
80
|
+
try:
|
|
81
|
+
p = w.focus_position
|
|
82
|
+
except IndexError:
|
|
83
|
+
return out
|
|
84
|
+
out.append(p)
|
|
85
|
+
w = w.focus.base_widget
|
|
86
|
+
|
|
87
|
+
def set_focus_path(self, positions: Iterable[int | str]) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Set the .focus_position property starting from this container
|
|
90
|
+
widget and proceeding along newly focused child widgets. Any
|
|
91
|
+
failed assignment due do incompatible position types or invalid
|
|
92
|
+
positions will raise an IndexError.
|
|
93
|
+
|
|
94
|
+
This method may be used to restore a particular widget to the
|
|
95
|
+
focus by passing in the value returned from an earlier call to
|
|
96
|
+
get_focus_path().
|
|
97
|
+
|
|
98
|
+
positions -- sequence of positions
|
|
99
|
+
"""
|
|
100
|
+
w = self
|
|
101
|
+
for p in positions:
|
|
102
|
+
if p != w.focus_position:
|
|
103
|
+
w.focus_position = p # modifies w.focus
|
|
104
|
+
w = w.focus.base_widget
|
|
105
|
+
|
|
106
|
+
def get_focus_widgets(self) -> list[Widget]:
|
|
107
|
+
"""
|
|
108
|
+
Return the .focus values starting from this container
|
|
109
|
+
and proceeding along each child widget until reaching a leaf
|
|
110
|
+
(non-container) widget.
|
|
111
|
+
|
|
112
|
+
Note that the list does not contain the topmost container widget
|
|
113
|
+
(i.e., on which this method is called), but does include the
|
|
114
|
+
lowest leaf widget.
|
|
115
|
+
"""
|
|
116
|
+
out = []
|
|
117
|
+
w = self
|
|
118
|
+
while True:
|
|
119
|
+
w = w.base_widget.focus
|
|
120
|
+
if w is None:
|
|
121
|
+
return out
|
|
122
|
+
out.append(w)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
@abc.abstractmethod
|
|
126
|
+
def focus(self) -> Widget:
|
|
127
|
+
"""
|
|
128
|
+
Read-only property returning the child widget in focus for
|
|
129
|
+
container widgets. This default implementation
|
|
130
|
+
always returns ``None``, indicating that this widget has no children.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def _get_focus(self) -> Widget:
|
|
134
|
+
warnings.warn(
|
|
135
|
+
f"method `{self.__class__.__name__}._get_focus` is deprecated, "
|
|
136
|
+
f"please use `{self.__class__.__name__}.focus` property",
|
|
137
|
+
DeprecationWarning,
|
|
138
|
+
stacklevel=3,
|
|
139
|
+
)
|
|
140
|
+
return self.focus
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class WidgetContainerListContentsMixin:
|
|
144
|
+
"""
|
|
145
|
+
Mixin class for widget containers whose positions are indexes into
|
|
146
|
+
a list available as self.contents.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __iter__(self) -> Iterator[int]:
|
|
150
|
+
"""
|
|
151
|
+
Return an iterable of positions for this container from first
|
|
152
|
+
to last.
|
|
153
|
+
"""
|
|
154
|
+
return iter(range(len(self.contents)))
|
|
155
|
+
|
|
156
|
+
def __reversed__(self) -> Iterator[int]:
|
|
157
|
+
"""
|
|
158
|
+
Return an iterable of positions for this container from last
|
|
159
|
+
to first.
|
|
160
|
+
"""
|
|
161
|
+
return iter(range(len(self.contents) - 1, -1, -1))
|
|
162
|
+
|
|
163
|
+
def __len__(self) -> int:
|
|
164
|
+
return len(self.contents)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
@abc.abstractmethod
|
|
168
|
+
def contents(self) -> list[tuple[Widget, typing.Any]]:
|
|
169
|
+
"""The contents of container as a list of (widget, options)"""
|
|
170
|
+
|
|
171
|
+
@contents.setter
|
|
172
|
+
def contents(self, new_contents: list[tuple[Widget, typing.Any]]) -> None:
|
|
173
|
+
"""The contents of container as a list of (widget, options)"""
|
|
174
|
+
|
|
175
|
+
def _get_contents(self) -> list[tuple[Widget, typing.Any]]:
|
|
176
|
+
warnings.warn(
|
|
177
|
+
f"method `{self.__class__.__name__}._get_contents` is deprecated, "
|
|
178
|
+
f"please use `{self.__class__.__name__}.contents` property",
|
|
179
|
+
DeprecationWarning,
|
|
180
|
+
stacklevel=2,
|
|
181
|
+
)
|
|
182
|
+
return self.contents
|
|
183
|
+
|
|
184
|
+
def _set_contents(self, c: list[tuple[Widget, typing.Any]]) -> None:
|
|
185
|
+
warnings.warn(
|
|
186
|
+
f"method `{self.__class__.__name__}._set_contents` is deprecated, "
|
|
187
|
+
f"please use `{self.__class__.__name__}.contents` property",
|
|
188
|
+
DeprecationWarning,
|
|
189
|
+
stacklevel=2,
|
|
190
|
+
)
|
|
191
|
+
self.contents = c
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
@abc.abstractmethod
|
|
195
|
+
def focus_position(self) -> int | None:
|
|
196
|
+
"""
|
|
197
|
+
index of child widget in focus.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
@focus_position.setter
|
|
201
|
+
def focus_position(self, position: int) -> None:
|
|
202
|
+
"""
|
|
203
|
+
index of child widget in focus.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
def _get_focus_position(self) -> int | None:
|
|
207
|
+
warnings.warn(
|
|
208
|
+
f"method `{self.__class__.__name__}._get_focus_position` is deprecated, "
|
|
209
|
+
f"please use `{self.__class__.__name__}.focus_position` property",
|
|
210
|
+
DeprecationWarning,
|
|
211
|
+
stacklevel=3,
|
|
212
|
+
)
|
|
213
|
+
return self.focus_position
|
|
214
|
+
|
|
215
|
+
def _set_focus_position(self, position: int) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Set the widget in focus.
|
|
218
|
+
|
|
219
|
+
position -- index of child widget to be made focus
|
|
220
|
+
"""
|
|
221
|
+
warnings.warn(
|
|
222
|
+
f"method `{self.__class__.__name__}._set_focus_position` is deprecated, "
|
|
223
|
+
f"please use `{self.__class__.__name__}.focus_position` property",
|
|
224
|
+
DeprecationWarning,
|
|
225
|
+
stacklevel=3,
|
|
226
|
+
)
|
|
227
|
+
self.focus_position = position
|
urwid/widget/divider.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from urwid.canvas import CompositeCanvas, SolidCanvas
|
|
7
|
+
|
|
8
|
+
from .constants import BOX_SYMBOLS, SHADE_SYMBOLS, Sizing
|
|
9
|
+
from .widget import Widget
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DividerSymbols(str, enum.Enum):
|
|
13
|
+
"""Common symbols for divider widgets."""
|
|
14
|
+
|
|
15
|
+
# Lines
|
|
16
|
+
LIGHT_HL = BOX_SYMBOLS.LIGHT.HORIZONTAL
|
|
17
|
+
LIGHT_4_DASHES = BOX_SYMBOLS.LIGHT.HORIZONTAL_4_DASHES
|
|
18
|
+
LIGHT_3_DASHES = BOX_SYMBOLS.LIGHT.HORIZONTAL_3_DASHES
|
|
19
|
+
LIGHT_2_DASHES = BOX_SYMBOLS.LIGHT.HORIZONTAL_2_DASHES
|
|
20
|
+
HEAVY_HL = BOX_SYMBOLS.HEAVY.HORIZONTAL
|
|
21
|
+
HEAVY_4_DASHES = BOX_SYMBOLS.HEAVY.HORIZONTAL_4_DASHES
|
|
22
|
+
HEAVY_3_DASHES = BOX_SYMBOLS.HEAVY.HORIZONTAL_3_DASHES
|
|
23
|
+
HEAVY_2_DASHES = BOX_SYMBOLS.HEAVY.HORIZONTAL_2_DASHES
|
|
24
|
+
DOUBLE_HL = BOX_SYMBOLS.DOUBLE.HORIZONTAL
|
|
25
|
+
|
|
26
|
+
# Full block
|
|
27
|
+
FULL_BLOCK = SHADE_SYMBOLS.FULL_BLOCK
|
|
28
|
+
DARK_SHADE = SHADE_SYMBOLS.DARK_SHADE
|
|
29
|
+
MEDIUM_SHADE = SHADE_SYMBOLS.MEDIUM_SHADE
|
|
30
|
+
LITE_SHADE = SHADE_SYMBOLS.LITE_SHADE
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Divider(Widget):
|
|
34
|
+
"""
|
|
35
|
+
Horizontal divider widget
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
Symbols = DividerSymbols
|
|
39
|
+
|
|
40
|
+
_sizing = frozenset([Sizing.FLOW])
|
|
41
|
+
|
|
42
|
+
ignore_focus = True
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
div_char: str | bytes = " ",
|
|
47
|
+
top: int = 0,
|
|
48
|
+
bottom: int = 0,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""
|
|
51
|
+
:param div_char: character to repeat across line
|
|
52
|
+
:type div_char: bytes or unicode
|
|
53
|
+
|
|
54
|
+
:param top: number of blank lines above
|
|
55
|
+
:type top: int
|
|
56
|
+
|
|
57
|
+
:param bottom: number of blank lines below
|
|
58
|
+
:type bottom: int
|
|
59
|
+
|
|
60
|
+
>>> Divider()
|
|
61
|
+
<Divider flow widget>
|
|
62
|
+
>>> Divider(u'-')
|
|
63
|
+
<Divider flow widget '-'>
|
|
64
|
+
>>> Divider(u'x', 1, 2)
|
|
65
|
+
<Divider flow widget 'x' bottom=2 top=1>
|
|
66
|
+
"""
|
|
67
|
+
super().__init__()
|
|
68
|
+
self.div_char = div_char
|
|
69
|
+
self.top = top
|
|
70
|
+
self.bottom = bottom
|
|
71
|
+
|
|
72
|
+
def _repr_words(self) -> list[str]:
|
|
73
|
+
return super()._repr_words() + [repr(self.div_char)] * (self.div_char != " ")
|
|
74
|
+
|
|
75
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
76
|
+
attrs = dict(super()._repr_attrs())
|
|
77
|
+
if self.top:
|
|
78
|
+
attrs["top"] = self.top
|
|
79
|
+
if self.bottom:
|
|
80
|
+
attrs["bottom"] = self.bottom
|
|
81
|
+
return attrs
|
|
82
|
+
|
|
83
|
+
def rows(self, size: tuple[int], focus: bool = False) -> int:
|
|
84
|
+
"""
|
|
85
|
+
Return the number of lines that will be rendered.
|
|
86
|
+
|
|
87
|
+
>>> Divider().rows((10,))
|
|
88
|
+
1
|
|
89
|
+
>>> Divider(u'x', 1, 2).rows((10,))
|
|
90
|
+
4
|
|
91
|
+
"""
|
|
92
|
+
(_maxcol,) = size
|
|
93
|
+
return self.top + 1 + self.bottom
|
|
94
|
+
|
|
95
|
+
def render(self, size: tuple[int], focus: bool = False) -> CompositeCanvas:
|
|
96
|
+
"""
|
|
97
|
+
Render the divider as a canvas and return it.
|
|
98
|
+
|
|
99
|
+
>>> Divider().render((10,)).text # ... = b in Python 3
|
|
100
|
+
[...' ']
|
|
101
|
+
>>> Divider(u'-', top=1).render((10,)).text
|
|
102
|
+
[...' ', ...'----------']
|
|
103
|
+
>>> Divider(u'x', bottom=2).render((5,)).text
|
|
104
|
+
[...'xxxxx', ...' ', ...' ']
|
|
105
|
+
"""
|
|
106
|
+
(maxcol,) = size
|
|
107
|
+
canv = CompositeCanvas(SolidCanvas(self.div_char, maxcol, 1))
|
|
108
|
+
if self.top or self.bottom:
|
|
109
|
+
canv.pad_trim_top_bottom(self.top, self.bottom)
|
|
110
|
+
return canv
|