Personal tools
You are here: Home Documentation Tutorials Working with Forms in Grok Creating custom widgets and overriding widgets globally

Creating custom widgets and overriding widgets globally

How to create new widgets, as well as replace existing widgets with your own custom versions.
A walkthrough of the basics of automatically generating HTML forms using Grok, as well as a discussion of a few more advanced Form manipulations.
Page 7 of 9.

The widgets used to render form fields in Grok are supplied by the zope.app.form package. You can create your own custom widget and override the default widget on a per field basis, or override that widget globally for all fields of a particular type.

Creating a new widget

Making a widget is fairly straightforward. Every widget implements the zope.app.form.interfaces.IWidget interface. There are number of useful existing widget implementations and base classes to make it easier to implement this interface.

Let's say that we wanted to make a custom text input widget where the default display width is longer. We could write this widget as:

from zope.app.form.browser.textwidgets import TextWidget

class LongTextWidget(TextWidget):
    displayWidth = 35

Then use it in form on a per field basis with by setting the custom_widget attribute.

class MammothForm(grok.AddForm):
    form_fields = grok.AutoFields(Mammoth)
    form_fields['owner'].custom_widget = LongTextWidget

Overriding widgets globally

If you want to override every TextLine form field globally, then you need to register an adapter as an override using ZCML. Create a file in your project named overrides.zcml and put the following adapter in it:

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">

    <adapter
        for="zope.schema.interfaces.ITextLine
             zope.publisher.interfaces.browser.IBrowserRequest"
        provides="zope.app.form.browser.interfaces.ITextBrowserWidget"
        factory="gum.widgets.LongTextWidget"
        permission="zope.Public"
        />

</configure>

The you need to tell Zope 3 to include that configuration and have it override the existing configuration with the includeOverrides directive. Change the configure.zcml file in your package to read:

<configure xmlns="http://namespaces.zope.org/zope"
           xmlns:grok="http://namespaces.zope.org/grok">

    <include package="grok" />
    <includeDependencies package="." />
    <grok:grok package="." />
    <includeOverrides file="overrides.zcml" />

</configure>

Now every TextLine form field will use your custom widget. Note that this is very global - so if you have multiple apps within the same server instance, they will all be updated.

Widget interfaces

For reference, the IWidget, IInputWidget and IDisplayWidget interfaces are shown below. Also note that IView` in IWidget is from the ``zope.component.interfaces.IView interface. This interface is quite simple and only declares a generic context and request attribute:

class IWidget(IView):
    """Generically describes the behavior of a widget.

    Note that this level must be still presentation independent.
    """

    name = Attribute(
        """The unique widget name

        This must be unique within a set of widgets.""")

    label = Attribute(
        """The widget label.

        Label may be translated for the request.

        The attribute may be implemented as either a read-write or read-only
        property, depending on the requirements for a specific implementation.

        """)

    hint = Attribute(
        """A hint regarding the use of the widget.

        Hints are traditionally rendered using tooltips in GUIs, but may be
        rendered differently depending on the UI implementation.

        Hint may be translated for the request.

        The attribute may be implemented as either a read-write or read-only
        property, depending on the requirements for a specific implementation.

        """)

    visible = Attribute(
        """A flag indicating whether or not the widget is visible.""")

    def setRenderedValue(value):
        """Set the value to be rendered by the widget.

        Calling this method will override any values provided by the user.

        For input widgets (`IInputWidget` implementations), calling
        this sets the value that will be rendered even if there is
        already user input.

        """

    def setPrefix(prefix):
        """Set the name prefix used for the widget

        The widget name is used to identify the widget's data within
        input data.  For example, for HTTP forms, the widget name is
        used for the form key.

        It is acceptable to *reset* the prefix: set it once to read
        values from the request, and again to redraw with a different
        prefix but maintained state.

        """

class IInputWidget(IWidget):
    """A widget for editing a field value."""

    required = Bool(
        title=u"Required",
        description=u"""If True, widget should be displayed as requiring input.

        By default, this value is the field's 'required' attribute. This
        field can be set to False for widgets that always provide input (e.g.
        a checkbox) to avoid unnecessary 'required' UI notations.
        """)

    def getInputValue():
        """Return value suitable for the widget's field.

        The widget must return a value that can be legally assigned to
        its bound field or otherwise raise ``WidgetInputError``.

        The return value is not affected by `setRenderedValue()`.
        """

    def applyChanges(content):
        """Validate the user input data and apply it to the content.

        Return a boolean indicating whether a change was actually applied.

        This raises an error if there is no user input.
        """

    def hasInput():
        """Returns ``True`` if the widget has input.

        Input is used by the widget to calculate an 'input value', which is
        a value that can be legally assigned to a field.

        Note that the widget may return ``True``, indicating it has input, but
        still be unable to return a value from `getInputValue`. Use
        `hasValidInput` to determine whether or not `getInputValue` will return
        a valid value.

        A widget that does not have input should generally not be used
        to update its bound field.  Values set using
        `setRenderedValue()` do not count as user input.

        A widget that has been rendered into a form which has been
        submitted must report that it has input.  If the form
        containing the widget has not been submitted, the widget
        shall report that it has no input.

        """

    def hasValidInput():
        """Returns ``True`` is the widget has valid input.

        This method is similar to `hasInput` but it also confirms that the
        input provided by the user can be converted to a valid field value
        based on the field constraints.
        """

class IDisplayWidget(IWidget):
    """A widget for displaying a field value."""

    required = Bool(
        title=u"Required",
        description=u"""If True, widget should be displayed as requiring input.

        Display widgets should never be required.
        """)