# Introducing Twisted Web Widgets

## Abstract

This is more of a simple description of all the classes, plus the common pitfalls of coding in Web Widgets. Oh well.

## Example Code

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.

## The Diagram

## The Classes

### Gadget

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

### Widget

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

### Presentation

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.

### WidgetPage

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

# Common Pitfalls

## WidgetPage

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>'''`

## Adding Widgets to a Gadget

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.

## Return values of display()

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.

Last modified: Tue Mar 12 22:50:39 EST 2002