Add CssListProperty.

This commit is contained in:
Ian Foote 2013-03-24 20:07:09 +00:00
parent 3936836ef7
commit fd1b2c6bd1
1 changed files with 84 additions and 2 deletions

View File

@ -169,7 +169,7 @@ __all__ = ('Property',
'NumericProperty', 'StringProperty', 'ListProperty',
'ObjectProperty', 'BooleanProperty', 'BoundedNumericProperty',
'OptionProperty', 'ReferenceListProperty', 'AliasProperty',
'DictProperty')
'DictProperty', 'CssListProperty')
from weakref import ref
@ -177,6 +177,8 @@ cdef float g_dpi = -1
cdef float g_density = -1
cdef float g_fontscale = -1
NUMERIC_FORMATS = ('in', 'px', 'dp', 'sp', 'pt', 'cm', 'mm')
cpdef float dpi2px(value, ext):
# 1in = 2.54cm = 25.4mm = 72pt = 12pc
global g_dpi, g_density, g_fontscale
@ -201,7 +203,6 @@ cpdef float dpi2px(value, ext):
elif ext == 'mm':
return rv * g_dpi / 25.4
cdef class Property:
'''Base class for building more complex properties.
@ -1081,3 +1082,84 @@ cdef class AliasProperty(Property):
if obj.__storage[self._name]['setter'](obj, value):
obj.__storage[self._name]['value'] = self.get(obj)
self.dispatch(obj)
cdef class CssListProperty(Property):
'''A ListProperty that mimics the css way of defining numeric values such
as padding, margin, etc.
Accepts a list of 1, 2 or 4 Numeric arguments or a single Numeric argument.
CssListProperty([1]) represents [1, 1, 1, 1].
CssListProperty([1, 2]) represents [1, 1, 2, 2].
CssListProperty(['1px', (2, 'px'), 3, 4.0]) represents [1, 2, 3, 4.0].
CssListProperty(5) represents [5, 5, 5, 5].
.. versionadded:: 1.7.0
'''
def __init__(self, defaultvalue=None, **kw):
defaultvalue = defaultvalue or [0, 0, 0, 0]
super(CssListProperty, self).__init__(defaultvalue, **kw)
cdef check(self, EventDispatcher obj, value):
if Property.check(self, obj, value):
return True
if type(value) not in (int, float, long, list, tuple, str):
err = '%s.%s accepts only int/float/long/list/tuple/str (got %r)'
raise ValueError(err % (obj.__class__.__name__, self.name, value))
cdef convert(self, EventDispatcher obj, x):
if x is None:
return x
tp = type(x)
if tp is list or tp is tuple:
l = len(x)
if l == 1:
y = self._convert_numeric(obj, x[0])
return [y, y, y, y]
elif l == 2:
if x[1] in NUMERIC_FORMATS:
# defaultvalue is a list or tuple representing one value
y = self._convert_numeric(obj, x)
return [y, y, y, y]
else:
y = self._convert_numeric(obj, x[0])
z = self._convert_numeric(obj, x[1])
return [y, y, z, z]
elif l == 4:
return [self._convert_numeric(obj, y) for y in x]
else:
err = '%s.%s must have 1, 2 or 4 components (got %r)'
raise ValueError(err % (obj.__class__.__name__, self.name, x))
elif tp is int or tp is long or tp is float or tp is str:
y = self._convert_numeric(obj, x)
return [y, y, y, y]
else:
raise ValueError('%s.%s has an invalid format (got %r)' % (
obj.__class__.__name__,
self.name, x))
cdef _convert_numeric(self, EventDispatcher obj, x):
tp = type(x)
if tp is int or tp is float or tp is long:
return x
if tp is tuple or tp is list:
if len(x) != 2:
raise ValueError('%s.%s must have 2 components (got %r)' % (
obj.__class__.__name__,
self.name, x))
return self.parse_list(obj, x[0], <str>x[1])
elif tp is str:
return self.parse_str(obj, x)
else:
raise ValueError('%s.%s have an invalid format (got %r)' % (
obj.__class__.__name__,
self.name, x))
cdef float parse_str(self, EventDispatcher obj, value):
return self.parse_list(obj, value[:-2], <str>value[-2:])
cdef float parse_list(self, EventDispatcher obj, value, str ext):
return dpi2px(value, ext)