[Zope3-Users] Custom Image Widget

Tom Dossis td at yoma.com.au
Mon Dec 11 18:53:40 EST 2006


Adam Summers wrote:
> Hi,
> 
> I have the following widgets.
> 
> 
> class MyImageDisplayWidget(DisplayWidget):
> 
>    class_ = Image
> 
>    def __call__(self):
>        mycontent = u"oops! no img"
>        if self._renderedValueSet():
>            mycontent = "<img src=\"data:image/gif;base64, " +
> b64encode(self._data.data) + " \" />"
>        return mycontent
> 
> MyImageListDisplayWidget = CustomWidgetFactory(SequenceDisplayWidget,
> subwidget = MyImageDisplayWidget)
> 
> class MyImageWidget(FileWidget):
> 
>    class_ = Image
> 
>    def _toFieldValue(self, input):
>        value = super(MyImageWidget, self)._toFieldValue(input)
>        return self.class_(value)
> 
> MyImageListWidget = CustomWidgetFactory(ListSequenceWidget, subwidget =
> MyImageWidget)
> 
> I want to build a better input widget (MyImageWidget), so that we can do
> the following:
>    * If the field has no data, display a file input.
>    * If the field has data, display the image.
> 
> I know that there are design shortcomings in this, but I need a simple
> example (and I only use the files in lists anyway, so I can delete images).
> 
> Any pointers on how to go about this would be much appreciated; as always


Hi Adam,

I'm not exactly sure of your use case, but I've included a widget
implementation (see below) which you may find useful.  I've used this
widget for an attribute which is an IImage, e.g.


class IContent(zope.interface.Interface):
  image=zope.schema.Object(schema=zope.app.file.interfaces.IImage)

class Content(persistent.Persistent):
  # This property makes the image object locatable (see below)
  image=LocatableProperty(IContent['image'])

ImageAttributeTraverser=\
  z3c.traverser.traverser.SingleAttributeTraverserPlugin('image')


########## property.py

from zope import location
from zope.schema.fieldproperty import FieldProperty

def LocatableProperty(field_or_descriptor):
    """A property which ensures a field instance is locatable.

    The argument can be a field or a descriptor for that field.  The
    zope.fieldproperty.FieldProperty descriptor is used internally
    when a field object argument is passed.

    A locatable property can be used for traversable attribute objects to
    locate them in the referencing object.

    Let's create a simple content object with an attribute which is
    an image:

        >>> from zope import schema
        >>> from zope.app.file.interfaces import IImage

        >>> image_field=schema.Object(__name__='image', schema=IImage)

        >>> class Content(object):
        ...     image=LocatableProperty(image_field)

        >>> ob = Content()
        >>> ob.image is None
        True

    Assign an image object which doesn't have location to the attribute:

        >>> from zope.app.file import Image
        >>> image = Image(data='1234')

        >>> image.__parent__
        Traceback (most recent call last):
        ...
        AttributeError: 'Image' object has no attribute '__parent__'

        >>> ob.image = image

    When we now access the attribute object, it we find that is has
    been wrapped inside a location proxy object:

        >>> ob.image.__parent__ is ob
        True
        >>> ob.image.__name__
        'image'
        >>> ob.image.data
        '1234'
    """

    try:
        field_or_descriptor.__get__
        field_or_descriptor.__set__
        return Locatable(field_or_descriptor)
    except AttributeError:
        return Locatable(FieldProperty(field_or_descriptor))


class Locatable(object):
    """Wrap a FieldProperty descriptor to make it's field locatable.
    """

    def __init__(self, descriptor):
        self.descriptor = descriptor
        self.name = self.descriptor._FieldProperty__name

    def __get__(self, inst, cls):
        ob = self.descriptor.__get__(inst, cls)
        if ob is not None:
            if location.interfaces.ILocation(ob, None) is None:
                ob = location.LocationProxy(ob, inst, self.name)
        return ob

    def __set__(self, inst, ob):
        self.descriptor.__set__(inst, ob)


########## widget.py

from zope.event import notify
from zope.lifecycleevent import ObjectCreatedEvent
from zope.i18n import translate
from zope.dublincore.interfaces import IZopeDublinCore
from zope.size.interfaces import ISized
from zope.component import getMultiAdapter
from zope.traversing.browser import absoluteURL
from zope.app.file import Image
from zope.app.file.browser.image import ImageData
from zope.app.form.browser import DisplayWidget
from zope.app.form.browser import FileWidget


class ImageDisplayWidget(DisplayWidget):

    def __call__(self):
        field = self.context
        image = field.get(field.context)
        if image is None:
            return u"(No image object)"
        alt = IZopeDublinCore(image).title or ''
        title = IZopeDublinCore(image).description or ''
        v = ImageData()
        v.context, v.request = image, self.request
        return v.tag(alt=alt, title=title)


class ImageInputWidget(FileWidget):

    def _toFieldValue(self, input):
        data = super(ImageWidget, self)._toFieldValue(input)
        if data is not None:
            img = Image(data)
            notify(ObjectCreatedEvent(img))
            return img

    def __call__(self):
        input_widget = super(ImageWidget, self).__call__()
        try:
            image = ImageDisplayWidget(self.context, self.request)()
            info = self.info()
            return u'<br />'.join([image, info, input_widget])
        except AttributeError:
            # This happens because the context is IAdding
            return input_widget

    def info(self):
        field = self.context
        image = field.get(field.context)
        if image is not None:
            v = getMultiAdapter((image,self.request),
name='EditMetaData.html')
            url = absoluteURL(v, self.request)
            size = translate(ISized(image).sizeForDisplay())
            title = IZopeDublinCore(image).title or ''
            description = IZopeDublinCore(image).description or ''
            return imageInfoText(title, description, size, url)
        return ''


def imageInfoText(title, desc, size, url):
    return u'<br />'.join([
     '%s: %s' % (_("Size"), size),
     '%s: %s <a href="%s">[%s]</a>' % (u"Title", title, url, u"Edit"),
     '%s: %s <a href="%s">[%s]</a>' % (u"Description", desc, url, u"Edit"),
     ])



More information about the Zope3-users mailing list