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:
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:
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.