Usage
Basic XML/CSS Rendering with Layout and LayoutManager
The LayoutManager class is used to manager layouts, custom widgets, and styles/palettes:
manager = LayoutManager()
Layouts with XML and CSS are created with the Layout class:
layout = Layout(
Path("path/to/layout.xml"), # paths must be pathlib.Path
Path("path/to/stylesheet.css"),
CustomResources, # pass custom resources by class type (this can also be None)
)
XML:
<pile xmlns:mu="https://github.com/Jackkillian/modern-urwid" id="root">
<filler mu:height="1">
<text id="header_text" class="custom">Hello, world</text>
</filler>
<filler mu:height="1">
<edit caption="Edit: ">
<mu:signal name="change" callback="@on_edit_change" />
<mu:signal name="postchange" callback="@on_edit_postchange" />
</edit>
</filler>
<filler mu:height="1"><button
on_press="@quit_callback"
>Quit</button></filler>
<solidfill mu:height="3">.</solidfill>
<filler mu:height="2"><divider /></filler>
<filler mu:height="1">
<progressbar
normal="pb_empty"
complete="pb_full"
current="57"
/>
</filler>
<filler valign="top" class="dark">
<text>This should be dark</text>
</filler>
<padding mu:height="2" left="5" right="2">
<filler>
<text id="padded">Left is padded by 5; right is be 2</text>
</filler>
</padding>
<customwidget />
<customwidgetfromxml mu:height="1" />
<scrollbar>
<listbox id="dynamic" />
</scrollbar>
</pile>
CSS:
:root {
--default-color: dark green;
--my-var: light red;
}
edit {
color: var(--default-color);
}
#root {
color: black;
background: var(--my-var);
}
.custom {
color: light green;
background: dark gray;
}
.dark {
color: black;
background: white;
}
button {
color: yellow;
}
button:focus {
color: light red;
}
scrollbar {
color: light blue;
}
scrollbar:focus {
color: black;
}
Layouts use the LayoutResourceHandler class to access custom widgets, palettes, CSS variables, and widget callbacks. The LayoutResourceHandler class can also be used to dynamically create additional widgets within the layout in the on_load() method, or handle the layout’s on_enter() and on_exit() methods.
class CustomResources(LayoutResourceHandler):
def __init__(self, layout):
super().__init__(
layout,
palettes=[
("pb_empty", "white", "black"),
("pb_full", "black", "light blue"),
],
css_variables={"--my-var": "light gray"}, # override any variables in the stylesheet's ':root' declaration
)
def quit_callback(self, w):
raise urwid.ExitMainLoop()
def on_edit_change(self, w: urwid.Edit, full_text):
w.set_caption(f"Edit ({full_text}): ")
def on_edit_postchange(self, w, text):
widget = self.layout.get_widget_by_id("header_text")
if isinstance(widget, urwid.Text):
widget.set_text(text)
def on_load(self):
# get the widget with id="dynamic" from the XML
widget = self.layout.get_widget_by_id("dynamic")
# dynamically add 10 buttons to the listbox
if isinstance(widget, urwid.ListBox):
widget.body.extend(
[
self.layout.style_widget( # use self.layout.style_widget() to apply the '#root' style
urwid.Button(f"Dynamic Button {i}"), id="root"
)
for i in range(10)
]
)
def on_enter(self):
pass
def on_exit(self):
pass
Layouts can then be registered with the manager with register(). Before the MainLoop can be run, a layout must be activated with switch().
manager.register("my_layout", layout)
manager.switch("my_layout") # switch to the layout named "my_layout"
manager.run() # call urwid.MainLoop.run
Rendering custom widgets
Custom widgets can be made with the @manager.register_widget() decorator:
@manager.register_widget()
class CustomWidget(WidgetBuilder):
def build(self, **kwargs):
return urwid.Filler(urwid.Text("This is a custom widget!"))
Custom widgets can also be created from XML with the self.render_from_xml() method:
@manager.register_widget()
class CustomWidgetFromXML(WidgetBuilder):
def build(self, **kwargs):
parser = self.render_from_xml(
Path("path/to/my_custom_widget.xml",
css_path=Path("path/to/my_custom_widget.css"),
)
# don't forget to register any custom palettes that
# may be parsed from the widget's stylesheet:
manager.register_palette(parser.get_palettes())
return parser.get_root()
Note
Custom widgets must be registered before layouts are registered.