[Subversion] / Presentable / README.txt  

View of /Presentable/README.txt

Parent Directory | Revision Log
Revision: 2439 - (download)
Mon Dec 10 20:54:37 2007 UTC (16 years, 3 months ago) by pje
File size: 6143 byte(s)
Implement the rendering process, w/rules and handlers and such.
=================================================
"Presentable" GUIs with Styles, Skins, and Layout
=================================================


----------------------------------
Style Sheets, Skins, and Rendering
----------------------------------

>>> from peak.ui import rendering


Renderer Objects
================

Style rules operate on ``Renderer`` objects.  Normally, renderer objects are
created automatically, then passed to your rendering rules.  For demonstration
purposes, however, we'll create a renderer manually::

    >>> skin = rendering.Defaults()
    >>> r = rendering.Renderer(skin, None, 42)


Renderer Attributes
-------------------

Renderers can have a ``parent`` renderer, which is ``None`` for a root
renderer::

    >>> print r.parent
    None

They have a ``subject``, which is the object to be rendered, and an ``output``
which will hold the rendered result::

    >>> r.subject
    42

    >>> print r.output
    None

They know what skin they will use to create their target, along with what
skin will be used to create child components (by default, it's the same one)::

    >>> r.skin is skin
    True

    >>> r.child_skin is skin
    True

They have a ``factory`` attribute (which should be set to a function that will
create the output), which will be called with the ``args`` and ``kw``
attributes.  The default factory takes no arguments or keywords, and produces
no output::

    >>> r.args
    ()

    >>> r.kw
    {}

    >>> print r.factory()
    None


Renderer Handlers
-----------------

Renderers have five "handler lists" for callbacks that will be invoked at
various stages of the rendering process.  As with rules, these handlers can
manipulate the renderer's state (or the output object's state), attach add-ons,
etc., as needed.  The five handler lists are as follows::

    >>> r.before_create     # run before factory(*args, **kw) is called
    []
    >>> r.after_create      # run after output = factory(*args, **kw)
    []
    >>> r.find_children     # callbacks must yield subjects for child rendering
    []
    >>> r.add_child         # invoked for each rendered child
    []
    >>> r.finish            # invoked after all children are rendered
    []

Although these look like normal lists, they are in fact a special kind of list,
with two special features for working with callback.  First, they can be
called with positional arguments, returning a list of the results of calling
each item in the list with those arguments::

    >>> r.finish(1,2,3)
    []
    
    >>> r.finish.append(lambda x: x*2)
    >>> r.finish.append(lambda x: x*3)

    >>> r.finish(1)
    [2, 3]

    >>> r.finish(42)
    [84, 126]

    >>> del r.finish[:]

Second, you don't have to use ``append`` to add elements to these lists, and in
fact you generally *shouldn't* do so.  Instead, use the ``add()`` method, which
avoids inserting duplicates::

    >>> r.finish.add(42)
    >>> r.finish
    [42]

    >>> r.finish.add(42)
    >>> r.finish
    [42]

    >>> del r.finish[:]


The Rendering Process
---------------------

The ``.render()`` method of renderers begins by executing any style rules
defined by its skin for the subject being rendered.  These rules can configure
any handlers or attributes necessary, including setting up the ``factory``,
``args``, and ``kw`` attributes if desired.

Once this is done, the ``before_create`` handlers are invoked, giving them a
chance to do any last-minute configuration prior to invoking the ``factory``.
The ``factory`` is then called with the ``args`` and ``kw``, and the result
is placed in the renderer's ``output`` attribute.

Next, the ``after_create`` handlers are invoked, allowing them to do any
post-initialization configuring of the output.

After that, the child rendering phase begins.  The ``find_children`` handlers
are invoked, and each must return a sequence or iterator that yields subjects
to be rendered.  Each of these subjects will have a child renderer created for
it, and a nested rendering operation will be performed.  Each rendered child
will then be passed to the ``add_child`` handlers.

Finally, the ``finish`` handlers are called.  These can manipulate or replace
the ``output``, if desired.

Let's see how it all goes together::


    >>> class MyStyles(rendering.Defaults):
    ...
    ...     class int_rule(rendering.Rule):
    ...         rendering.for_types(int)
    ...
    ...         def before_create(skin, renderer, subject):
    ...             print "before create", renderer, subject
    ...
    ...         args = ('foo',)
    ...         def factory(arg):
    ...             return arg
    ...
    ...         def after_create(skin, renderer, subject):
    ...             print "after create, output =", renderer.output
    ...
    ...         def find_children(skin, renderer, subject):
    ...             print "finding children"
    ...             if subject==42: yield 27
    ...
    ...         def add_child(skin, renderer, child_renderer):
    ...             print "adding child", child_renderer.subject, "->",
    ...             print child_renderer.output
    ...
    ...         def finish(skin, renderer, subject):
    ...             print "finish", renderer, subject
    ...             renderer.output += 'bar '+str(subject)

    >>> print MyStyles().render(42)
    before create <peak.ui.rendering.Renderer object at ...> 42
    after create, output = foo
    finding children
    before create <peak.ui.rendering.Renderer object at ...> 27
    after create, output = foo
    finding children
    finish <peak.ui.rendering.Renderer object at ...> 27
    adding child 27 -> foobar 27
    finish <peak.ui.rendering.Renderer object at ...> 42
    foobar 42

The ``rendering.Rule`` class lets you define handlers and attributes that will
be added to a renderer when the rule matches a subject.  As you can see, any
attribute named as a handler will be added to the renderer's handlers.  Any
other attributes are simply set directly on the renderer.

The ``rendering.for_types()`` decorator must be used within the Rule class'
body, to specify what types the rule will be applied to.

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help