Widget Methods
Widgets in Urwid are easiest to create by extending other widgets. If you are making a new type of widget that can use other widgets to display its content, like a new type of button or control, then you should start by extending WidgetWrap and passing the display widget to its constructor.
This section describes the Widget interface in detail and is useful if you're looking to modify the behavior of an existing widget, build a new widget class from scratch or just want a better understanding of the library.
One design choice that stands out is that widgets in Urwid typically have no size. Widgets don't store the size they will be displayed at, and instead are passed that information when they need it.
This choice has some advantages:
- widgets may be reused in different locations
- reused widgets only need to be rendered once per size displayed
- widgets don't need to know their parents
- less data to store and update
- no worrying about widgets that haven't received their size yet
- same widgets could be displayed at different sizes to different users simultaneously
It also has disadvantages:
- difficult to determine a widget's size on screen
- more parameters to parse
- duplicated size calculations across methods
For determining a widget's size on screen it is possible to look up the size(s) it was rendered at in the CanvasCache. There are plans to address some of the duplicated size handling code in the container widgets in a future Urwid release.
The same holds true for a widget's focus state, so that too is passed in to functions that need it.
The Widget base class has some metaclass magic that creates a __super attribute for calling your superclass: self.__super is the same as the usual super(MyClassName, self).
sizing(self)
Return a set of the sizing modes this widget supports, one or more of FLOW, BOX or FIXED.
The Widget base class defines this method as "return self._sizing", so if your widget's sizing modes never change you can define _sizing in your class definition, and not bother overriding this method.
If you inherit from FlowWidget, BoxWidget or FixedWidget, _sizing is already defined for you as set([FLOW]), set([BOX]) or set([FIXED]), respectively.
If FLOW is among the values returned then your other methods must be able to accept a single-element tuple (maxcol,) to their size parameters, and the rows() method must be defined. Flow widgets are given a certain column size by their parents and may take as many rows as they require to display their content.
If BOX is among the values returned then your other methods must be able to accept a two-element tuple (maxcol, maxrow) to their size paramters. Box widgets are given their size by their parents and must display their contents in the space given.
If FIXED is among the values returned then your other methods must be able to accept an empty tuple () to their size parameters, and the pack() method must be defined. Fixed widgets have a fixed number of rows and columns, and so are the least flexible and must me handled carefully.
render(self, size, focus=False)
Render the widget content and return it as a Canvas subclass. Text widgets return a TextCanvas (arbitrary text and attributes), SolidFill widgets return a SolidCanvas (a single character repeated across the whole surface) and container widgets return a CompositeCanvas (one or more other canvases arranged arbitrarily).
If focus is False, the returned canvas may not have a cursor position set.
There is some metaclass magic in the Widget base class that causes the result of this function to be cached by CanvasCache, and later calls will automatically look up the value in the cache first. The class variable ignore_focus may be defined and set to True if this widget renders the same regardless of the value of the focus parameter.
Any time the content of your widget changes you must call self._invalidate() to remove any cached canvases, or your widget may not change as you expect.
selectable(self)
Return True if this is a widget that is designed to take the focus, False otherwise.
The Widget base class defines this method as "return self._selectable" and _selectable is defined as False, so you may do nothing if your widget is not selectable, or if your widget is always selectable just set your _selectable class variable to True.
If this method returns True then the keypress() method must be implemented.
Returning False does not guarantee that this widget will never be in focus, only that this widget will usually be skipped over when changing focus. It is still possible for non selectable widgets to have the focus (typically when there are no other selectable widgets visible).
rows(self, size, focus=False)
Return the number of rows required for this widget given a number of columns in size.
This is the method flow widgets use to communicate their size to other widgets without having to render a canvas. This should be a quick calculation as this function may be called a number of times in normal operation. If your implementation may take a long time you should add your own caching here.
There is some metaclass magic in the Widget base class that causes the result of this function to be computed from any canvas cached by CanvasCache, so if your widget has been rendered you may not receive calls to this function. The class variable ignore_focus may be defined and set to True if this widget renders the same size regardless of the value of the focus parameter.
pack(self, size, focus=False)
Return a "packed" size (maxcol, maxrow) for this widget, a minimum size where all content could still be displayed. Fixed widgets must implement this method and return their size when () is passed as the size parameter.
The Widget base class definition of this method returns the size passed, or the maxcol passed and the value of self.rows() as the maxrow when (maxcol,) is passed as the size parameter.
This is a new method that hasn't been fully implemented across the standard widget types. In particular it has not yet been implemented for container widgets.
Text widgets have implemented this method. You can use pack() to calculate the minumum columns and rows required to display a text widget without wrapping, or call it iteratively to calculate the minimum number of columns required to display the text wrapped into a target number of rows.
keypress(self, size, key)
Handle the keypress event for key and return None, otherwise return key.
Container widgets will typically call the keypress() method on whichever of their children is set as being in focus.
A command_map dictionary-like object is used by the standard widgets to determine what action should be performed for a given key. You may modify these values to your liking in your application. See command_map.py for the defaults.
In your own widgets you may use whatever logic you like: filtering or translating keys, selectively passing along events etc.
mouse_event(self, size, event, button, col, row, focus)
Handle a mouse event and return True, otherwise return False
Container widgets will typically call the mouse_event() method on whichever of their children is at the position (col, row). (col, row) is relative to the top-left corner of the widget.
get_cursor_coords(self, size)
Return the cursor coordinates (col, row) of a cursor that will appear as part of the canvas rendered by this widget when in focus, or None if no cursor is displayed. The ListBox widget uses this method to make sure a cursor in the focus widget is not scrolled out of view. It is a separate method to avoid having to render the whole widget while calculating layout.
Container widgets will typically call the get_cursor_coords() method on their focus widget.
get_pref_col(self, size)
Return the preferred column for the cursor to be displayed in this widget. This value might not be the same as the column returned from get_cursor_coords().
The ListBox and Pile widgets call this method on a widget losing focus and use the value returned to call move_cursor_to_coords() on the widget becoming the focus. This allows the focus to move up and down through widgets while keeping the cursor in approximately the same column on screen.
move_cursor_to_coords(self, size, col, row)
Set the cursor position within a widget and return True, if the position cannot be set somewhere within the row specified return False.
