ToscaWidgets has several core features:
- The main function of a widget is simply to render a template.
- Widgets can define parameters, which affect how they are displayed.
- Hooks are provided that allow server-side code to process the parameters.
- Widgets can define resources that they depend on - primarily JavaScript and CSS.
- A set of widgets can exist as a hierarchy, with parent-child relationships.
There are also some minor features, such as assistance with calling JavaScript functions, and creating sets of widgets using declarative Python code. Note that validation is not a feature of ToscaWidgets - this is in tw.forms.
To give you an idea on how this works, this is about the minimal example you can get:
from tw.api import Widget
class MyFirstWidget(Widget):
template = "<div>Hello there ${value}!</div>"
engine_name = "genshi"
my_first_widget = MyFirstWidget("my_first_widget")
print my_first_widget.display("Peter Pan")
That wasn’t so hard, was it?
Every widget can define a number of parameters that affect how it is displayed. For example, a TextField has a size parameter that controls how many characters can be entered. To a user of the widget, the parameters are everything, the whole way they control the widget’s appearance and behaviour. It’s very important for a widget developer to provide good documentation on the parameters - this is the majority of the widget’s documentation.
So let’s expand our above example to contain a parameter - with proper documentation of course:
class MyFirstWidget(Widget):
template = "<div style='background-color: ${color}'>Hello there ${value}!</div>"
engine_name = "genshi"
params = dict(color="The background color of the widget")
color = "red" # this provides the default value for color
So now our example widget offers a parameter called color which will determine the background color of our widget. Put aside for a moment that good style (pun intended) would of course instead to use a class-attribute and a stylesheet.
So when using our widget as we did before, this will be the result:
<div style="background-color: red">Hello there Peter Pan!</div>
Values for parameters can generally be specified at three points:
- When the widget class is defined - this allows widget developers to provide defaults. As a matter of style, defaults should be provided wherever possible.
- When the widget is instantiated - this is where a web developer will want to customise the appearance and behaviour of a widget.
- When the widget is displayed - this is for parameters that are not known at instantiation time, e.g. the contents of fields.
Let’s explore these possibilities by some short examples:
# use the given default
my_first_widget = MyFirstWidget("my_first_widget")
print my_first_widget.display("Peter Pan")
# pass a parameter at instantiation-time
my_second_widget = MyFirstWidget("my_second_widget", color="green")
print my_second_widget.display("Peter Pan")
# override a parameter at display-time
my_third_widget = MyFirstWidget("my_third_widget")
print my_third_widget.display("Peter Pan", color="blue")
The result of this is the following:
<div style="background-color: red">Hello there Peter Pan!</div>
<div style="background-color: green">Hello there Peter Pan!</div>
<div style="background-color: blue">Hello there Peter Pan!</div>
Some parameters apply to all widgets and are particularly important: id is a unique name of the widget that should be rendered as ID-attribute in the DOM, and value is the displayed value, e.g. the text that appears in a TextField. In almost all cases, id will be specified at instantiation time, and value at display time.
We have been using value already - it is simply the first positional argument to the display()-call.
The id has been used also - it`s the first positional argument to the Widget-constructor.
And because the purpose of a widget is to render HTML, all widgets also define two parameters, css_class and css_classes. The former is a string, the latter a list of strings.
Both are automatically combined into a single parameter to the template, css_class that you can then use inside the template.
So proper widgets will also always use the css_class and id parameters as part of their template and default-declarations, so that there is one well-known way to override these for a designer. Let’s do so four our test-widget, and remedy the style-issue we introduced before:
class MyFirstWidget(Widget):
template = """<div id="${id}" xmlns:py="http://genshi.edgewall.org/" class="${css_class}">Hello there ${value}!</div>"""
engine_name = "genshi"
css_class = "my_widget"
my_first_widget = MyFirstWidget("my_first_widget")
print my_first_widget.display("Peter Pan", css_classes=["foo", "bar"])
The output is
<div id="my_first_widget" class="bar foo my_widget">Hello there Peter Pan!</div>
As you can see, the css-class is combined from css_class and css_classes, and the ID appears also.
So the styling of the widget is now very easy and flexible.
Once a widget has been instantiated, the instance parameters are locked and cannot be modified - any modification must be done at display time, which works in a purely request-local manner. In other words, widgets are stateless, and this is a key principle in the design of ToscaWidgets - it allows a widget instance to be safely shared between threads. In development mode, ToscaWidgets will detect and prevent attempts to modify instance parameters, although this is disabled in production for efficiency.
In some cases you may need to modify a parameter after the constructor has completed, but before the widget is actually used. The post_init method can be overridden to do this. By the time post_init is called, all of the widget’s children will have been constructed. Also, widgets can be cloned, and the clone method allows modification of any instance parameter.
Depending on the particular widget, some parameters cannot be modified at display time. In particular, it is almost always a bad idea to modify id at display time. At present, ToscaWidgets does not have any way of detecting such misuse, although this is a planned feature.
Sometimes you want parameters to be dynamic, but not pass them on each display()-call. This is especially true for e.g. forms, where you want select-fields to be populated from SQL-queries.
To accomodate this, ToscaWidgets allows you to pass a callable for a parameter. This will get called on rendering. A short example to illustrate this:
import datetime
def get_class():
if datetime.datetime.today().day % 2:
return "green"
return "blue"
my_fourth_widget = MyFirstWidget("my_fourth_widget", css_class=get_class)
print my_fourth_widget.display("Peter Pan")
Note - This behaviour is often useful, but has also been known to cause problems, for example when database classes are passed as parameters, because these are callables themselves. In the future, this will be remedied by introducing a special CallableParameter-class.
Another pitfall with callable parameters is if they are defined at class-declaration-time, like this:
class SomeWidget(Widget):
params = dict(param="A parameter")
param = lambda: "param value"
This is not working - the reason is, that python will make any callable at class-level an instancemethod, so that it expects to be called with an implicit first self-argument. So to make the above work do:
class SomeWidget(Widget):
params = dict(param="A parameter")
def param(self):
return "param value"
Some widgets will want to perform server-side processing of parameters before the template is rendered. For example, a dynamic TextArea may want to adjust the number of visible rows, based on the amount of text displayed. The best place to do such processing is in the widget’s update_params method, which is called just before the template is rendered.
Again, a short example:
class MySecondWidget(Widget):
template = """<div xmlns:py="http://genshi.edgewall.org/" >
${value} is ${length} characters long!
</div>"""
engine_name = "genshi"
def update_params(self, d):
super(MySecondWidget, self).update_params(d)
d.length = len(d.value)
my_second_widget = MySecondWidget("my_second_widget")
print my_second_widget.display("Peter Pan")
The output is:
<div>
Peter Pan is 9 characters long!
</div>
Not to spectacular, but we will see a more useful example right away. The key point here is that in update_params you can put all kinds of values into the namespace of the widget’s template, and use them there.
There other methods that could be hooked, primarily prepare_dict and adapt_value. These exist for historical reasons; update_params should be used wherever possible.
Widgets can define resources that they need to function correctly, usually links to JavaScript and CSS files. ToscaWidgets will insert appropriate HTML fragments to link to these files in the generated output. This is technically challenging, as a widget half-way down the page may want to include a link in the <HEAD> section of the page. To support this, ToscaWidgets post-processes the entire page and inserts fragments at the appropriate points.
The main fragments that are injected are:
- Links to JavaScript and CSS files.
- Inline JavaScript code, generally to initialise a widget.
Now this is one heck of a powerful feature, and deserves a somewhat more elaborated example:
from webtest import TestApp
from tw.api import make_middleware
from tw.api import Widget, JSLink
first_file = JSLink(modname="test", filename="first_file.js")
class ResourceDependingWidget(Widget):
template = "<html><head></head><body>I injected some JavaScript!</body></html>"
engine_name = "genshi"
javascript = [JSLink(modname="test", filename="file.js", javascript=[first_file]),
JSLink(modname="test", filename="bodybottom.js", location="bodybottom")]
def update_params(self, d):
super(ResourceDependingWidget, self).update_params(d)
self.add_call("""alert("look ma, I am calling! And my name is %s");""" % self.id)
resource_depending_widget = ResourceDependingWidget("resource_depending_widget")
def fake_app(environ, start_response):
start_response("200 OK", [("Content-type", "text/html")])
# we need to return an iterable!
return [resource_depending_widget.render().encode("utf-8")]
app = make_middleware(fake_app, stack_registry=True)
app = TestApp(app)
res = app.get("/")
print res.body
And the output of the above magic is this:
<html>
<head>
<script type="text/javascript" src="/toscawidgets/resources/test/first_file.js"></script>
<script type="text/javascript" src="/toscawidgets/resources/test/file.js"></script>
</head>
<body>I injected some JavaScript!
<script type="text/javascript">
alert("look ma, I am calling! And my name is resource_depending_widget");
</script>
<script type="text/javascript" src="/toscawidgets/resources/test/bodybottom.js"></script>
</body>
</html>
Now this is quite a mouthful, but don’t worry, most of it is just some boilerplate to make it run standalone. Let’s try and dissect what has happened here:
- the whole make_middleware and TestApp-stuff is just there so that we create a fake-WSGI-app we can then call. It will create a stack of WSGI-apps including the ToscaWidgetsMiddleware which is needed to inject the referenced resources into the output-stream of our actual WSGI-app.
- our widget renders a “full” page, because that is needed for the middleware to work - it assumes it gets a full HTML-page. This is normally of course not done that way, instead you’d render the widget as part of an enclosing template via your webframework of choice. But for brevities` sake, it’s done as it is.
- the widget declares two static dependencies, one to file.js and one to bodybottom.js. Both are injected into the output. file.js defaults to the location head, and thus is rendered there. bodybottom.js is declared to be run at bodybottom, so there it goes.
- Additionally, file.js is declared to have a dependency itself - and that dependency is injected before the file.js itself! This is a huge & important feature - declaring dependencies like this will take away all pains of maintaining order between your JS-files (and CSS) yourself!
- in the dynamic part of the widget, a JS-callback is rendered. This is done with reference to the widget’s ID. This is immensely helpful - you can put e.g. JS-class-library code into a JSLink, and instantiate a concrete instance with the ID that is rendered into the output.
For the last point, I want to provide a short example:
class MyWidget(Widget):
javascript = [jquery_js]
template = """<div id='${id}'/>"""
def update_params(self, d):
super(MyWidget, self).update_params(d)
self.add_call("""$(document).ready(function() {
$('#%s').text("hello everybody!");
});""" % d.id)
In the last section we saw that ToscaWidgets can inject resources. A question is of course how these are served - and the good news is: ToscaWidgets can also directly serve resources as static files.
This is helpful in development, but is not recommended for production - the ToscaWidgets middleware is considerably less efficient a dedicated web server.
See :doc:`deploy` for instructions on how to configure a web server appropriately to serve static resources.
To save bandwidth and thus loading time of a website, javascript-libraries and CSS-files often are distributed in variants, the “normal” one, and debug, minified and packed variants.
So at development time, one wants to use the normal or even debug-variants, but in the production system, the minified or even packed versions are supposed to be delivered.
ToscaWidgets caters to these needs by so-called variants.
When declaring a static resource (either JSLink or CSSLink), you pass a module-name, and a filename relative to that module.
But instead of passing the filename as string, you can pass it a dictionary with the following keys:
- normal must be present, and is used as fallback.
- min for minified variants.
- packed for packed variants.
- debug for debug variants.
Now depending on a global setting in the ResourcesApp, you can determine which of the variants is delivered.
In ToscaWidgets, any widget can have child widgets. Some widgets (e.g. TableForm), will always need to children to function usefully; we’ll call these “compound widgets”. For other widgets (e.g. TextField), having child widgets doesn’t really make sense; we’ll call these “leaf widgets”. ToscaWidgets allows leaf widgets to have children - it’s just down to convention for the user not to specify any. There is also the concept of repeating widgets. A WidgetRepeater is defined with a single child (the parameter is actually called widget) and can generate multiple clones of this dynamically.
Every widget has a reference to its parent (except root widgets, which have no parent). This is useful, as a widget always knows its precise place in the hierarchy. When the id is generated for a widget, it is a compound ID that includes the IDs of it’s parents. Where WidgetRepeater is used, the id includes the repetition number. For example:
>>> mywidget = TableForm('a', children=[TableForm('b', children=[TextField('c')])])
>>> mywidget.children['b'].children['c'].id
'a_b_c'
tw.forms takes this further and has a strip_name parameter that allows a widget to appear in the hierarchy, but not appear in compound IDs. ToscaWidgets has a few hooks to support this feature, although users should not usually have to be aware of them.
In general, the value passed to a widget will be:
- Compound widgets - a dictionary or class instance
- Repeated widgers - a list or iterable
- Leaf widgets - a scalar type
However, individual widgets can override this. For example, OtherSingleSelectField appears to the user to be a leaf widget and it takes a scalar value. However, internally it is a compound widget, and it automatically passes the scalar value to the appropriate child.
Every widget has a special parameter, child_args, which allow overriding parameters on child widgets at diplay time. For example, you may want to apply a particular CSS class to a child:
widget.display(value, child_args={'mychild': {'css_class': 'flashing'}})
To assist with rendering the children of a compound/repeated widget, some helper-functions and variables are passed inside the template namespace:
- display_child
- args_for
- value_for
- c, children
While all this sounds a bit complicated, it’s actually straightforward to use, so let’s see an example:
class HierachyWidget(Widget):
template = """<div xmlns:py="http://genshi.edgewall.org/" >
<ul>
<li py:for="child in children">
${display_child(child)}
</li>
</ul></div>
"""
engine_name = "genshi"
my_first_hierachical_widget = HierachyWidget("my_first_hierachical_widget", children=[MyFirstWidget("first"), MyFirstWidget("second")])
print my_first_hierachical_widget.display(
dict(first="Peter Pan", second="Captain Hook"),
child_args=dict(first=dict(css_class="yellow"),
second=dict(css_class="black")))
The output of this is the following:
<div>
<ul>
<li>
<div id="my_first_hierachical_widget_first" class="yellow">Hello there Peter Pan!</div>
</li>
<li>
<div id="my_first_hierachical_widget_second" class="black">Hello there Captain Hook!</div>
</li>
</ul>
</div>
As you can see, the value and child_args are being pushed downwards to the children just right, and the IDs are composed.
The main feature of ToscaWidgets is simply to render a template.
ToscaWidgets has strong support for multiple template engines. For example, a widget that uses a Genshi template will display correctly inside a widget that uses a Mako template. Root widgets (widgets that have no parent) are displayed directly inside the web framework’s templates. To ensure root widgets display correctly, it’s necessary to specify default_view in the ToscaWidgets configuration.
For efficiency, it’s best to avoid using multiple template engines. To support this, it is possible for a widget to have multiple templates defined. Where possible, ToscaWidgets will use the template that matches the parent widget. All the widgets in tw.forms have both Genshi and Mako templates available.
To make use of this feature, we must move away from the template declarations used so far - that is, inline templates.
Instead we have to use the package-name-syntax. Which makes our examples a bit less self-contained, but that shouldn’t be a problem.:
from tw.api import Widget, JSLink
from webtest import TestApp
from tw.api import make_middleware
from tw.api import Widget, JSLink
class BothWayWidget(Widget):
available_engines = ['mako', 'genshi']
template = "templates.have_it_both_ways"
both_way_widget = BothWayWidget("both_way_widget")
def fake_app(environ, start_response):
start_response("200 OK", [("Content-type", "text/html")])
# we need to return an iterable!
return [both_way_widget.render("hello").encode("utf-8")]
app = make_middleware(fake_app, stack_registry=True, config={
"toscawidgets.framework.default_view" : "genshi",
})
app = TestApp(app)
res = app.get("/")
print res.body
app = make_middleware(fake_app, stack_registry=True, config={
"toscawidgets.framework.default_view" : "mako",
})
app = TestApp(app)
res = app.get("/")
print res.body
This example assumes that there is a package called templates in the sys.path that has two template-files in it, have_it_both_ways.html and have_it_both_ways.mak. They look like this:
have_it_both_ways.html
<div xmlns:py="http://genshi.edgewall.org/" >
Genshi: ${value}
</div>
have_it_both_ways.mak
<div>
Mako: ${value}
</div>
What happens is that depending on the configured default_view, one of the two templates is chosen. To make it works, it’s crucial that you define the attribute available_engines to contain the names of all the engines you provided templates for.