twisted.web.widgets is being gradually
deprecated in favour of Woven. See the module
docstring for details.
This is more of a simple description of all the classes, plus the common pitfalls of coding in Web Widgets. Oh well.
Chris Armstrong has made some example (contrived) Widgets
code available, at http://twistedmatrix.com/users/carmstro.twistd/files/Example.tar.gz.
Unpack it into your ~/TwistedPlugins / directory and run
twistd -g Example somewhere to start the server on
localhost:8080. Please read the code (comments) before getting
confused about the odd behavior of the example server -- note
that you are supposed to get a No Resource error on the
root URL (http://localhost:8080/) when you first
load it up; the code explains this.

A collection of widgets, like a directory
of HTML files.
You add widgets to it with self.putWidget("name", WidgetInstance()).
This widget will be rendered inside the Gadget-local page.
Also, if you make a Gadget that is also a subclass of Widget
,
then whenever the index
(http://foo.com/foo/, foo
being the
Gadget/Widget resource) is requested, the object will be
rendered as a Widget inside of the Gadget-local page factory.
The Gadget-local page factory is the 'pageFactory' attribute of the gadget,
which should be a class that takes a widget in it's
constructor, and displays that Widget in some form. So in your
__init__ method for your Gadget subclass, do self.pageFactory = SomeWidgetPageSubclass
(see WidgetPage
below) (note that it is not an instance,
but the actual class object).
A Widget is simply something that is renderable, through its
display() method. This method is
expected to return a list of HTML strings. (it can also contain
instances of defer.Deferred -- but this is another story).
This is a special Widget that already has a display() method, which renders some objects through a template. You override the special 'template' variable, which is a string with interpolated python expressions. It should look something like:
template = '''\
<html>
<head><title>%%%%self.title%%%%</title></head>
<body>%%%%self.getContent(request)%%%%
</body></html>
'''
As you can see, Python expressions are denoted with
surrounding sets of 4 %s. The expressions are
evaluated in a special namespace with only 'self' and 'request'
in it.
A WidgetPage is a special Page/Presentation combination that
allows you to pass a Widget object to its constructor. The most
common use of this class is for subclassing; you should have a
subclass that defines a custom 'template' attribute. WidgetPage stores
the widget you pass to it in it's 'widget' attribute, so remember that
whenever you're making a customized template, use
%%%%self.widget%%%% to access it (see Common
Pitfalls: WidgetPage
below).
If you have a subclass of widgets.WidgetPage , make sure your template
accesses the widget it's displaying with the 'self.widget' object. For example, if you
want to get the title from the current widget you're
displaying:
template = '''\ <html> <head><title>%%%%self.widget.title%%%%</title> </head></html> '''
instead of:
template = '''\ <html> <head><title>%%%%title%%%%</title></head> </html> '''
I had some code like this in one of my Gadgets: self.putWidget("Foo",
widgets.TitleBox(MyWidget())). Later whenever trying to
access this widget I got this traceback (word wrapped for readability):
Traceback evaluating code in twisted.words.webwords.Page:Traceback
(most recent call last):
File "/usr/lib/python2.1/site-packages/twisted/web/widgets.py",
line 86, in display
x = eval(elem, namespace, namespace)
File "<string>", line 0, in ?
AttributeError: TitleBox instance has no attribute 'getHeader'
Now remember, widgets that you add to a gadget with
putWidget are rendered with self.pageFactory like so: self.pageFactory(theChildWidget). The
problem is, theChildWidget in this case was actually TitleBox!
and of course, TitleBox doesn't follow our template's protocol
of having a 'getHeader' method. So,
the lesson is: do not wrap your real widgets with other widgets
when adding to a Gadget: do formatting either in a) the
template or b) the widget's display() method.
If you ever get this traceback (word wrapped for readability):
web.Server Traceback
Traceback (most recent call last):
File "/home/punck/cvs/Twisted/twisted/web/server.py", line 215, in process
body = resrc.render(self)
File "/usr/lib/python2.1/site-packages/twisted/web/widgets.py", line 408,
in render
displayed = self.display(request)
File "/usr/lib/python2.1/site-packages/twisted/web/widgets.py", line 97,
in display
tm.extend(val)
AttributeError: TitleBox instance has no attribute '__len__'
It's because you tried to put a widget in the list that
display() returns! For now, just tack on .display(request) to all the widgets you
want to return in that list.