This is really yummy! I play a bit with it yesterday (http://www.checkandshare.com/blog/?p=24) and have a couple of questions.

Toolbox:
In toolbox-start cherrypy.config is set to  "server.logToScreen":True, but (at least in my setup) nothing is sent to the console. How do I turn this on?

CSS:
I couldn't get my style sheet over in my test page ( css=[CSSLink(static, "grid.css")] ) . I ended up adding the link to the css in my widget template.

Validation:
- What about client side validation (for example _javascript_ when the widgets loose focus)
Should this be implemented on a per widget basis?

Cheers
Ronald

On Nov 10, 2005, at 10:01 PM, Kevin Dangoor wrote:


As I mentioned previously (in "TurboGears forms first look":
http://tinyurl.com/c28c7), turbogears.forms has the concept of
"widgets" that are put together to create a form. In fact, widgets are
so important to how forms are put together that I've renamed the
package turbogears.widgets. Form objects are, in fact, widgets (and
now live in turbogears.widgets.forms).

You'll remember from the "first look" that my opinion is that doing
on-the-fly generation of the form has some significant advantages over
straight code generation. By using widgets all over the place, you can
customize the look and behavior of the widget and have that change
apply everywhere.

I know that there are times when you need complete 100% control over
how things look on the web. Even though you can extensively customize
widgets, there will be circumstances where you need to go even
further. For this reason, widgets also support output of Kid for the
purposes of customization. You'll be able to take that output and drop
it directly into your template so that you can modify however you need
to.

What I'm going to talk about in the following sections is all up for
discussion. Now that there's actual code, people can play with it and
decide what they like and don't like. In truth, I expect that there
will be a lot more discussion after the release of 0.9 and that's
fine. We'll clean up the rough edges between 0.9 and 1.0.

Using Widgets
-------------

Here's an example from the "first look":
widgets.TextField("age", default=0, validator=validators.Int())

The first parameter is the name that will be given to the field on the
form. You can set a default value, and you can also change the
validator used by the widget.

The role of validators here is not just validation: the FormEncode
validator interface provides from_python and to_python conversion. In
the simple case above, this means that the value coming in from the
web is converted to an int on its way in.

Widgets have two similar methods for using them: insert and render. In
practice, insert is the one that is used most often. The difference
between them is that render returns HTML (or whatever text form you're
expecting) and insert returns a Element generator, which is convenient
for inclusion in a surrounding Kid template.

Insert (and render) both take a "value" parameter. The basic idea is
something like this:

<div py:replace="agefield.insert(age)"/>

And, that is roughly what a Form object will do when it is insert-ed
into your page.

The validator is important because you can have something like a
select widget that needs to translate between IDs and names. Or, you
could have a repeating form element widget that takes in a collection
of data as its value.

Customizing Widgets
-------------------

The easiest customization you can do is to include a stylesheet that
implements the classes used by the widgets. All of the standard
widgets should use CSS classes extensively and consistently to make
them easy to customize.

Beyond that, you can replace the look of a widget without changing its
behavior very easily. Let's say you wanted the age field in the
example above to say "years old" after the field. (This is a goofy
example, but run with me for a second here...)

widgets.TextField("age", default=0, validator=validators.Int(),
template="myproject.templates.agewidget"

And agewidget.kid could look like this:
<div xmlns:py="http://purl.org/kid/ns#"><input type="text"
name="${widget.name}" value="${widget_value}"/> years old</div>

That's not hard. How do you know what to put in that template? The
TurboGears Toolbox will help with that, but this message isn't about
the Toolbox :)

Sometimes just changing the generated markup isn't enough and you need
to change the behavior of the widget. Widgets are standard Python, so
subclassing will let you customize further.

Writing Widgets
---------------
I'll go for what is currently the most complex widget to show the
parts of writing a widget:

class CalendarDatePicker(Widget):
    css=[CSSLink(static, "calendar-system.css")]
    _javascript_=[JSLink(static, "calendar.js"),
                JSLink(static, "lang/calendar-en.js"),
                JSLink(static, "calendar-setup.js")]
    template = """<div><input type="text" id="${widget.name}"
name="${widget.name}" value="${widget_value}"/>
<button id="${widget.name}-trigger">Choose</button>
<script type="text/_javascript_">
Calendar.setup(
    {
        inputField : "${widget.name}",
        ifFormat : "%m/%d/%Y",
        button : "${widget.name}-trigger"
    }
);
</script></div>
"""
    validator = validators.DateConverter()
    _default = None
    sampleValue = datetime.now()

    def __init__(self, name=None, default=None, **kw):
        if default is not None:
            self._default = default
        super(CalendarDatePicker, self).__init__(name, **kw)

    def _get_default(self):
        if self._default is None:
            return datetime.now()
        return self._default

    default = property(_get_default)

    def source(self):
        return self.render("${%s.strftime('%%m/%%d/%%Y')}" % self.name,
                           format="xml", convert=False)

All widgets subclass turbogears.widgets.Widget.

Widgets can specify that they need their own CSS and _javascript_. There
are CSSLink, JSLink, CSSSource and JSSource objects to specify these.
The Link and CSSSource objects automatically appear in the <head>. The
JSSource ones can appear in the <head>, just inside the <body> or at
the bottom of the <body>. At the moment, those insertions are
happening in master.kid, but I think it needs to move to sitetemplate.

These are only included once. If you have 5 calendar widgets on a
page, only 1 set of CSS and _javascript_ references will appear at the
top.

There is a function called "register_static_directory" in
turbogears.widgets. The way this is generally used is (at the module
level, in your widget module):

register_static_directory(__name__,
pkg_resources.resource_filename(__name__, "static"))

This will register widget statics for your module assuming that they
appear in a directory called static underneath your module's
directory. The "static" parameter that you see in the CSSLinks above
is just set like this in turbogears.widgets:

static = __name__

This widget shows off the use of a different default validator, so
that incoming values automatically become standard Python datetime
objects. It also changes the default (if you don't specify one) to be
datetime.now() by making default into a property.

The source method is used for generating Kid output that can be used
for the "code generation" style of working.

You can also see the sampleValue that is set. This is used when
generating a visual sample that appears in the TurboGears Toolbox
(which is not what this message is about :).

So, how flexible is a widget?

- the template can look like whatever you want (and can be inline or
specified via the standard "full.package.reference")
- it can require its own CSS
- it can have its own _javascript_
- the validator can be changed to handle different to/from_python behavior
- you can override create_dict to alter how the dictionary that is
passed to the template is derived
- you can change the default default value
- you can change how the visual sample is created
- you can alter the default label text used on forms

Overall, pretty flexible. I'm sure people will find ways in which it's
not flexible enough... but, this should meet a bunch of needs.

Current Status
--------------

We need widgets, and the current ones need to be fleshed out. The
Label widget is probably the only one that's complete. Take a look at
turbogears/widgets/__init__.py if you'd like to help out with that.

Work needs to be done on TurboGears Toolbox.

A function for instantiating widgets from an SQLObject needs to be created.

etc. etc.

But, I wanted to get a first look out there. I think this is ready for
people to start playing with and trying out (and certainly commenting
on!).
--
Kevin Dangoor
Author of the Zesty News RSS newsreader

email: [EMAIL PROTECTED]
company: http://www.BlazingThings.com
blog: http://www.BlueSkyOnMars.com


Henriksvej 15

2400 København NV

+45 22 27 85 11

[EMAIL PROTECTED]


Reply via email to