Ian Ward | 7 Mar 2012 22:51
Favicon
Gravatar

feature-containers merge

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!



CONTENTS

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


1. NEW CONTAINER API

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.


  cont.focus

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.


  cont.focus_position

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


  cont.contents

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 redrawn.

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

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


  cont.options(...)

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 encouraged.


  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.


  cont.get_focus_path()

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.


  cont.set_focus_path(p)

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.


2. CONTAINER AND DECORATION WIDGET IMPROVEMENTS

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

 * 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_amount

 * 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 compatibility

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

 * 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


3. LIST WALKER API V2

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


4. NEW SIMPLE LIST WALKER AND MONITORED LIST TYPES

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!

Ian
_______________________________________________
Urwid mailing list
Urwid <at> lists.excess.org
http://lists.excess.org/mailman/listinfo/urwid

Gmane