Canvas Cache

In an Urwid application each time the screen is redrawn typically only part of the screen actually needs to be updated. A canvas cache is used to store visible, unchanged canvases so that not all of the visible widgets need to be rendered for each update.

The Widget base class uses some metaclass magic to capture the canvas objects returned when Widget.render() is called and return them the next time Widget.render() is called again with the same parameters. The Widget._invalidate() method is provided as a way to remove cached widgets so that changes to the widget are visible the next time the screen is redrawn.

Similar metaclass magic is used for flow widgets’ Widget.rows() method. If a canvas for that widget with the same parameters is cached then the rows of that canvas are returned instead of calling the widget’s actual Widget.rows() method.

Composite Canvases

When container and decoration widgets are rendered, they collect the canvases returned by their children and arrange them into a composite canvas. Composite canvases are nested to form a tree with the topmost widget’s Widget.render() method returning the root of the tree. That canvas is sent to the display module to be rendered on the screen.

Composite canvases reference the content and layout from their children, reducing the number of copies required to build them. When a canvas is removed from the cache by a call to Widget._invalidate() all the direct parents of that canvas are removed from the cache as well, forcing those widgets to be re-drawn on the next screen update. This cascade-removal happens only once per update (the canvas is then no longer in the cache) so batched changes to visible widgets may be made efficiently. This is important when a user’s input gets ahead of the screen updating – Urwid handles all the pending input first then updates the screen with the final result, instead of falling further and further behind.

Cache Lifetime

The canvases “stored” in the canvas cache are actually weak references to the canvases. The canvases must have a real reference somewhere for the cache to function properly. Urwid’s display modules store the currently displayed topmost canvas for this reason. All canvases that are visible on the screen will remain in the cache, and others will be garbage collected.

Future Work

A updating method that invalidates regions of the display without redrawing parent widgets would be more efficient for the common case of a single change on the screen that does not affect the screen layout. Send an email to the mailing list if you’re interested in helping with this or other display optimizations.