1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
|
from config import settings
class _UNIT(str): pass
# for now, we can't use _UNIT() yet; there is some code to be modified first
UNIT_CM = "cm" #_UNIT("cm")
UNIT_IN = "in" #_UNIT("in")
UNIT_PERCENT = "%" #_UNIT("%")
UNIT_PT = "pt" #_UNIT("pt")
UNIT_PX = "px" #_UNIT("px")
def _round(v): return (v < 0) and int(v - 0.5) or int(v + 0.5)
class Unit(object):
"""
Class for coordinate and size values with units. This class abstracts from
actual lengths with concrete units.
"""
__slots__ = ('__is_unset', '__unit', '__cache_px', '__cache_pt',
'__cache_cm', '__cache_in', '__cache_pcnt', '__in2pcnt',
'__original_value', '__value',
'__IN2PT_SCALE', '__PT2IN_SCALE', '__IN2PX_SCALE',
'__PX2IN_SCALE', '__IN2CM_SCALE', '__CM2IN_SCALE')
def __init__(self, value = None, unit = None, string = None):
# scaling factors
self.__IN2PT_SCALE = 72.0
self.__PT2IN_SCALE = 1 / self.__IN2PT_SCALE
self.__IN2PX_SCALE = float(settings.dpi)
self.__PX2IN_SCALE = 1 / self.__IN2PX_SCALE
self.__IN2CM_SCALE = 2.54
self.__CM2IN_SCALE = 1 / self.__IN2CM_SCALE
if (not string): string = None
if (value == unit == string == None):
value, unit = 0, UNIT_PX
self.__is_unset = True
else:
self.__is_unset = False
if (string):
if (string[-1] == "%"):
value = float(string[:-1])
unit = UNIT_PERCENT
elif (string[-2:] == "cm"):
value = float(string[:-2])
unit = UNIT_CM
elif (string[-2:] == "in"):
value = float(string[:-2])
unit = UNIT_IN
elif (string[-2:] == "pt"):
value = float(string[:-2])
unit = UNIT_PT
else:
value = int(float(string))
unit = UNIT_PX
# the unit type
self.__unit = unit
# cache for computed values
self.__cache_px = (unit != UNIT_PX) and value or None
self.__cache_pt = (unit == UNIT_PT) and value or None
self.__cache_cm = (unit == UNIT_CM) and value or None
self.__cache_in = (unit == UNIT_IN) and value or None
self.__cache_pcnt = (unit == UNIT_PERCENT) and value or None
# the scaling factor from inches to percents
self.__in2pcnt = 0.1
self.__validate(self.__unit)
# store value in inches
self.__original_value = value
self.__value = self.__unit_to_inch(value, unit)
self.set_100_percent(100)
#
# Validates the unit type.
#
def __validate(self, unit):
if (unit not in (UNIT_PX, UNIT_CM, UNIT_IN, UNIT_PT, UNIT_PERCENT)):
raise TypeError("%s is not a valid UNIT." % (unit,))
def __unit_to_inch(self, value, unit):
# trust no one
self.__validate(unit)
if (unit == UNIT_IN): return value
elif (unit == UNIT_CM): return value * self.__CM2IN_SCALE
elif (unit == UNIT_PX): return value * self.__PX2IN_SCALE
elif (unit == UNIT_PT): return value * self.__PT2IN_SCALE
# percentual values get converted at a later time
elif (unit == UNIT_PERCENT): return value
def __inch_to_unit(self, value, unit):
# trust no one
self.__validate(unit)
if (unit == UNIT_IN):
v = value
self.__cache_in = v
elif (unit == UNIT_CM):
v = value * self.__IN2CM_SCALE
self.__cache_cm = v
elif (unit == UNIT_PX):
v = _round(value * self.__IN2PX_SCALE)
self.__cache_px = v
elif (unit == UNIT_PT):
v = value * self.__IN2PT_SCALE
self.__cache_pt = v
elif (unit == UNIT_PERCENT):
v = value * self.__in2pcnt
self.__cache_pcnt = v
return v
def set_100_percent(self, size):
#assert (size > 0)
if (size <= 0): size = 1
# size is a value in pixels, therefore convert it to inches
size = self.__unit_to_inch(size, UNIT_PX)
if (self.__unit == UNIT_PERCENT):
self.__value = (self.__original_value) * (size / 100.0)
self.__in2pcnt = 100.0 / size
# invalidate cached values
self.__cache_pcnt = None
self.__cache_px = None
self.__cache_pt = None
self.__cache_cm = None
self.__cache_in = None
def as_px(self):
return self.__cache_px or self.__inch_to_unit(self.__value, UNIT_PX)
def as_pt(self):
return self.__cache_pt or self.__inch_to_unit(self.__value, UNIT_PT)
def as_cm(self):
return self.__cache_cm or self.__inch_to_unit(self.__value, UNIT_CM)
def as_in(self):
return self.__cache_in or self.__inch_to_unit(self.__value, UNIT_IN)
def as_percent(self):
return self.__cache_pcnt or self.__inch_to_unit(self.__value,
UNIT_PERCENT)
def get_unit(self): return self.__unit
def is_unset(self): return self.__is_unset
#
# Creates a new Unit object as a copy of this one.
# TODO: copy the set_100_percent value as well
#
def copy(self):
new = Unit(self.as_pt(), UNIT_PT)
return new
def __getitem__(self, unit):
log("Deprecation:\n"
"Accessing the Unit object as a dictionary is deprecated. "
"Please use the appropriate accessor methods instead.", True)
return self.__inch_to_unit(self.__value, unit)
def __add__(self, other):
return Unit(self.as_pt() + other.as_pt(), UNIT_PT)
def __sub__(self, other):
return Unit(self.as_pt() - other.as_pt(), UNIT_PT)
def __mul__(self, other):
if (isinstance(other, Unit)):
return Unit(self.as_pt() * other.as_pt(), UNIT_PT)
else:
return Unit(self.as_pt() * other, UNIT_PT)
def __div__(self, other):
if (isinstance(other, Unit)):
return Unit(self.as_pt() / other.as_pt(), UNIT_PT)
else:
return Unit(self.as_pt() / other, UNIT_PT)
def __cmp__(self, other):
assert (other == None or isinstance(other, Unit)), \
"Cannot compare with non-Unit object"
if (not other): return 1
else: return cmp(int(self.as_pt()), int(other.as_pt()))
def __repr__(self):
unit = self.get_unit()
if (self.is_unset()): return "unset"
if (unit == UNIT_CM): return "%fcm" % self.as_cm()
elif (unit == UNIT_IN): return "%fin" % self.as_in()
elif (unit == UNIT_PT): return "%fpt" % self.as_pt()
elif (unit == UNIT_PX): return "%f" % self.as_px()
elif (unit == UNIT_PERCENT): return "%f%%" % self.as_percent()
def __hash__(self):
return hash(repr(self))
import sys
# for convenience
ZERO = Unit(0, UNIT_PT)
ONE = Unit(1, UNIT_PT)
MAXIMUM = Unit(sys.maxint, UNIT_PT)
UNSET = Unit()
if (__name__ == "__main__"):
u = Unit(10, UNIT_PX)
u.set_100_percent(11)
print u.as_px()
print u.as_in()
print u.as_percent()
|