Here's the widget I came up with. The value is (low, high, unit), aka
(float, float, str) and renders like:
____ to ____ [unit]
It works fine, but I ended up duplicating the validation in .set_value()
and ._parse(), and also doing the subwidgets' validation. I wasn't sure
how to get around that since .set_value() wants to raise ValueError, and
with different messages. I could call .parse() in .set_value() but that
doesn't sound like a good idea.
The unit list would like to be two-level (HTML <optgroup>), but that
doesn't seem feasable without rewriting SelectWidget. Is anybody doing
optgroups?
http://www.w3.org/TR/REC-html40/interact/forms.html#edef-OPTGROUP
=====
class QuantityRangeWidget(widget.CompositeWidget):
"""A widget that takes a number range and a picklist of units.
The value is a tuple: (low, high, unit)
'low' and 'high' are floats, 'unit' is a string.
Null value is (None, None, anything).
Validation:
- Propagate errors from subwidgets (e.g., bad number).
- If 'low' is present, 'unit' must be present.
- If 'high' is present, both 'low' and 'unit' must be present.
- If both 'low' and 'high' are present, 'high' must be greater
than 'low'.
"""
def __init__(self, name, value=(None, None, 'gallons'), **kwargs):
options = [ (x, x, x) for x in VOLUME_UNITS + MASS_UNITS]
widget.CompositeWidget.__init__(self, name, **kwargs)
self.add(widget.FloatWidget, 'low', value=value[0])
self.add(widget.FloatWidget, 'high', value=value[1])
self.add(widget.SingleSelectWidget, 'unit', value=value[2],
options=options)
def render_content(self):
tio = TemplateIO(html=True)
tio += self.get_widget('low').render_content()
tio += htmltext(" to ")
tio += self.get_widget('high').render_content()
tio += htmltext("   ")
tio += self.get_widget('unit').render_content()
return tio.getvalue()
def set_value(self, value):
if not isinstance(value, (tuple, list)) or len(value) != 3:
raise TypeError("value must be 3-tuple: (low, high, unit)")
low, high, unit = value
def floatify(n, what):
if n is not None:
try:
n = float(n)
except TypeError:
raise TypeError("%s must be number" % what)
return n
low = floatify(low, "low value")
high = floatify(high, "high value")
if low is not None and (low == 0.0 or low >= 1.0):
low = int(low)
if high is not None and (high == 0.0 or high >= 1.0):
high = int(high)
is_low = low is not None
is_high = high is not None
is_unit = unit is not None
if is_high and not is_low:
raise ValueError("Must provide low number.")
if (is_low or is_high) and not is_unit:
raise ValueError("Must provide unit.")
if (is_low and is_high) and low > high:
raise ValueError("High number must be higher than low number.")
self.value = (low, high, unit)
self.get_widget('low').set_value(low)
self.get_widget('high').set_value(high)
self.get_widget('unit').set_value(unit)
def _parse(self, request):
low = self['low']
high = self['high']
unit = self['unit']
self.value = (low, high, unit)
for name in ['low', 'high', 'unit']:
error = self.get_widget(name).get_error()
if error:
what = (name != "unit") and "value" or "control"
tup = name.capitalize(), what, error
self.error("%s %s: %s." % tup)
return
is_low = low is not None
is_high = high is not None
is_unit = unit is not None
if is_high and not is_low:
self.error = "Must provide low number."
elif (is_low or is_high) and not is_unit:
self.error = "Must provide unit."
elif (is_low and is_high) and low > high:
self.error = "Second value must be greater than first value."
=====
--
-- Mike Orr <[EMAIL PROTECTED]>
_______________________________________________
Quixote-users mailing list
[email protected]
http://mail.mems-exchange.org/mailman/listinfo/quixote-users