This Blog continues on http://aliafshar.github.io/blog

Monday, November 13, 2006

Kiwi proxy widgets, a common widget API

While we are on the notion of Kiwi, the PyGTK helper library, we should be discussing the proxy widgets. They offer a certain set of advantages over their plain PyGTK counterparts:
  1. They have a common signal emission API for change in content
  2. They offer a common API for setting and reading their value.
  3. They offer validation, and coercion
  4. They contain a few general improvements over the plain PyGTK widgets
What does this mean?

Simply it means you can make any data widget (eg Entry, CheckButton, Combo etc), you can connect its signal "content-changed", you can read the data with widget.read(), and set the value with widget.update().

If you have read any of the previous blogs about adapting Zope.schema fields to widgets, and you are alert, you will notice that this is amazingly useful. It means that we can define an interface for a general InputWidget type, something like:

class IInputWidget(Interface):

def read():
"""Read the value in the widget"""

def update(value):
"""Set the value of the widget"""

def connect_content_changed(callback):
"""Set a callback to get called when the widget value is changed by the user"""

Once we have done this we can essentially forget about what widget we are using, and what type of field it is representing. A dream come true for the "program for an interface" paradigm.

At the same time, these widgets do have some downsides:
  1. They are bound to an arbitrary concept of a "model" and an attribute within that model
  2. They insist on having a data-type set
The good thing is that the downsides can be faked by setting the appropriate PyGTK properties. Hurrah!

Let's look at a basic example:


from kiwi.ui.widgets.entry import ProxyEntry

e = ProxyEntry()
# Fake those things that Kiwi requires
e.set_property('data-type', str)
e.set_property('model-attribute', 'fake')

# Test reading and writing
e.update('I am the starting value')
assert e.read() == 'I am the starting value'


Now imagine you had a lot of these, and that they were all automatically adapted from a zope schema (as discussed before). These widgets could all be readable using something like:


widgets[name].read()


Amazing! One might be a CheckButton, returning a boolean value. One might be a Date chooser returning a datetime, one might be an entry returning a str or a unicode. Complete automatic UI generation that things like Django would be jealous of!