Taking ToscaWidgets for a spin

by gldnspud on December 28, 2006

I finally had the time to fiddle around with ToscaWidgets inside Pylons recently, and came up with a top bar for SPHYRA that is driven by information in a Schevo database.

  • The widget class takes care of converting parameters given to the widget to data structures usable by the Genshi template.
  • The widget template converts it into a fairly simple navigation bar.
  • The controller base class populates a widget using the database and makes it available to every controller.

Notes

Some notes for my own edification, as I learn this from the ground up as it's being developed. (Thanks, Alberto, et al, for doing all the hard work!)

Creating a simple widget class usually involves the following:

from toscawidgets.core import Widget

class MyWidget(Widget):

    # 'params' are the valid keyword arguments that can be passed to
    # __init__ and __call__
    params = [
        'abc',
        ]

    # 'template' is 'templatetype:templatename'
    template = 'genshi:myapp.widgets.templates.mywidget'

    # Set default values for parameters.
    abc = 123

    # When ToscaWidgets prepares a dictionary to be sent to the
    # template specified above, it calls update_params against
    # that dictionary.  Use update_params to modify the parameters
    # set on the widget to a form that is 'massaged' enough for
    # the template to make proper use of.
    def update_params(self, d):
        d['abc'] = d.get('abc', 0) * 2

To include a widget in the final HTML, given that c.widget is an instance of a ToscaWidget:

${c.widget}

To create a new widget based on an existing one, just call the widget instance with the parameters you want to override:

c.widget = c.widget(some_param=some_new_value)

Questions and thoughts

Is there a way for the TopBar widget to include an image resource for the logo, and then be configured in such a way that it is used as the default unless overridden by a parameter that contains the URL for a different image?

I'd also like to get my TopBar-specific CSS packaged up with the TopBar widget itself, and simplify it a little more.

I'm sure I'll figure these out in time but if anyone responds to this I'll give them a free cookie. (*)

(*) Cookies not guaranteed to be edible. Cookies may consist of electronic transactions already made in the course of reading this text.

{ 4 comments }

Alberto December 30, 2006 at 10:23 am

Hi Matthew,

Glad you’ve finally found some time to take a spin to TWs… I’ll be following this to see how you’re doing with them (and because it’s interesting to see what users come up with when there are virtually no docs… ;)

As my browser already swallowed your cookie I feel obliged to answer your questions:

The easiest way to provide the topbar image is to use an ‘image_url’ param (listing it at params) and provide a default url for it which you can override when displaying the widget. However, you’ll have to take care yourself of computing the correct url and passing it to the widget.

But this brings up an interesting idea I had never thought of before: You can also create an ImageLink (subclassing tw.resources.Link) which used the same semantics as (JS/CSS)Link for linking to an image on your filesystem. The advantage of this is that TGWidgetsMiddleware will take care of serving them and computing the correct urls (in case the web root is not /). If you don’t submit a patch for this before I get back home on the Jan. 2nd I might implement it :)

To include the topbar’s CSS you can use a CSSSource or a CSSLink:

topbar_css = CSSLink(__name__, ‘static/topbar.css’)

will link to ‘static/topbar.css’ relative to the module you write thios snippet in (unless you specify another one instead of __name__).

or declare it it inline with CSSSource:

topbar_css = CSSSource(“””
#topbar {
….
};”””)

To make sure it’s always included in every page the topbar is rendered you’ll need 3 things:

1) List it at the TopBar’s css attribute:

class TopBar(Widget):
css = [topbar_css]

2) Your page template must XInclude a template that will render resources. See for example:

(damn! TG’s Trac is down again, can’t provide a link… well, I’f you’re running from a SVN checkout see ToscaWidgets/examples/PylonsTWSample/pylonstwsample/templates/master.html )

3 ) You finally need to pass needed resources at the c.resources attribute. To do it use toscawidgets.api.retrieve_resources to collect them from your widget(s) (it’s a generic function which accepts iterators so to retrieve all resources from many widgets just stuff them in a list)

In the PylonsTWExample I’m using retrieve_resources on the whole g anc c objects. However, I wouldn’t recommend that because that operation could be *very* expensive as it’ll recursively inspect everything it knows how to inspect. What I have in mind as a better solution is to provide a g.w and c.w variables where widgets can be stuffed for every request or a particular request and just run retrieve_resources on those. To remove the boiler-plate for this at every controller method you could write a specialized render_response that did this before calling pylons’ render_response.

BTW, the same way you do this for css you can do it for javascript code too. In fact, you can also declare dependencies among javascript resources to make sure everything is included in the correct order. For example:

my_js = JSLink(__name__, ‘static/my.js’, javascript=[dep1, dep2, dep3])

will make sure dep1, dep2 and dep3 are included before my_js in every page a widget that has my_js listed in it’s javascript attribute is included in.

Wish you a great 2007 :)

Best regards,
Alberto

gldnspud December 31, 2006 at 12:10 am

Well I’ve got one of those down, the ImageLink class you mentioned. The version I needed now is here, with commentary on where it could wind up as far as handy features: http://code.ostsystems.com/browser/trunk/Sphyra/sphyra/widgets/imagelink.py

Thanks for the quick reply Alberto!

gldnspud December 31, 2006 at 12:45 am

c.w, g.w, and custom render_response now in http://code.ostsystems.com/changeset/58 :)

Alberto Valverde January 9, 2007 at 6:15 am

Hi,

I’ve finally made the Image widget I promised (http://paste.turbogears.org/paste/799).

Take a look as it shows how to properly display widgets inside other widgets. The important thing is that you must make the inner widget a child of the container and use “display” to display widgets in templates (not __str__ or render). The reason for this is so widgets written in different templating languages are displayed properly.

I’ve decided not to include it in core ToscaWidgets as it requires Genshi (mainly for the handy py:attrs construct). Maybe a watered-down version using string.Template can make it.

You need to upgrade ToscaWidgets as it uses the new signature for Links (no modname or filename pos. parameters). This allows setting an id and parent for Links (a use-case I hadn’t contemplated before).

Alberto

Comments on this entry are closed.

Previous post:

Next post: