[Urwid] Development update: performance, incompatibilities

Ian Ward ian at excess.org
Fri Feb 23 15:11:08 EST 2007


The latest version of Urwid in subversion now incorporates a new caching 
mechanism and a compiled C version of some string utilities from Rebecca 
Breu.

The performance has really improved since the last stable release 
(0.9.7.2).  Here are the results I get when comparing the two versions 
running the benchmark programs in contrib:

: reduction in running time / speed improvement
bench_1.py (tour):      41% /  70%
bench_2.py (bigtext):   82% / 450%
bench_3.py (calc):      56% / 125%
bench_4.py (graph):     73% / 268%

Some further improvements are still possible, but the latest version is 
very close to what will be released as 0.9.8.


Now to the changes.

All the standard widgets in Urwid now cache the canvas they produce from 
their render() functions.  The caching is done using a new CanvasCache 
class.  The CanvasCache will hold on to a canvas as long as there is 
still a reference to it.  The Screen objects in raw_display and 
curses_display hold on to a reference to the last screen rendered, 
allowing the cache to work without modifying your applications.

However, caching canvases requires that we know when a widget has 
changed, so there are a couple changes to Urwid's interface that I 
couldn't avoid.

Pile and Columns widgets need to know when their content changes, so 
while you used to be able to write code like:

   some_widgets = [widget_a, widget_b, widget_c]
   piled_widgets = urwid.Pile(some_widgets)
   # later add/remove items from some_widgets

Now you will have to write:

   piled_widgets = urwid.Pile([widget_a, widget_b, widget_c])
   some_widgets = piled_widgets.widget_list
   # later add/remove items from some_widgets

This change is backwards-compatible with earlier versions of Urwid. Now 
the widget_list member of Pile and Columns is a special list type that 
can detect changes to its contents.


The ListBox class delegates determining what widgets are displayed to a 
"list walker".  The default list walker class can now detect changes in 
the same way that Pile and Columns do.  If you are using code similar to:

   some_widgets = [widget_a, widget_b, widget_c]
   listbox = urwid.ListBox(some_widgets)
   # later add/remove items from some_widgets

Now you will have to write:

   listbox = urwid.ListBox([widget_a, widget_b, widget_c])
   some_widgets = urwid.ListBox.body
   # later add/remove items from some_widgets

This change is also backwards compatible.

Custom list walker classes now need to support sending a "modified" 
signal when their contents change.  A list walker class might have 
looked like:

   class CustomListWalker(object):
     # definitions of get_focus, set_focus, get_next, get_prev...

Now you will have to write:

   class CustomListWalker(urwid.ListWalker):
     # definitions of get_focus, set_focus, get_next, get_prev...
     # call self._modified() when the contents change.

Or, if you need to have backwards compatibility write:

   try: ListWalker = urwid.ListWalker
   except:
     class ListWalker(object):
       def _modified(self): pass

   class CustomListWalker(ListWalker):
     # definitions of get_focus, set_focus, get_next, get_prev...
     # call self._modified() when the contents change.


Please post to this list or join our IRC channel if you have any 
questions or comments.

Ian




More information about the Urwid mailing list