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. The Gadget-local page is the 'page'
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.page = SomeWidgetPageSubclass
(see "WidgetPage" below)
(note that it is _not_ an instance, but the actual class object).
A Widget is simply something that is renderable, using the 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 you don't create a display()
method for. 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 it's 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%%%%
(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.page like so: self.page(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 a traceback like this:
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. This will be fixed later on, that is, you will be able to include Widget objects in the list that display() returns.