[Urwid] feature-containers merge

Ian Ward ian at excess.org
Wed Mar 7 16:51:52 EST 2012

Hello everyone!

I'm happy to announce the containers changes I've been working on for a few
months have now landed in the default/master branch.  This is a long post
but there's lots of good stuff here.  If you see a problem with the API or
its implementation now's the time to let me know!


 1. New Container API
 2. Container and Decoration Widget Improvements
 3. List Walker API V2
 4. New Simple List Walker and Monitored List Types


Urwid's container widgets:

 * Columns
 * Pile
 * GridFlow
 * Overlay
 * Frame
 * ListBox

All now have a common container API you can use, regardless of the
container type.  Backwards compatibility is still maintained for the old
container-specific ways of accessing and modifying contents, but the new
API is now the preferred way of modifying and traversing containers.


is a read-only property that returns the widget in focus for this
container.  Empty containers and non-container widgets (that inherit from
Widget) will return None.


is a read/write property that provides access to the position of the
container's widget in focus.  This will often be a integer value but may be
any object*.  Reading this value on an empty container or on any
non-container widgets (that inherit from Widget) raises an IndexError.
 Writing to this property with an invalid position will also raise an
IndexError.  Writing a new value automatically marks this widget to be
redrawn and will be reflected in cont.focus.

* Columns, Pile, GridFlow, Overlay and ListBox with a SimpleListWalker or
SimpleFocusListWalker as its body use integer positions;  Frame uses
'body', 'header' and 'footer';  ListBox with a custom list walker will use
the positions the list walker returns


is a read-only property** that provides access to an mapping- or list-like
object that contains the child widgets and the options used for displaying
those widgets in this container.  The mapping- or list-like object always
allows reading from positions with the usual __getitem__ method and may
support assignment and deletion*** with __setitem__ and __delitem__
methods.  The values are (child widget, option) tuples.  When this object
or its contents are modified the widget is automatically flagged to be

** Columns, Pile and GridFlow also allow assigning an iterable to this
property and overwrite the values in their contents list with the ones

*** Columns, Pile, GridFlow, Overlay and Frame support item assignment and


is a method that returns options objects for use in items added to
cont.contents.  The arguments are specific to the container type, and
generally match the __init__ arguments for the container.  The objects
returned are currently tuples of strings and integers or None for
containers without child widget options.  This method exists to allow
future versions of Urwid to add new options to existing containers.  Code
that expects the option tuples to remain the same size will fail when new
options are added, so defensive programming with options tuples is strongly

  cont.__getitem__(x)  # <=>  cont[x]

is a short-cut method behaving identically to:
cont.contents[x][0].base_widget.  Which means roughly "give me the child
widget at position x and skip all the Decoration widgets wrapping it".
 Decoration widgets include Padding, Filler, AttrMap etc.


is a method that returns the focus position for this container *and* all
child containers along the path defined by their focus settings.  This list
of positions is the closest thing we have to the singular widget-in-focus
in other UI frameworks, because the ultimate widget in focus in Urwid
depends on the focus setting of all its parent container widgets.


is a method that assigns to the focus_position property of each container
along the path given by the list of positions p.  It may be used to restore
focus to a widget as returned by a previous call to cont.get_focus_path().

  cont.__iter__()  and cont.__reversed__()

are methods that allow iteration over the *positions* of this container.
 Normally the order of the positions generated by __reversed__() will be
the opposite of __iter__().  The exception is the case of ListBox with
certain custom list walkers, and the reason goes back to the original way
list walker interface was defined.  Note that a custom list walker might
also generate an unbounded number of positions, so care should be used with
this interface and ListBoxes.


I made a number of other improvements to the container and decoration

 * GridFlow child widgets may now be given different widths, and more types
of widths will likely be added in the future to make it a much more
flexible container

 * GridFlow, Columns, Overlay and Padding now consistently use the names
width_type and width_amount

 * width_types formerly called 'fixed' for a set number of screen columns
are now called 'given' to avoid confusion with fixed widgets, 'fixed' is
still accepted for backwards compatibility

 * width_types formerly called 'flow' for asking a widget to calculate its
own number of screen columns is now called 'pack' to avoid confusion with
flow widgets, 'flow' is still accepted for backwards compatibility

 * Pile, Overlay and Filler now consistently use the names height_type and

 * height_types formerly called 'flow' (or None) for asking a widget to
calculate its own number of rows are now called 'pack' to be consistent
with width_type, 'flow' (and None) is still accepted for backwards

 * Filler now has top and bottom parameters like Padding's left and right

 * Overlay now has min_height, min_width, left, right, top and bottom
parameters which behave like the same options on Padding and Filler

 * Frame now has some better docstrings and a comparison to a similar use
of Pile

 * Updated tour.py example to use new container/decoration parameters

 * FlowWidget, BoxWidget and FixedWidget are now deprecated, sizing is
given by a sizing() method or from the _sizing property/attribute


The current list walker API ("V1") will remain available and is still the
least restrictive option for the programmer.  The list walker API V2 is an
attempt to remove some of the duplicate code that V1 requires for many
users.  List walker API V1 will be implemented automatically by subclassing
ListWalker and implementing the V2 methods:

 * walker.__getitem__(p)  # return widget at position p or raise an
IndexError or KeyError

 * walker.next_position(p)  # return position following position p or raise
an IndexError or KeyError

 * walker.prev_position(p)  # return position preceding position p or raise
an IndexError or KeyError

 * walker.set_focus(p)  # same as V1, may call self._modified()

 * walker.focus  # attribute or property containing the focus position, or
define walker.get_focus() as in V1

Also, there is an optional iteration helper method that may be defined in
any list walker.  When this is defined it will be used by
ListBox.__iter__() and ListBox.__reversed__():

 * walker.positions(reverse=False) # return a forward or reverse iterable
of positions


For some time there has been an unpublicised monitored list type in Urwid:
MonitoredFocusList.  This class is a list with a .focus position attribute
that is automatically updated as the contents of the list changes.  i.e. If
you remove or insert items before the focus position using any of the
normal list methods the focus position is updated accordingly.  This class
is now used by a number of the container widgets to manage their contents
lists, and is the type of object that is returned when you access their
.contents property.

SimpleListWalker has for a long time been the recommended list walker for
the common case of using a normal list of widgets in a ListBox.
 SimpleListWalker uses the MonitoredList type to track its contents and
focus, and that can't be changed without possibly breaking existing code.

So, the new recommended simple list walker is SimpleFocusListWalker.
 SimpleFocusListWalker uses MonitoredFocusList and gets all its nice
focus-position-updating goodness.

I know.  Sorry about the long name.  I couldn't think of something better,
but suggestions are welcome.

That's it.  Hey, thanks for reading this far and happy Urwidding!

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.excess.org/pipermail/urwid/attachments/20120307/585ae9dc/attachment.html 

More information about the Urwid mailing list