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.