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.