Changeset 1022
- Timestamp:
- 11/30/11 17:32:55 (18 months ago)
- Location:
- packages/defcon/branches/ufo3
- Files:
-
- 21 edited
-
Lib/defcon/objects/anchor.py (modified) (7 diffs)
-
Lib/defcon/objects/base.py (modified) (4 diffs)
-
Lib/defcon/objects/component.py (modified) (6 diffs)
-
Lib/defcon/objects/contour.py (modified) (7 diffs)
-
Lib/defcon/objects/dataSet.py (modified) (4 diffs)
-
Lib/defcon/objects/features.py (modified) (3 diffs)
-
Lib/defcon/objects/font.py (modified) (14 diffs)
-
Lib/defcon/objects/glyph.py (modified) (17 diffs)
-
Lib/defcon/objects/groups.py (modified) (2 diffs)
-
Lib/defcon/objects/guideline.py (modified) (13 diffs)
-
Lib/defcon/objects/image.py (modified) (9 diffs)
-
Lib/defcon/objects/imageSet.py (modified) (6 diffs)
-
Lib/defcon/objects/info.py (modified) (7 diffs)
-
Lib/defcon/objects/kerning.py (modified) (2 diffs)
-
Lib/defcon/objects/layer.py (modified) (23 diffs)
-
Lib/defcon/objects/layerSet.py (modified) (12 diffs)
-
Lib/defcon/objects/lib.py (modified) (3 diffs)
-
Lib/defcon/objects/uniData.py (modified) (3 diffs)
-
Lib/defcon/pens/glyphObjectPointPen.py (modified) (2 diffs)
-
Lib/defcon/test/testAll.py (modified) (2 diffs)
-
tools/infoObjectGenerator.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
packages/defcon/branches/ufo3/Lib/defcon/objects/anchor.py
r1016 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 from defcon.objects.color import Color … … 29 30 representationFactories = {} 30 31 31 def __init__(self, anchorDict=None): 32 def __init__(self, glyph=None, anchorDict=None): 33 self._glyph = None 34 self.glyph = glyph 32 35 super(Anchor, self).__init__() 36 self.beginSelfNotificationObservation() 33 37 self._dirty = False 34 38 if anchorDict is not None: … … 41 45 # parents 42 46 47 def getParent(self): 48 return self.glyph 49 43 50 def _get_font(self): 44 51 glyph = self.glyph … … 66 73 67 74 def _get_glyph(self): 68 return self.getParent() 69 70 glyph = property(_get_glyph, doc="The :class:`Glyph` that this anchor belongs to.") 75 if self._glyph is None: 76 return None 77 return self._glyph() 78 79 def _set_glyph(self, glyph): 80 assert self._glyph is None 81 if glyph is not None: 82 glyph = weakref.ref(glyph) 83 self._glyph = glyph 84 85 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this anchor belongs to. This should not be set externally.") 71 86 72 87 # coordinates … … 171 186 self.identifier = identifier 172 187 173 # ---- ---174 # M ethods175 # ---- ---188 # ---- 189 # Move 190 # ---- 176 191 177 192 def move(self, (x, y)): … … 183 198 self.x += x 184 199 self.y += y 200 201 # ------------------------ 202 # Notification Observation 203 # ------------------------ 204 205 def endSelfNotificationObservation(self): 206 super(Anchor, self).endSelfNotificationObservation() 207 self._glyph = None 185 208 186 209 … … 229 252 False 230 253 231 >>> a = Anchor( dict(x=1, y=2, name="3", identifier="4", color="1,1,1,1"))254 >>> a = Anchor(anchorDict=dict(x=1, y=2, name="3", identifier="4", color="1,1,1,1")) 232 255 >>> a.x, a.y, a.name, a.identifier, a.color 233 256 (1, 2, '3', '4', '1,1,1,1') -
packages/defcon/branches/ufo3/Lib/defcon/objects/base.py
r1005 r1022 39 39 40 40 def _init(self): 41 self._parent = None42 41 self._dispatcher = None 43 42 self._dataOnDisk = None … … 45 44 self._representations = {} 46 45 46 def __del__(self): 47 self.endSelfNotificationObservation() 48 47 49 # ------ 48 50 # Parent 49 51 # ------ 50 52 51 def setParent(self, obj):52 """53 Set the parent of the object. This will reference the parent using weakref.54 """55 if obj is None:56 self._parent = None57 else:58 self._parent = weakref.ref(obj)59 60 53 def getParent(self): 61 """ 62 Get the parent. Returns None if no parent is set. 63 Note that because the reference to the parent is stored 64 as a weakref, the parent can disappear if it is no longer 65 referenced by any object other than this one. 66 """ 67 if self._parent is not None: 68 return self._parent() 69 return None 54 raise NotImplementedError 70 55 71 56 # ------------- … … 74 59 75 60 def _get_dispatcher(self): 76 parent = self.getParent() 77 if parent is None: 61 if not hasattr(self, "font"): 62 return 63 font = self.font 64 if font is None: 78 65 return None 79 return parent.dispatcher66 return font.dispatcher 80 67 81 68 dispatcher = property(_get_dispatcher, doc="The :class:`defcon.tools.notifications.NotificationCenter` assigned to the parent of this object.") … … 214 201 if dispatcher is not None: 215 202 dispatcher.postNotification(notification=notification, observable=self, data=data) 203 204 # ------------------------ 205 # Notification Observation 206 # ------------------------ 207 208 def beginSelfNotificationObservation(self): 209 if self.dispatcher is None: 210 return 211 self.addObserver(self, "selfNotificationCallback", notification=None) 212 213 def endSelfNotificationObservation(self): 214 if self.dispatcher is None: 215 return 216 self.removeObserver(self, notification=None) 217 218 def selfNotificationCallback(self, notification): 219 pass 216 220 217 221 # --------------- -
packages/defcon/branches/ufo3/Lib/defcon/objects/component.py
r1017 r1022 27 27 representationFactories = {} 28 28 29 def __init__(self): 29 def __init__(self, glyph=None): 30 self._glyph = None 31 self.glyph = glyph 30 32 super(Component, self).__init__() 33 self.beginSelfNotificationObservation() 31 34 self._dirty = False 32 35 self._baseGlyph = None … … 40 43 # parents 41 44 45 def getParent(self): 46 return self.glyph 47 42 48 def _get_font(self): 43 49 glyph = self.glyph … … 65 71 66 72 def _get_glyph(self): 67 return self.getParent() 68 69 glyph = property(_get_glyph, doc="The :class:`Glyph` that this component belongs to.") 73 if self._glyph is None: 74 return None 75 return self._glyph() 76 77 def _set_glyph(self, glyph): 78 assert self._glyph is None 79 if glyph is not None: 80 glyph = weakref.ref(glyph) 81 self._glyph = glyph 82 83 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this component belongs to. This should not be set externally.") 70 84 71 85 # bounds … … 151 165 warn("The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.", DeprecationWarning) 152 166 153 # ---- ---154 # M ethods155 # ---- ---167 # ---- 168 # Move 169 # ---- 156 170 157 171 def move(self, (x, y)): … … 165 179 yOffset += y 166 180 self.transformation = (xScale, xyScale, yxScale, yScale, xOffset, yOffset) 181 182 # ------------ 183 # Point Inside 184 # ------------ 167 185 168 186 def pointInside(self, (x, y), evenOdd=False): … … 228 246 self.identifier = identifier 229 247 248 # ------------------------ 249 # Notification Observation 250 # ------------------------ 251 252 def endSelfNotificationObservation(self): 253 super(Component, self).endSelfNotificationObservation() 254 self._glyph = None 255 230 256 231 257 def _testIdentifier(): -
packages/defcon/branches/ufo3/Lib/defcon/objects/contour.py
r1018 r1022 42 42 representationFactories = {} 43 43 44 def __init__(self, pointClass=None): 44 def __init__(self, glyph=None, pointClass=None): 45 self._glyph = None 46 self.glyph = glyph 45 47 super(Contour, self).__init__() 48 self.beginSelfNotificationObservation() 46 49 self._points = [] 47 50 self._boundsCache = None … … 58 61 self._controlPointBoundsCache = None 59 62 60 # ---------- 61 # Attributes 62 # ---------- 63 64 # parents 63 def __del__(self): 64 super(Contour, self).__del__() 65 self._points = None 66 67 # -------------- 68 # Parent Objects 69 # -------------- 70 71 def getParent(self): 72 return self.glyph 65 73 66 74 def _get_font(self): … … 89 97 90 98 def _get_glyph(self): 91 return self.getParent() 92 93 glyph = property(_get_glyph, doc="The :class:`Glyph` that this contour belongs to.") 99 if self._glyph is None: 100 return None 101 return self._glyph() 102 103 def _set_glyph(self, glyph): 104 assert self._glyph is None 105 if glyph is not None: 106 glyph = weakref.ref(glyph) 107 self._glyph = glyph 108 109 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this contour belongs to. This should not be set externally.") 110 111 # ------ 112 # Points 113 # ------ 94 114 95 115 def _get_pointClass(self): … … 98 118 pointClass = property(_get_pointClass, doc="The class used for point.") 99 119 100 def _get_bounds(self):101 from robofab.pens.boundsPen import BoundsPen102 if self._boundsCache is None:103 pen = BoundsPen(None)104 self.draw(pen)105 self._boundsCache = pen.bounds106 return self._boundsCache107 108 bounds = property(_get_bounds, doc="The bounds of the contour's outline expressed as a tuple of form (xMin, yMin, xMax, yMax).")109 110 def _get_controlPointBounds(self):111 from fontTools.pens.boundsPen import ControlBoundsPen112 if self._controlPointBoundsCache is None:113 pen = ControlBoundsPen(None)114 self.draw(pen)115 self._controlPointBoundsCache = pen.bounds116 return self._controlPointBoundsCache117 118 controlPointBounds = property(_get_controlPointBounds, doc="The control bounds of all points in the contour. This only measures the point positions, it does not measure curves. So, curves without points at the extrema will not be properly measured.")119 120 def _get_clockwise(self):121 from defcon.pens.clockwiseTestPointPen import ClockwiseTestPointPen122 if self._clockwiseCache is None:123 pen = ClockwiseTestPointPen()124 self.drawPoints(pen)125 self._clockwiseCache = pen.getIsClockwise()126 return self._clockwiseCache127 128 def _set_clockwise(self, value):129 if self.clockwise != value:130 self.reverse()131 self._clockwiseCache = None132 133 clockwise = property(_get_clockwise, _set_clockwise, doc="A boolean representing if the contour has a clockwise direction. Setting this posts *Contour.WindingDirectionChanged* and *Contour.Changed* notifications.")134 135 def _get_open(self):136 if not self._points:137 return True138 return self._points[0].segmentType == 'move'139 140 open = property(_get_open, doc="A boolean indicating if the contour is open or not.")141 142 120 def _get_onCurvePoints(self): 143 121 return [point for point in self._points if point.segmentType] 144 122 145 123 onCurvePoints = property(_get_onCurvePoints, doc="A list of all on curve points in the contour.") 124 125 def appendPoint(self, point): 126 """ 127 Append **point** to the glyph. The point must be a defcon 128 :class:`Point` object or a subclass of that object. An error 129 will be raised if the point's identifier conflicts with any of 130 the identifiers within the glyph. 131 132 This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 133 """ 134 assert point not in self._points 135 self.insertPoint(len(self._points), point) 136 137 def insertPoint(self, index, point): 138 """ 139 Insert **point** into the contour at index. The point 140 must be a defcon :class:`Point` object or a subclass 141 of that object. An error will be raised if the points's 142 identifier conflicts with any of the identifiers within 143 the glyph. 144 145 This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 146 """ 147 assert point not in self._points 148 if point.identifier is not None: 149 identifiers = self.identifiers 150 assert point.identifier not in identifiers 151 if point.identifier is not None: 152 identifiers.add(point.identifier) 153 self._points.insert(index, point) 154 self._destroyBoundsCache() 155 self._clockwiseCache = None 156 self.postNotification("Contour.PointsChanged") 157 self.dirty = True 158 159 def setStartPoint(self, index): 160 """ 161 Set the point at **index** as the first point in the contour. 162 This point must be an on-curve point. 163 164 This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 165 """ 166 onCurvePoints = self.onCurvePoints 167 if len(onCurvePoints) < 2: 168 return 169 if self.open: 170 return 171 point = self._points[index] 172 assert point.segmentType is not None, "index must represent an on curve point" 173 before = self._points[:index] 174 self._points = self._points[index:] + before 175 self.postNotification("Contour.PointsChanged") 176 self.dirty = True 177 178 # ------------- 179 # List Behavior 180 # ------------- 181 182 def __len__(self): 183 return len(self._points) 184 185 def __getitem__(self, index): 186 if index > len(self._points): 187 raise IndexError 188 return self._points[index] 189 190 def __iter__(self): 191 pointCount = len(self) 192 index = 0 193 while index < pointCount: 194 point = self[index] 195 yield point 196 index += 1 197 198 def clear(self): 199 """ 200 Clear the contents of the contour. 201 202 This posts *Contour.PointsChanged* and *Contour.Changed* notifications. 203 """ 204 self._clear() 205 206 def _clear(self, postNotification=True): 207 # clear the internal storage 208 self._points = [] 209 # reset the clockwise cache 210 self._clockwiseCache = None 211 # post a dirty notification 212 if postNotification: 213 self.postNotification("Contour.PointsChanged") 214 self.dirty = True 215 216 def index(self, point): 217 """ 218 Get the index for **point**. 219 """ 220 return self._points.index(point) 221 222 def reverse(self): 223 """ 224 Reverse the direction of the contour. It's important to note 225 that the actual points stored in this object will be completely 226 repalced by new points. 227 228 This will post *Contour.WindingDirectionChanged*, 229 *Contour.PointsChanged* and *Contour.Changed* notifications. 230 """ 231 from robofab.pens.reverseContourPointPen import ReverseContourPointPen 232 oldDirection = self.clockwise 233 # put the current points in another contour 234 otherContour = self.__class__(glyph=None, pointClass=self.pointClass) 235 # draw the points in this contour through 236 # the reversing pen. 237 reversePen = ReverseContourPointPen(otherContour) 238 self.drawPoints(reversePen) 239 # clear the points in this contour 240 self._clear(postNotification=False) 241 # draw the points back into this contour 242 self.disableNotifications() 243 otherContour.drawPoints(self) 244 self.enableNotifications() 245 # post a notification 246 self.postNotification("Contour.WindingDirectionChanged", data=dict(oldValue=oldDirection, newValue=self.clockwise)) 247 self.postNotification("Contour.PointsChanged") 248 self.dirty = True 249 250 # -------- 251 # Segments 252 # -------- 146 253 147 254 def _get_segments(self): … … 169 276 170 277 segments = property(_get_segments, doc="A list of all points in the contour organized into segments.") 171 172 # -------173 # Methods174 # -------175 176 def __len__(self):177 return len(self._points)178 179 def __getitem__(self, index):180 if index > len(self._points):181 raise IndexError182 return self._points[index]183 184 def __iter__(self):185 pointCount = len(self)186 index = 0187 while index < pointCount:188 point = self[index]189 yield point190 index += 1191 192 def clear(self):193 """194 Clear the contents of the contour.195 196 This posts *Contour.PointsChanged* and *Contour.Changed* notifications.197 """198 self._clear()199 200 def _clear(self, postNotification=True):201 # clear the internal storage202 self._points = []203 # reset the clockwise cache204 self._clockwiseCache = None205 # post a dirty notification206 if postNotification:207 self.postNotification("Contour.PointsChanged")208 self.dirty = True209 210 def appendPoint(self, point):211 """212 Append **point** to the glyph. The point must be a defcon213 :class:`Point` object or a subclass of that object. An error214 will be raised if the point's identifier conflicts with any of215 the identifiers within the glyph.216 217 This will post *Contour.PointsChanged* and *Contour.Changed* notifications.218 """219 assert point not in self._points220 self.insertPoint(len(self._points), point)221 222 def insertPoint(self, index, point):223 """224 Insert **point** into the contour at index. The point225 must be a defcon :class:`Point` object or a subclass226 of that object. An error will be raised if the points's227 identifier conflicts with any of the identifiers within228 the glyph.229 230 This will post *Contour.PointsChanged* and *Contour.Changed* notifications.231 """232 assert point not in self._points233 if point.identifier is not None:234 identifiers = self.identifiers235 assert point.identifier not in identifiers236 if point.identifier is not None:237 identifiers.add(point.identifier)238 self._points.insert(index, point)239 self._destroyBoundsCache()240 self._clockwiseCache = None241 self.postNotification("Contour.PointsChanged")242 self.dirty = True243 244 def reverse(self):245 """246 Reverse the direction of the contour. It's important to note247 that the actual points stored in this object will be completely248 repalced by new points.249 250 This will post *Contour.WindingDirectionChanged*,251 *Contour.PointsChanged* and *Contour.Changed* notifications.252 """253 from robofab.pens.reverseContourPointPen import ReverseContourPointPen254 oldDirection = self.clockwise255 # put the current points in another contour256 otherContour = self.__class__(self.pointClass)257 # draw the points in this contour through258 # the reversing pen.259 reversePen = ReverseContourPointPen(otherContour)260 self.drawPoints(reversePen)261 # clear the points in this contour262 self._clear(postNotification=False)263 # draw the points back into this contour264 self.disableNotifications()265 otherContour.drawPoints(self)266 self.enableNotifications()267 # post a notification268 self.postNotification("Contour.WindingDirectionChanged", data=dict(oldValue=oldDirection, newValue=self.clockwise))269 self.postNotification("Contour.PointsChanged")270 self.dirty = True271 272 def move(self, (x, y)):273 """274 Move all points in the contour by **(x, y)**.275 276 This will post *Contour.PointsChanged* and *Contour.Changed* notifications.277 """278 for point in self._points:279 point.move((x, y))280 # update the bounds cache281 if self._boundsCache:282 xMin, yMin, xMax, yMax = self._boundsCache283 xMin += x284 yMin += y285 xMax += x286 yMax += y287 self._boundsCache = (xMin, yMin, xMax, yMax)288 if self._controlPointBoundsCache:289 xMin, yMin, xMax, yMax = self._controlPointBoundsCache290 xMin += x291 yMin += y292 xMax += x293 yMax += y294 self._controlPointBoundsCache = (xMin, yMin, xMax, yMax)295 self.postNotification("Contour.PointsChanged")296 self.dirty = True297 298 def pointInside(self, (x, y), evenOdd=False):299 """300 Returns a boolean indicating if **(x, y)** is in the301 "black" area of the contour.302 """303 from fontTools.pens.pointInsidePen import PointInsidePen304 pen = PointInsidePen(glyphSet=None, testPoint=(x, y), evenOdd=evenOdd)305 self.draw(pen)306 return pen.getResult()307 308 def index(self, point):309 """310 Get the index for **point**.311 """312 return self._points.index(point)313 314 def setStartPoint(self, index):315 """316 Set the point at **index** as the first point in the contour.317 This point must be an on-curve point.318 319 This will post *Contour.PointsChanged* and *Contour.Changed* notifications.320 """321 onCurvePoints = self.onCurvePoints322 if len(onCurvePoints) < 2:323 return324 if self.open:325 return326 point = self._points[index]327 assert point.segmentType is not None, "index must represent an on curve point"328 before = self._points[:index]329 self._points = self._points[index:] + before330 self.postNotification("Contour.PointsChanged")331 self.dirty = True332 333 def positionForProspectivePointInsertionAtSegmentAndT(self, segmentIndex, t):334 """335 Get the precise coordinates and a boolean indicating336 if the point will be smooth for the given **segmentIndex**337 and **t**.338 """339 return self._splitAndInsertAtSegmentAndT(segmentIndex, t, False)340 341 def splitAndInsertPointAtSegmentAndT(self, segmentIndex, t):342 """343 Insert a point into the contour for the given344 **segmentIndex** and **t**.345 346 This posts a *Contour.Changed* notification.347 """348 self._splitAndInsertAtSegmentAndT(segmentIndex, t, True)349 350 def _splitAndInsertAtSegmentAndT(self, segmentIndex, t, insert):351 segments = self.segments352 segment = segments[segmentIndex]353 segment.insert(0, segments[segmentIndex-1][-1])354 firstPoint = segment[0]355 lastPoint = segment[-1]356 segmentType = lastPoint.segmentType357 segment = [(point.x, point.y) for point in segment]358 if segmentType == "line":359 (x1, y1), (x2, y2) = segment360 x = x1 + (x2 - x1) * t361 y = y1 + (y2 - y1) * t362 pointsToInsert = [((x, y), "line", False)]363 insertionPoint = (x, y)364 pointWillBeSmooth = False365 elif segmentType == "curve":366 pt1, pt2, pt3, pt4 = segment367 (pt1, pt2, pt3, pt4), (pt5, pt6, pt7, pt8) = bezierTools.splitCubicAtT(pt1, pt2, pt3, pt4, t)368 pointsToInsert = [(pt2, None, False), (pt3, None, False), (pt4, "curve", True), (pt6, None, False), (pt7, None, False)]369 insertionPoint = tuple(pt4)370 pointWillBeSmooth = True371 else:372 # XXX could be a quad. in that case, we could handle it.373 raise NotImplementedError("unknown segment type: %s" % segmentType)374 if insert:375 firstPointIndex = self._points.index(firstPoint)376 lastPointIndex = self._points.index(lastPoint)377 firstPoints = self._points[:firstPointIndex + 1]378 if firstPointIndex == len(self._points) - 1:379 firstPoints = firstPoints[lastPointIndex:]380 lastPoints = []381 elif lastPointIndex == 0:382 lastPoints = []383 else:384 lastPoints = self._points[lastPointIndex:]385 newPoints = [self._pointClass(pos, segmentType=segmentType, smooth=smooth) for pos, segmentType, smooth in pointsToInsert]386 self._points = firstPoints + newPoints + lastPoints387 self.dirty = True388 return insertionPoint, pointWillBeSmooth389 278 390 279 def removeSegment(self, segmentIndex, preserveCurve=False): … … 472 361 self.dirty = True 473 362 363 # ---------------- 364 # Basic Attributes 365 # ---------------- 366 367 # clockwise 368 369 def _get_clockwise(self): 370 from defcon.pens.clockwiseTestPointPen import ClockwiseTestPointPen 371 if self._clockwiseCache is None: 372 pen = ClockwiseTestPointPen() 373 self.drawPoints(pen) 374 self._clockwiseCache = pen.getIsClockwise() 375 return self._clockwiseCache 376 377 def _set_clockwise(self, value): 378 if self.clockwise != value: 379 self.reverse() 380 self._clockwiseCache = None 381 382 clockwise = property(_get_clockwise, _set_clockwise, doc="A boolean representing if the contour has a clockwise direction. Setting this posts *Contour.WindingDirectionChanged* and *Contour.Changed* notifications.") 383 384 # open 385 386 def _get_open(self): 387 if not self._points: 388 return True 389 return self._points[0].segmentType == 'move' 390 391 open = property(_get_open, doc="A boolean indicating if the contour is open or not.") 392 393 # ------ 394 # Bounds 395 # ------ 396 397 def _get_bounds(self): 398 from robofab.pens.boundsPen import BoundsPen 399 if self._boundsCache is None: 400 pen = BoundsPen(None) 401 self.draw(pen) 402 self._boundsCache = pen.bounds 403 return self._boundsCache 404 405 bounds = property(_get_bounds, doc="The bounds of the contour's outline expressed as a tuple of form (xMin, yMin, xMax, yMax).") 406 407 def _get_controlPointBounds(self): 408 from fontTools.pens.boundsPen import ControlBoundsPen 409 if self._controlPointBoundsCache is None: 410 pen = ControlBoundsPen(None) 411 self.draw(pen) 412 self._controlPointBoundsCache = pen.bounds 413 return self._controlPointBoundsCache 414 415 controlPointBounds = property(_get_controlPointBounds, doc="The control bounds of all points in the contour. This only measures the point positions, it does not measure curves. So, curves without points at the extrema will not be properly measured.") 416 417 # ---- 418 # Move 419 # ---- 420 421 def move(self, (x, y)): 422 """ 423 Move all points in the contour by **(x, y)**. 424 425 This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 426 """ 427 for point in self._points: 428 point.move((x, y)) 429 # update the bounds cache 430 if self._boundsCache: 431 xMin, yMin, xMax, yMax = self._boundsCache 432 xMin += x 433 yMin += y 434 xMax += x 435 yMax += y 436 self._boundsCache = (xMin, yMin, xMax, yMax) 437 if self._controlPointBoundsCache: 438 xMin, yMin, xMax, yMax = self._controlPointBoundsCache 439 xMin += x 440 yMin += y 441 xMax += x 442 yMax += y 443 self._controlPointBoundsCache = (xMin, yMin, xMax, yMax) 444 self.postNotification("Contour.PointsChanged") 445 self.dirty = True 446 447 # ------------ 448 # Point Inside 449 # ------------ 450 451 def pointInside(self, (x, y), evenOdd=False): 452 """ 453 Returns a boolean indicating if **(x, y)** is in the 454 "black" area of the contour. 455 """ 456 from fontTools.pens.pointInsidePen import PointInsidePen 457 pen = PointInsidePen(glyphSet=None, testPoint=(x, y), evenOdd=evenOdd) 458 self.draw(pen) 459 return pen.getResult() 460 461 # --------- 462 # Splitting 463 # --------- 464 465 def positionForProspectivePointInsertionAtSegmentAndT(self, segmentIndex, t): 466 """ 467 Get the precise coordinates and a boolean indicating 468 if the point will be smooth for the given **segmentIndex** 469 and **t**. 470 """ 471 return self._splitAndInsertAtSegmentAndT(segmentIndex, t, False) 472 473 def splitAndInsertPointAtSegmentAndT(self, segmentIndex, t): 474 """ 475 Insert a point into the contour for the given 476 **segmentIndex** and **t**. 477 478 This posts a *Contour.Changed* notification. 479 """ 480 self._splitAndInsertAtSegmentAndT(segmentIndex, t, True) 481 482 def _splitAndInsertAtSegmentAndT(self, segmentIndex, t, insert): 483 segments = self.segments 484 segment = segments[segmentIndex] 485 segment.insert(0, segments[segmentIndex-1][-1]) 486 firstPoint = segment[0] 487 lastPoint = segment[-1] 488 segmentType = lastPoint.segmentType 489 segment = [(point.x, point.y) for point in segment] 490 if segmentType == "line": 491 (x1, y1), (x2, y2) = segment 492 x = x1 + (x2 - x1) * t 493 y = y1 + (y2 - y1) * t 494 pointsToInsert = [((x, y), "line", False)] 495 insertionPoint = (x, y) 496 pointWillBeSmooth = False 497 elif segmentType == "curve": 498 pt1, pt2, pt3, pt4 = segment 499 (pt1, pt2, pt3, pt4), (pt5, pt6, pt7, pt8) = bezierTools.splitCubicAtT(pt1, pt2, pt3, pt4, t) 500 pointsToInsert = [(pt2, None, False), (pt3, None, False), (pt4, "curve", True), (pt6, None, False), (pt7, None, False)] 501 insertionPoint = tuple(pt4) 502 pointWillBeSmooth = True 503 else: 504 # XXX could be a quad. in that case, we could handle it. 505 raise NotImplementedError("unknown segment type: %s" % segmentType) 506 if insert: 507 firstPointIndex = self._points.index(firstPoint) 508 lastPointIndex = self._points.index(lastPoint) 509 firstPoints = self._points[:firstPointIndex + 1] 510 if firstPointIndex == len(self._points) - 1: 511 firstPoints = firstPoints[lastPointIndex:] 512 lastPoints = [] 513 elif lastPointIndex == 0: 514 lastPoints = [] 515 else: 516 lastPoints = self._points[lastPointIndex:] 517 newPoints = [self._pointClass(pos, segmentType=segmentType, smooth=smooth) for pos, segmentType, smooth in pointsToInsert] 518 self._points = firstPoints + newPoints + lastPoints 519 self.dirty = True 520 return insertionPoint, pointWillBeSmooth 521 474 522 # ----------- 475 523 # Pen methods … … 577 625 point.identifier = identifier 578 626 self.dirty = True 627 628 # ------------------------ 629 # Notification Observation 630 # ------------------------ 631 632 def endSelfNotificationObservation(self): 633 super(Contour, self).endSelfNotificationObservation() 634 self._glyph = None 635 579 636 580 637 # ----- -
packages/defcon/branches/ufo3/Lib/defcon/objects/dataSet.py
r1016 r1022 1 1 import os 2 import weakref 2 3 from ufoLib import UFOReader, UFOLibError 3 4 from defcon.objects.base import BaseObject … … 21 22 """ 22 23 24 changeNotificationName = "DataSet.Changed" 23 25 representationFactories = {} 24 26 25 def __init__(self, fileNames=None): 27 def __init__(self, font=None): 28 self._font = None 29 if font is not None: 30 self._font = weakref.ref(font) 26 31 super(DataSet, self).__init__() 32 self.beginSelfNotificationObservation() 27 33 self._data = {} 28 34 self._scheduledForDeletion = {} 29 35 36 # -------------- 37 # Parent Objects 38 # -------------- 39 40 def getParent(self): 41 return self.font 42 30 43 def _get_font(self): 31 return self.getParent() 44 if self._font is not None: 45 return self._font() 46 return None 32 47 33 48 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 49 50 # ---------- 51 # File Names 52 # ---------- 34 53 35 54 def _get_fileNames(self): … … 77 96 self.dirty = True 78 97 79 # ---- -----------80 # File Management81 # ---- -----------98 # ---- 99 # Save 100 # ---- 82 101 83 102 def getSaveProgressBarTickCount(self, formatVersion): … … 170 189 data = self[fileName] 171 190 191 # ------------------------ 192 # Notification Observation 193 # ------------------------ 194 195 def endSelfNotificationObservation(self): 196 super(DataSet, self).endSelfNotificationObservation() 197 self._font = None 198 172 199 173 200 def _dataDict(data=None, dirty=False, onDisk=True, onDiskModTime=None): -
packages/defcon/branches/ufo3/Lib/defcon/objects/features.py
r1014 r1022 1 import weakref 1 2 from defcon.objects.base import BaseObject 2 3 … … 20 21 representationFactories = {} 21 22 22 def __init__(self): 23 def __init__(self, font=None): 24 self._font = None 25 if font is not None: 26 self._font = weakref.ref(font) 23 27 super(Features, self).__init__() 28 self.beginSelfNotificationObservation() 24 29 self._dirty = False 25 30 self._text = None 26 31 32 # -------------- 33 # Parent Objects 34 # -------------- 35 36 def getParent(self): 37 return self.font 38 27 39 def _get_font(self): 28 return self.getParent() 40 if self._font is not None: 41 return self._font() 42 return None 29 43 30 44 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 45 46 # ---- 47 # Text 48 # ---- 31 49 32 50 def _set_text(self, value): … … 43 61 text = property(_get_text, _set_text, doc="The raw feature text. Setting this post *Features.TextChanged* and *Features.Changed* notifications.") 44 62 63 # ------------------------ 64 # Notification Observation 65 # ------------------------ 66 67 def endSelfNotificationObservation(self): 68 super(Features, self).endSelfNotificationObservation() 69 self._font = None -
packages/defcon/branches/ufo3/Lib/defcon/objects/font.py
r1019 r1022 70 70 layerSetClass=None, layerClass=None, imageSetClass=None, dataSetClass=None, 71 71 guidelineClass=None, 72 glyphClass=None, glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None): 72 glyphClass=None, glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None, glyphImageClass=None): 73 73 74 super(Font, self).__init__() 75 self._dispatcher = NotificationCenter() 76 self.beginSelfNotificationObservation() 77 74 78 if infoClass is None: 75 79 infoClass = Info … … 88 92 if dataSetClass is None: 89 93 dataSetClass = DataSet 90 91 self._dispatcher = NotificationCenter() 92 94 self._unicodeDataClass = unicodeDataClass 95 self._layerSetClass = layerSetClass 96 self._layerClass = layerClass 97 self._glyphClass = glyphClass 98 self._glyphContourClass = glyphContourClass 99 self._glyphPointClass = glyphPointClass 100 self._glyphComponentClass = glyphComponentClass 101 self._glyphAnchorClass = glyphAnchorClass 102 self._glyphImageClass = glyphImageClass 93 103 self._kerningClass = kerningClass 94 104 self._infoClass = infoClass … … 97 107 self._libClass = libClass 98 108 self._guidelineClass = guidelineClass 109 self._imageSetClass = imageSetClass 110 self._dataSetClass = dataSetClass 99 111 100 112 self._path = path … … 107 119 self._lib = None 108 120 109 self._layers = layerSetClass( 110 libClass=libClass, unicodeDataClass=unicodeDataClass, guidelineClass=guidelineClass, 111 layerClass=layerClass, glyphClass=glyphClass, 112 glyphContourClass=glyphContourClass, glyphPointClass=glyphPointClass, 113 glyphComponentClass=glyphComponentClass, glyphAnchorClass=glyphAnchorClass 114 ) 115 self._layers.setParent(self) 116 self._layers.addObserver(self, "_objectDirtyStateChange", "LayerSet.Changed") 117 self._layers.addObserver(self, "_layerAddedNotificationCallback", "LayerSet.LayerAdded") 118 self._layers.addObserver(self, "_layerWillBeDeletedNotificationCallback", "LayerSet.LayerWillBeDeleted") 119 120 self._images = imageSetClass() 121 self._images.setParent(self) 122 123 self._data = dataSetClass() 124 self._data.setParent(self) 121 self._layers = self.instantiateLayerSet() 122 self.beginSelfLayerSetNotificationObservation() 123 self._images = self.instantiateImageSet() 124 self.beginSelfImageSetNotificationObservation() 125 self._data = self.instantiateDataSet() 126 self.beginSelfDataSetNotificationObservation() 125 127 126 128 self._dirty = False … … 272 274 # ----------- 273 275 276 # layers 277 278 def instantiateLayerSet(self): 279 layers = self._layerSetClass( 280 font=self, 281 libClass=self._libClass, 282 unicodeDataClass=self._unicodeDataClass, 283 guidelineClass=self._guidelineClass, 284 layerClass=self._layerClass, 285 glyphClass=self._glyphClass, 286 glyphContourClass=self._glyphContourClass, 287 glyphPointClass=self._glyphPointClass, 288 glyphComponentClass=self._glyphComponentClass, 289 glyphAnchorClass=self._glyphAnchorClass, 290 glyphImageClass=self._glyphImageClass 291 ) 292 return layers 293 294 def beginSelfLayerSetNotificationObservation(self): 295 layers = self.layers 296 layers.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="LayerSet.Changed") 297 layers.addObserver(observer=self, methodName="_layerAddedNotificationCallback", notification="LayerSet.LayerAdded") 298 layers.addObserver(observer=self, methodName="_layerWillBeDeletedNotificationCallback", notification="LayerSet.LayerWillBeDeleted") 299 300 def endSelfLayerSetNotificationObservation(self): 301 layers = self.layers 302 if layers.dispatcher is None: 303 return 304 layers.removeObserver(observer=self, notification="LayerSet.Changed") 305 layers.removeObserver(observer=self, notification="LayerSet.LayerAdded") 306 layers.removeObserver(observer=self, notification="LayerSet.LayerWillBeDeleted") 307 layers.endSelfNotificationObservation() 308 274 309 def _get_layers(self): 275 310 return self._layers … … 277 312 layers = property(_get_layers, doc="The font's :class:`LayerSet` object.") 278 313 314 # info 315 316 def instantiateInfo(self): 317 info = self._infoClass( 318 font=self, 319 guidelineClass=self._guidelineClass 320 ) 321 return info 322 323 def beginSelfInfoSetNotificationObservation(self): 324 info = self.info 325 info.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Info.Changed") 326 327 def endSelfInfoSetNotificationObservation(self): 328 if self._info is None: 329 return 330 if self._info.dispatcher is None: 331 return 332 self._info.removeObserver(observer=self, notification="Info.Changed") 333 self._info.endSelfNotificationObservation() 334 279 335 def _get_info(self): 280 336 if self._info is None: 281 self._info = self. _infoClass(guidelineClass=self._guidelineClass)282 self. _info.setParent(self)337 self._info = self.instantiateInfo() 338 self.beginSelfInfoSetNotificationObservation() 283 339 reader = None 284 340 if self._path is not None: 341 self._info.disableNotifications() 285 342 reader = UFOReader(self._path) 286 343 reader.readInfo(self._info) 287 344 self._info.dirty = False 288 self._info.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Info.Changed")345 self._info.enableNotifications() 289 346 self._stampInfoDataState(reader) 290 347 return self._info … … 292 349 info = property(_get_info, doc="The font's :class:`Info` object.") 293 350 351 # kerning 352 353 def instantiateKerning(self): 354 kerning = self._kerningClass( 355 font=self 356 ) 357 return kerning 358 359 def beginSelfKerningNotificationObservation(self): 360 kerning = self.kerning 361 kerning.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Kerning.Changed") 362 363 def endSelfKerningNotificationObservation(self): 364 if self._kerning is None: 365 return 366 if self._kerning.dispatcher is None: 367 return 368 self._kerning.addObserver(observer=self, notification="Kerning.Changed") 369 self._kerning.endSelfNotificationObservation() 370 294 371 def _get_kerning(self): 295 372 if self._kerning is None: 296 self._kerning = self. _kerningClass()297 self. _kerning.setParent(self)373 self._kerning = self.instantiateKerning() 374 self.beginSelfKerningNotificationObservation() 298 375 reader = None 299 376 if self._path is not None: 377 self._kerning.disableNotifications() 300 378 # the _reader attribute may be present during __init__ 301 379 # but only under certain conditions. … … 307 385 self._kerning.update(d) 308 386 self._kerning.dirty = False 309 self._kerning.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Kerning.Changed")387 self._kerning.enableNotifications() 310 388 self._stampKerningDataState(reader) 311 389 return self._kerning … … 313 391 kerning = property(_get_kerning, doc="The font's :class:`Kerning` object.") 314 392 393 # groups 394 395 def instantiateGroups(self): 396 groups = self._groupsClass( 397 font=self 398 ) 399 return groups 400 401 def beginSelfGroupsNotificationObservation(self): 402 groups = self.groups 403 groups.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Groups.Changed") 404 405 def endSelfGroupsNotificationObservation(self): 406 if self._groups is None: 407 return 408 if self._groups.dispatcher is None: 409 return 410 self._groups.addObserver(observer=self, notification="Groups.Changed") 411 self._groups.endSelfNotificationObservation() 412 315 413 def _get_groups(self): 316 414 if self._groups is None: 317 self._groups = self. _groupsClass()318 self. _groups.setParent(self)415 self._groups = self.instantiateGroups() 416 self.beginSelfGroupsNotificationObservation() 319 417 reader = None 320 418 if self._path is not None: 419 self._groups.disableNotifications() 321 420 # the _reader attribute may be present during __init__ 322 421 # but only under certain conditions. … … 328 427 self._groups.update(d) 329 428 self._groups.dirty = False 330 self._groups.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Groups.Changed")429 self._groups.enableNotifications() 331 430 self._stampGroupsDataState(reader) 332 431 return self._groups … … 334 433 groups = property(_get_groups, doc="The font's :class:`Groups` object.") 335 434 435 # features 436 437 def instantiateFeatures(self): 438 features = self._featuresClass( 439 font=self 440 ) 441 return features 442 443 def beginSelfFeaturesNotificationObservation(self): 444 features = self.features 445 features.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Features.Changed") 446 447 def endSelfFeaturesNotificationObservation(self): 448 if self._features is None: 449 return 450 if self._features.dispatcher is None: 451 return 452 self._features.addObserver(observer=self, notification="Features.Changed") 453 self._features.endSelfNotificationObservation() 454 336 455 def _get_features(self): 337 456 if self._features is None: 338 self._features = self. _featuresClass()339 self. _features.setParent(self)457 self._features = self.instantiateFeatures() 458 self.beginSelfFeaturesNotificationObservation() 340 459 reader = None 341 460 if self._path is not None: 461 self._features.disableNotifications() 342 462 reader = UFOReader(self._path) 343 463 t = reader.readFeatures() 344 464 self._features.text = t 345 465 self._features.dirty = False 346 self._features.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Features.Changed")466 self._features.enableNotifications() 347 467 self._stampFeaturesDataState(reader) 348 468 return self._features … … 350 470 features = property(_get_features, doc="The font's :class:`Features` object.") 351 471 472 # lib 473 474 def instantiateLib(self): 475 lib = self._libClass( 476 font=self 477 ) 478 return lib 479 480 def beginSelfLibNotificationObservation(self): 481 self._lib.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Lib.Changed") 482 483 def endSelfLibNotificationObservation(self): 484 if self._lib is None: 485 return 486 if self._lib.dispatcher is None: 487 return 488 self._lib.removeObserver(observer=self, notification="Lib.Changed") 489 self._lib.endSelfNotificationObservation() 490 352 491 def _get_lib(self): 353 492 if self._lib is None: 354 self._lib = self. _libClass()355 self. _lib.setParent(self)493 self._lib = self.instantiateLib() 494 self.beginSelfLibNotificationObservation() 356 495 reader = None 357 496 if self._path is not None: 497 self._lib.disableNotifications() 358 498 reader = UFOReader(self._path) 359 499 d = reader.readLib() 360 500 self._lib.update(d) 361 self._lib.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Lib.Changed")501 self._lib.enableNotifications() 362 502 self._stampLibDataState(reader) 363 503 return self._lib … … 365 505 lib = property(_get_lib, doc="The font's :class:`Lib` object.") 366 506 507 # images 508 509 def instantiateImageSet(self): 510 imageSet = self._imageSetClass( 511 font=self 512 ) 513 return imageSet 514 515 def beginSelfImageSetNotificationObservation(self): 516 self._images.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="ImageSet.Changed") 517 518 def endSelfImageSetNotificationObservation(self): 519 if self._images.dispatcher is None: 520 return 521 self._images.removeObserver(observer=self, notification="ImageSet.Changed") 522 self._images.endSelfNotificationObservation() 523 524 def _get_images(self): 525 return self._images 526 527 images = property(_get_images, doc="The font's :class:`ImageSet` object.") 528 529 # data 530 531 def instantiateDataSet(self): 532 dataSet = self._dataSetClass( 533 font=self 534 ) 535 return dataSet 536 537 def beginSelfDataSetNotificationObservation(self): 538 self._data.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="DataSet.Changed") 539 540 def endSelfDataSetNotificationObservation(self): 541 if self._data.dispatcher is None: 542 return 543 self._data.removeObserver(observer=self, notification="DataSet.Changed") 544 self._data.endSelfNotificationObservation() 545 546 def _get_data(self): 547 return self._data 548 549 data = property(_get_data, doc="The font's :class:`DataSet` object.") 550 551 # unicode data (legacy) 552 367 553 def _get_unicodeData(self): 368 554 return self._glyphSet._unicodeData 369 555 370 556 unicodeData = property(_get_unicodeData, doc="The font's :class:`UnicodeData` object.") 371 372 def _get_images(self):373 return self._images374 375 images = property(_get_images, doc="The font's :class:`ImageSet` object.")376 377 def _get_data(self):378 return self._data379 380 data = property(_get_data, doc="The font's :class:`DataSet` object.")381 557 382 558 # glyph order … … 627 803 progressBar.update() 628 804 629 # ---------------------- 630 # Notification Callbacks 631 # ---------------------- 805 # ------------------------ 806 # Notification Observation 807 # ------------------------ 808 809 def endSelfNotificationObservation(self): 810 if self.dispatcher is None: 811 return 812 self.endSelfLayerSetNotificationObservation() 813 #self.endSelfInfoSetNotificationObservation() 814 #self.endSelfKerningNotificationObservation() 815 #self.endSelfGroupsNotificationObservation() 816 #self.endSelfLibNotificationObservation() 817 #self.endSelfFeaturesNotificationObservation() 818 #self.endSelfImageSetNotificationObservation() 819 #self.endSelfDataSetNotificationObservation() 820 super(Font, self).endSelfNotificationObservation() 632 821 633 822 def _objectDirtyStateChange(self, notification): -
packages/defcon/branches/ufo3/Lib/defcon/objects/glyph.py
r1016 r1022 73 73 representationFactories = {} 74 74 75 def __init__(self, contourClass=None, pointClass=None, componentClass=None, anchorClass=None, guidelineClass=None, libClass=None): 75 def __init__(self, layer=None, 76 contourClass=None, pointClass=None, componentClass=None, anchorClass=None, 77 guidelineClass=None, libClass=None, imageClass=None): 78 79 if layer is not None: 80 layer = weakref.ref(layer) 81 self._layer = layer 76 82 super(Glyph, self).__init__() 77 78 self._layer = None 79 self._parent = None 83 self.beginSelfNotificationObservation() 84 80 85 self._dirty = False 81 86 self._name = None … … 106 111 if libClass is None: 107 112 libClass = Lib 108 113 if imageClass is None: 114 imageClass = Image 109 115 self._contourClass = contourClass 110 116 self._pointClass = pointClass … … 112 118 self._anchorClass = anchorClass 113 119 self._guidelineClass = Guideline 114 self._lib = libClass() 115 116 def setParent(self, obj): 117 if obj is None: 118 if self.getParent() is not None: 119 for contour in self._contours: 120 self._removeParentDataInContour(contour) 121 for component in self._components: 122 self._removeParentDataInComponent(component) 123 for anchor in self._anchors: 124 self._removeParentDataInAnchor(anchor) 125 for guideline in self._guidelines: 126 self._removeParentDataInGuideline(guideline) 127 self._removeParentDataInLib() 128 self._removeParentDataInImage() 129 self.removeObserver(observer=self, notification="Glyph.Changed") 130 super(Glyph, self).setParent(obj) 131 else: 132 assert self.getParent() is None 133 super(Glyph, self).setParent(obj) 134 for contour in self._contours: 135 self._setParentDataInContour(contour) 136 for component in self._components: 137 self._setParentDataInComponent(component) 138 for anchor in self._anchors: 139 self._setParentDataInAnchor(anchor) 140 for guideline in self._guidelines: 141 self._setParentDataInGuideline(guideline) 142 self._setParentDataInLib() 143 self._setParentDataInImage() 144 self.addObserver(observer=self, methodName="destroyAllRepresentations", notification="Glyph.Changed") 120 self._libClass = libClass 121 self._imageClass = imageClass 122 123 self._lib = self.instantiateLib() 124 self.beginSelfLibNotificationObservation() 145 125 146 126 def _destroyBoundsCache(self): … … 148 128 self._controlPointBoundsCache = None 149 129 150 # ---------- 151 # Attributes 152 # ---------- 130 def __del__(self): 131 super(Glyph, self).__del__() 132 self._contours = None 133 self._components = None 134 self._anchors = None 135 self._guidelines = None 136 self._lib = None 137 self._image = None 138 139 # -------------- 140 # Parent Objects 141 # -------------- 142 143 def getParent(self): 144 return self.font 153 145 154 146 def _get_font(self): 155 return self.getParent() 147 layerSet = self.layerSet 148 if layerSet is None: 149 return None 150 return layerSet.font 156 151 157 152 font = property(_get_font, doc="The :class:`Font` that this glyph belongs to.") … … 170 165 return self._layer() 171 166 172 def _set_layer(self, value): 173 if value is not None: 174 value = weakref.ref(value) 175 self._layer = value 176 177 layer = property(_get_layer, _set_layer, doc="The :class:`Layer` that this glyph belongs to. This should not be set externally.") 178 179 # classes 180 181 def _get_contourClass(self): 182 return self._contourClass 183 184 contourClass = property(_get_contourClass, doc="The class used for contours.") 185 186 def _get_pointClass(self): 187 return self._pointClass 188 189 pointClass = property(_get_pointClass, doc="The class used for points.") 190 191 def _get_componentClass(self): 192 return self._componentClass 193 194 componentClass = property(_get_componentClass, doc="The class used for components.") 195 196 def _get_anchorClass(self): 197 return self._anchorClass 198 199 anchorClass = property(_get_anchorClass, doc="The class used for anchors.") 200 201 def _get_guidelineClass(self): 202 return self._guidelineClass 203 204 guidelineClass = property(_get_guidelineClass, doc="The class used for guidelines.") 167 layer = property(_get_layer, doc="The :class:`Layer` that this glyph belongs to.") 168 169 # ---------------- 170 # Basic Attributes 171 # ---------------- 205 172 206 173 # identifiers … … 255 222 256 223 unicode = property(_get_unicode, _set_unicode, doc="The primary unicode value for the glyph. This is the equivalent of ``glyph.unicodes[0]``. This is a convenience attribute that works with the ``unicodes`` attribute.") 224 225 # ------- 226 # Metrics 227 # ------- 257 228 258 229 # bounds … … 348 319 height = property(_get_height, _set_height, doc="The height of the glyph. Setting this posts *Glyph.HeightChanged* and *Glyph.Changed* notifications.") 349 320 350 # sub-object collections 351 352 def _get_components(self): 353 return list(self._components) 354 355 components = property(_get_components, doc="An ordered list of :class:`Component` objects stored in the glyph.") 356 357 def _get_anchors(self): 358 return list(self._anchors) 359 360 def _set_anchors(self, value): 361 self.clearAnchors() 362 self.holdNotifications() 363 for anchor in value: 364 self.appendAnchor(anchor) 365 self.releaseHeldNotifications() 366 367 anchors = property(_get_anchors, _set_anchors, doc="An ordered list of :class:`Anchor` objects stored in the glyph.") 368 369 def _get_guidelines(self): 370 return list(self._guidelines) 371 372 def _set_guidelines(self, value): 373 self.clearGuidelines() 374 self.holdNotifications() 375 for guideline in value: 376 self.appendGuideline(guideline) 377 self.releaseHeldNotifications() 378 379 guidelines = property(_get_guidelines, _set_guidelines, doc="An ordered list of :class:`Guideline` objects stored in the glyph. Setting this will post a *Glyph.Changed* notification along with any notifications posted by the :py:meth:`Glyph.appendGuideline` and :py:meth:`Glyph.clearGuidelines` methods.") 380 381 # note 382 383 def _get_note(self): 384 return self._note 385 386 def _set_note(self, value): 387 if value is not None: 388 assert isinstance(value, basestring) 389 oldValue = self._note 390 if oldValue != value: 391 self._note = value 392 self.postNotification(notification="Glyph.NoteChanged", data=dict(oldValue=oldValue, newValue=value)) 393 self.dirty = True 394 395 note = property(_get_note, _set_note, doc="An arbitrary note for the glyph. Setting this will post a *Glyph.Changed* notification.") 396 397 # lib 398 399 def _get_lib(self): 400 return self._lib 401 402 def _set_lib(self, value): 403 self._lib.clear() 404 self._lib.update(value) 405 self.dirty = True 406 407 lib = property(_get_lib, _set_lib, doc="The glyph's :class:`Lib` object. Setting this will clear any existing lib data and post a *Glyph.Changed* notification if data was replaced.") 408 409 # image 410 411 def _get_image(self): 412 return self._image 413 414 def _set_image(self, image): 415 if image is None: 416 if self._image is not None: 417 self.postNotification(notification="Glyph.ImageWillBeDeleted") 418 self._removeParentDataInImage() 419 self._image = None 420 self.postNotification(notification="Glyph.ImageChanged") 421 self.dirty = True 422 else: 423 if self._image is None: 424 self._image = Image() 425 self._setParentDataInImage() 426 if set(self.image.items()) != set(image.items()): 427 for key in self._image.keys(): 428 self._image[key] = image.get(key) 429 self.postNotification(notification="Glyph.ImageChanged") 430 self.dirty = True 431 432 image = property(_get_image, _set_image, doc="The glyph's :class:`Image` object. Setting this posts *Glyph.ImageChanged* and *Glyph.Changed* notifications.") 321 # ---------------------- 322 # Lib Wrapped Attributes 323 # ---------------------- 433 324 434 325 # mark color … … 461 352 markColor = property(_get_markColor, _set_markColor, doc="The glyph's mark color. When setting, the value can be a UFO color string, a sequence of (r, g, b, a) or a :class:`Color` object. Setting this posts *Glyph.MarkColorChanged* and *Glyph.Changed* notifications.") 462 353 463 # ------- ----464 # Pen Methods465 # ------- ----354 # ------- 355 # Pen API 356 # ------- 466 357 467 358 def draw(self, pen): … … 496 387 return GlyphObjectPointPen(self) 497 388 498 # -------------------------- 499 # Parent Setting and Removal 500 # -------------------------- 501 502 def _setParentDataInLib(self): 503 self._lib.setParent(self) 504 if self.dispatcher is not None and not self._lib.hasObserver(observer=self, notification="Lib.Changed"): 505 self._lib.addObserver(observer=self, methodName="_libContentChanged", notification="Lib.Changed") 506 507 def _removeParentDataInLib(self): 508 if self.dispatcher is not None: 509 self._lib.removeObserver(observer=self, notification="Lib.Changed") 510 self._lib.setParent(None) 511 512 def _setParentDataInContour(self, contour): 513 contour.setParent(self) 514 if self.dispatcher is not None and not contour.hasObserver(observer=self, notification="Contour.Changed"): 515 contour.addObserver(observer=self, methodName="_contourChanged", notification="Contour.Changed") 516 517 def _removeParentDataInContour(self, contour): 518 if self.dispatcher is not None: 519 contour.removeObserver(observer=self, notification="Contour.Changed") 520 contour.setParent(None) 521 522 def _setParentDataInComponent(self, component): 523 component.setParent(self) 524 if self.dispatcher is not None and not component.hasObserver(observer=self, notification="Component.Changed"): 525 component.addObserver(observer=self, methodName="_componentChanged", notification="Component.Changed") 526 527 def _removeParentDataInComponent(self, component): 528 if self.dispatcher is not None: 529 component.removeObserver(observer=self, notification="Component.Changed") 530 component.setParent(None) 531 532 def _setParentDataInAnchor(self, anchor): 533 anchor.setParent(self) 534 if self.dispatcher is not None and not anchor.hasObserver(observer=self, notification="Anchor.Changed"): 535 anchor.addObserver(observer=self, methodName="_anchorChanged", notification="Anchor.Changed") 536 537 def _removeParentDataInAnchor(self, anchor): 538 if self.dispatcher is not None: 539 anchor.removeObserver(observer=self, notification="Anchor.Changed") 540 anchor.setParent(None) 541 542 def _setParentDataInGuideline(self, guideline): 543 guideline.setParent(self) 544 if self.dispatcher is not None and not guideline.hasObserver(observer=self, notification="Guideline.Changed"): 545 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 546 547 def _removeParentDataInGuideline(self, guideline): 548 if self.dispatcher is not None: 549 guideline.removeObserver(observer=self, notification="Guideline.Changed") 550 guideline.setParent(None) 551 552 def _setParentDataInImage(self): 389 # -------- 390 # Contours 391 # -------- 392 393 def _get_contourClass(self): 394 return self._contourClass 395 396 contourClass = property(_get_contourClass, doc="The class used for contours.") 397 398 def _get_pointClass(self): 399 return self._pointClass 400 401 pointClass = property(_get_pointClass, doc="The class used for points.") 402 403 def instantiateContour(self): 404 contour = self._contourClass( 405 glyph=self, 406 pointClass=self.pointClass 407 ) 408 return contour 409 410 def beginSelfContourNotificationObservation(self, contour): 411 if contour.dispatcher is None: 412 return 413 contour.addObserver(observer=self, methodName="_contourChanged", notification="Contour.Changed") 414 415 def endSelfContourNotificationObservation(self, contour): 416 if contour.dispatcher is None: 417 return 418 contour.removeObserver(observer=self, notification="Contour.Changed") 419 contour.endSelfNotificationObservation() 420 421 def appendContour(self, contour): 422 """ 423 Append **contour** to the glyph. The contour must be a defcon 424 :class:`Contour` object or a subclass of that object. An error 425 will be raised if the contour's identifier or a point identifier 426 conflicts with any of the identifiers within the glyph. 427 428 This will post a *Glyph.Changed* notification. 429 """ 430 assert contour not in self._contours 431 self.insertContour(len(self._contours), contour) 432 433 def insertContour(self, index, contour): 434 """ 435 Insert **contour** into the glyph at index. The contour 436 must be a defcon :class:`Contour` object or a subclass 437 of that object. An error will be raised if the contour's 438 identifier or a point identifier conflicts with any of 439 the identifiers within the glyph. 440 441 This will post a *Glyph.Changed* notification. 442 """ 443 assert contour not in self._contours 444 assert contour.glyph in (self, None), "This contour belongs to another glyph." 445 if contour.glyph is None: 446 identifiers = self._identifiers 447 if contour.identifier is not None: 448 assert contour.identifier not in identifiers 449 identifiers.add(contour.identifier) 450 for point in contour: 451 if point.identifier is not None: 452 assert point.identifier not in identifiers 453 identifiers.add(point.identifier) 454 contour.glyph = self 455 contour.beginSelfNotificationObservation() 456 self.beginSelfContourNotificationObservation(contour) 457 self._contours.insert(index, contour) 458 self._destroyBoundsCache() 459 self.postNotification(notification="Glyph.ContoursChanged") 460 self.dirty = True 461 462 def removeContour(self, contour): 463 """ 464 Remove **contour** from the glyph. 465 466 This will post a *Glyph.Changed* notification. 467 """ 468 self.postNotification(notification="Glyph.ContourWillBeDeleted", data=dict(object=contour)) 469 identifiers = self._identifiers 470 if contour.identifier is not None: 471 identifiers.remove(contour.identifier) 472 for point in contour: 473 if point.identifier is not None: 474 identifiers.remove(point.identifier) 475 self._contours.remove(contour) 476 self.endSelfContourNotificationObservation(contour) 477 self._destroyBoundsCache() 478 self.postNotification(notification="Glyph.ContoursChanged") 479 self.dirty = True 480 481 def contourIndex(self, contour): 482 """ 483 Get the index for **contour**. 484 """ 485 return self._contours.index(contour) 486 487 def clearContours(self): 488 """ 489 Clear all contours from the glyph. 490 491 This posts a *Glyph.Changed* notification. 492 """ 493 self.holdNotifications() 494 for contour in reversed(self._contours): 495 self.removeContour(contour) 496 self.releaseHeldNotifications() 497 498 # ---------- 499 # Components 500 # ---------- 501 502 def _get_componentClass(self): 503 return self._componentClass 504 505 componentClass = property(_get_componentClass, doc="The class used for components.") 506 507 def _get_components(self): 508 return list(self._components) 509 510 components = property(_get_components, doc="An ordered list of :class:`Component` objects stored in the glyph.") 511 512 def instantiateComponent(self): 513 component = self._componentClass( 514 glyph=self 515 ) 516 return component 517 518 def beginSelfComponentNotificationObservation(self, component): 519 if component.dispatcher is None: 520 return 521 component.addObserver(observer=self, methodName="_componentChanged", notification="Component.Changed") 522 523 def endSelfComponentNotificationObservation(self, component): 524 if component.dispatcher is None: 525 return 526 component.removeObserver(observer=self, notification="Component.Changed") 527 component.endSelfNotificationObservation() 528 529 def appendComponent(self, component): 530 """ 531 Append **component** to the glyph. The component must be a defcon 532 :class:`Component` object or a subclass of that object. An error 533 will be raised if the component's identifier conflicts with any of 534 the identifiers within the glyph. 535 536 This will post a *Glyph.Changed* notification. 537 """ 538 assert component not in self._components 539 self.insertComponent(len(self._components), component) 540 541 def insertComponent(self, index, component): 542 """ 543 Insert **component** into the glyph at index. The component 544 must be a defcon :class:`Component` object or a subclass 545 of that object. An error will be raised if the component's 546 identifier conflicts with any of the identifiers within 547 the glyph. 548 549 This will post a *Glyph.Changed* notification. 550 """ 551 assert component not in self._components 552 assert component.glyph in (self, None), "This component belongs to another glyph." 553 if component.glyph is None: 554 if component.identifier is not None: 555 identifiers = self._identifiers 556 assert component.identifier not in identifiers 557 identifiers.add(component.identifier) 558 component.glyph = self 559 component.beginSelfNotificationObservation() 560 self.beginSelfComponentNotificationObservation(component) 561 self._components.insert(index, component) 562 self._destroyBoundsCache() 563 self.postNotification(notification="Glyph.ComponentsChanged") 564 self.dirty = True 565 566 def removeComponent(self, component): 567 """ 568 Remove **component** from the glyph. 569 570 This will post a *Glyph.Changed* notification. 571 """ 572 self.postNotification(notification="Glyph.ComponentWillBeDeleted", data=dict(object=component)) 573 if component.identifier is not None: 574 self._identifiers.remove(component.identifier) 575 self._components.remove(component) 576 self.endSelfComponentNotificationObservation(component) 577 self._destroyBoundsCache() 578 self.postNotification(notification="Glyph.ComponentsChanged") 579 self.dirty = True 580 581 def componentIndex(self, component): 582 """ 583 Get the index for **component**. 584 """ 585 return self._components.index(component) 586 587 def clearComponents(self): 588 """ 589 Clear all components from the glyph. 590 591 This posts a *Glyph.Changed* notification. 592 """ 593 self.holdNotifications() 594 for component in reversed(self._components): 595 self.removeComponent(component) 596 self.releaseHeldNotifications() 597 598 # ------- 599 # Anchors 600 # ------- 601 602 def _get_anchorClass(self): 603 return self._anchorClass 604 605 anchorClass = property(_get_anchorClass, doc="The class used for anchors.") 606 607 def _get_anchors(self): 608 return list(self._anchors) 609 610 def _set_anchors(self, value): 611 self.clearAnchors() 612 self.holdNotifications() 613 for anchor in value: 614 self.appendAnchor(anchor) 615 self.releaseHeldNotifications() 616 617 anchors = property(_get_anchors, _set_anchors, doc="An ordered list of :class:`Anchor` objects stored in the glyph.") 618 619 def instantiateAnchor(self, anchorDict=None): 620 anchor = self._anchorClass( 621 glyph=self, 622 anchorDict=anchorDict 623 ) 624 return anchor 625 626 def beginSelfAnchorNotificationObservation(self, anchor): 627 if anchor.dispatcher is None: 628 return 629 anchor.addObserver(observer=self, methodName="_anchorChanged", notification="Anchor.Changed") 630 631 def endSelfAnchorNotificationObservation(self, anchor): 632 if anchor.dispatcher is None: 633 return 634 anchor.removeObserver(observer=self, notification="Anchor.Changed") 635 anchor.endSelfNotificationObservation() 636 637 def appendAnchor(self, anchor): 638 """ 639 Append **anchor** to the glyph. The anchor must be a defcon 640 :class:`Anchor` object or a subclass of that object. An error 641 will be raised if the anchor's identifier conflicts with any of 642 the identifiers within the glyph. 643 644 This will post a *Glyph.Changed* notification. 645 """ 646 assert anchor not in self._anchors 647 self.insertAnchor(len(self._anchors), anchor) 648 649 def insertAnchor(self, index, anchor): 650 """ 651 Insert **anchor** into the glyph at index. The anchor 652 must be a defcon :class:`Anchor` object or a subclass 653 of that object. An error will be raised if the anchor's 654 identifier conflicts with any of the identifiers within 655 the glyph. 656 657 This will post a *Glyph.Changed* notification. 658 """ 659 assert anchor not in self._anchors 660 if not isinstance(anchor, self._anchorClass): 661 anchor = self.instantiateAnchor(anchorDict=anchor) 662 assert anchor.glyph in (self, None), "This anchor belongs to another glyph." 663 if anchor.glyph is None: 664 if anchor.identifier is not None: 665 identifiers = self._identifiers 666 assert anchor.identifier not in identifiers 667 identifiers.add(anchor.identifier) 668 anchor.glyph = self 669 anchor.beginSelfNotificationObservation() 670 self.beginSelfAnchorNotificationObservation(anchor) 671 self._anchors.insert(index, anchor) 672 self.postNotification(notification="Glyph.AnchorsChanged") 673 self.dirty = True 674 675 def removeAnchor(self, anchor): 676 """ 677 Remove **anchor** from the glyph. 678 679 This will post a *Glyph.Changed* notification. 680 """ 681 self.postNotification(notification="Glyph.AnchorWillBeDeleted", data=dict(object=anchor)) 682 if anchor.identifier is not None: 683 self._identifiers.remove(anchor.identifier) 684 self._anchors.remove(anchor) 685 self.endSelfAnchorNotificationObservation(anchor) 686 self.postNotification(notification="Glyph.AnchorsChanged") 687 self.dirty = True 688 689 def anchorIndex(self, anchor): 690 """ 691 Get the index for **anchor**. 692 """ 693 return self._anchors.index(anchor) 694 695 def clearAnchors(self): 696 """ 697 Clear all anchors from the glyph. 698 699 This posts a *Glyph.Changed* notification. 700 """ 701 self.holdNotifications() 702 for anchor in reversed(self._anchors): 703 self.removeAnchor(anchor) 704 self.releaseHeldNotifications() 705 706 # ---------- 707 # Guidelines 708 # ---------- 709 710 def _get_guidelineClass(self): 711 return self._guidelineClass 712 713 guidelineClass = property(_get_guidelineClass, doc="The class used for guidelines.") 714 715 def _get_guidelines(self): 716 return list(self._guidelines) 717 718 def _set_guidelines(self, value): 719 self.clearGuidelines() 720 self.holdNotifications() 721 for guideline in value: 722 self.appendGuideline(guideline) 723 self.releaseHeldNotifications() 724 725 guidelines = property(_get_guidelines, _set_guidelines, doc="An ordered list of :class:`Guideline` objects stored in the glyph. Setting this will post a *Glyph.Changed* notification along with any notifications posted by the :py:meth:`Glyph.appendGuideline` and :py:meth:`Glyph.clearGuidelines` methods.") 726 727 def instantiateGuideline(self, guidelineDict=None): 728 guideline = self._guidelineClass( 729 glyph=self, 730 guidelineDict=guidelineDict 731 ) 732 return guideline 733 734 def beginSelfGuidelineNotificationObservation(self, guideline): 735 if guideline.dispatcher is None: 736 return 737 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 738 739 def endSelfGuidelineNotificationObservation(self, guideline): 740 if guideline.dispatcher is None: 741 return 742 guideline.removeObserver(observer=self, notification="Guideline.Changed") 743 guideline.endSelfNotificationObservation() 744 745 def appendGuideline(self, guideline): 746 """ 747 Append **guideline** to the glyph. The guideline must be a defcon 748 :class:`Guideline` object or a subclass of that object. An error 749 will be raised if the guideline's identifier conflicts with any of 750 the identifiers within the glyph. 751 752 This will post a *Glyph.Changed* notification. 753 """ 754 assert guideline not in self._guidelines 755 self.insertGuideline(len(self._guidelines), guideline) 756 757 def insertGuideline(self, index, guideline): 758 """ 759 Insert **guideline** into the glyph at index. The guideline 760 must be a defcon :class:`Guideline` object or a subclass 761 of that object. An error will be raised if the guideline's 762 identifier conflicts with any of the identifiers within 763 the glyph. 764 765 This will post a *Glyph.Changed* notification. 766 """ 767 assert guideline not in self._guidelines 768 if not isinstance(guideline, self._guidelineClass): 769 guideline = self.instantiateGuideline(guidelineDict=guideline) 770 assert guideline.glyph in (self, None), "This guideline belongs to another glyph." 771 if guideline.glyph is None: 772 assert guideline.fontInfo is None, "This guideline belongs to a font." 773 if guideline.glyph is None: 774 if guideline.identifier is not None: 775 identifiers = self._identifiers 776 assert guideline.identifier not in identifiers 777 if guideline.identifier is not None: 778 identifiers.add(guideline.identifier) 779 guideline.glyph = self 780 guideline.beginSelfNotificationObservation() 781 self.beginSelfGuidelineNotificationObservation(guideline) 782 self._guidelines.insert(index, guideline) 783 self.postNotification(notification="Glyph.GuidelinesChanged") 784 self.dirty = True 785 786 def removeGuideline(self, guideline): 787 """ 788 Remove **guideline** from the glyph. 789 790 This will post a *Glyph.Changed* notification. 791 """ 792 self.postNotification(notification="Glyph.GuidelineWillBeDeleted", data=dict(object=guideline)) 793 if guideline.identifier is not None: 794 self._identifiers.remove(guideline.identifier) 795 self._guidelines.remove(guideline) 796 self.endSelfGuidelineNotificationObservation(guideline) 797 self.postNotification(notification="Glyph.GuidelinesChanged") 798 self.dirty = True 799 800 def guidelineIndex(self, guideline): 801 """ 802 Get the index for **guideline**. 803 """ 804 return self._guidelines.index(guideline) 805 806 def clearGuidelines(self): 807 """ 808 Clear all guidelines from the glyph. 809 810 This posts a *Glyph.Changed* notification. 811 """ 812 self.holdNotifications() 813 for guideline in reversed(self._guidelines): 814 self.removeGuideline(guideline) 815 self.releaseHeldNotifications() 816 817 # ---- 818 # Note 819 # ---- 820 821 def _get_note(self): 822 return self._note 823 824 def _set_note(self, value): 825 if value is not None: 826 assert isinstance(value, basestring) 827 oldValue = self._note 828 if oldValue != value: 829 self._note = value 830 self.postNotification(notification="Glyph.NoteChanged", data=dict(oldValue=oldValue, newValue=value)) 831 self.dirty = True 832 833 note = property(_get_note, _set_note, doc="An arbitrary note for the glyph. Setting this will post a *Glyph.Changed* notification.") 834 835 # --- 836 # Lib 837 # --- 838 839 def instantiateLib(self): 840 lib = self._libClass( 841 glyph=self 842 ) 843 return lib 844 845 def _get_lib(self): 846 return self._lib 847 848 def _set_lib(self, value): 849 self._lib.clear() 850 self._lib.update(value) 851 self.dirty = True 852 853 lib = property(_get_lib, _set_lib, doc="The glyph's :class:`Lib` object. Setting this will clear any existing lib data and post a *Glyph.Changed* notification if data was replaced.") 854 855 def beginSelfLibNotificationObservation(self): 856 if self._lib.dispatcher is None: 857 return 858 self._lib.addObserver(observer=self, methodName="_libContentChanged", notification="Lib.Changed") 859 860 def endSelfLibNotificationObservation(self): 861 if self._lib.dispatcher is None: 862 return 863 self._lib.removeObserver(observer=self, notification="Lib.Changed") 864 self._lib.endSelfNotificationObservation() 865 866 # ----- 867 # Image 868 # ----- 869 870 def instantiateImage(self): 871 image = self._imageClass( 872 glyph=self 873 ) 874 return image 875 876 def _get_image(self): 877 return self._image 878 879 def _set_image(self, image): 880 # removing image 881 if image is None: 882 if self._image is not None: 883 self.postNotification(notification="Glyph.ImageWillBeDeleted") 884 self.endSelfImageNotificationObservation() 885 self._image = None 886 self.postNotification(notification="Glyph.ImageChanged") 887 self.dirty = True 888 # adding image 889 else: 890 if self._image is None: 891 self._image = self.instantiateImage() 892 self.beginSelfImageNotificationObservation() 893 if set(self.image.items()) != set(image.items()): 894 for key in self._image.keys(): 895 self._image[key] = image.get(key) 896 self.postNotification(notification="Glyph.ImageChanged") 897 self.dirty = True 898 899 image = property(_get_image, _set_image, doc="The glyph's :class:`Image` object. Setting this posts *Glyph.ImageChanged* and *Glyph.Changed* notifications.") 900 901 def beginSelfImageNotificationObservation(self): 902 if self._image.dispatcher is None: 903 return 904 self._image.addObserver(observer=self, methodName="_imageChanged", notification="Image.Changed") 905 906 def endSelfImageNotificationObservation(self): 553 907 if self._image is None: 554 908 return 555 self._image.setParent(self) 556 if self.dispatcher is not None and not self._image.hasObserver(observer=self, notification="Image.Changed"): 557 self._image.addObserver(observer=self, methodName="_imageChanged", notification="Image.Changed") 558 559 def _removeParentDataInImage(self): 560 if self._image is None: 909 if self._image.dispatcher is None: 561 910 return 562 if self.dispatcher is not None: 563 self._image.removeObserver(observer=self, notification="Image.Changed") 564 self._image.setParent(None) 565 566 # ------- 567 # Methods 568 # ------- 911 self._image.removeObserver(observer=self, notification="Image.Changed") 912 self._image.endSelfNotificationObservation() 913 914 # ------------- 915 # List Behavior 916 # ------------- 569 917 570 918 def __len__(self): … … 584 932 def _getContourIndex(self, contour): 585 933 return self._contours.index(contour) 934 935 # ---------------- 936 # Glyph Absorption 937 # ---------------- 586 938 587 939 def copyDataFromGlyph(self, glyph): … … 609 961 self.unicodes = list(glyph.unicodes) 610 962 self.note = glyph.note 611 self.guidelines = glyph.guidelines612 self.anchors = glyph.anchors963 self.guidelines = [self.instantiateGuideline(g) for g in glyph.guidelines] 964 self.anchors = [self.instantiateAnchor(a) for a in glyph.anchors] 613 965 self.image = glyph.image 614 966 pointPen = self.getPointPen() … … 616 968 self.lib = deepcopy(glyph.lib) 617 969 618 def appendContour(self, contour): 619 """ 620 Append **contour** to the glyph. The contour must be a defcon 621 :class:`Contour` object or a subclass of that object. An error 622 will be raised if the contour's identifier or a point identifier 623 conflicts with any of the identifiers within the glyph. 624 625 This will post a *Glyph.Changed* notification. 626 """ 627 assert contour not in self._contours 628 self.insertContour(len(self._contours), contour) 629 630 def appendComponent(self, component): 631 """ 632 Append **component** to the glyph. The component must be a defcon 633 :class:`Component` object or a subclass of that object. An error 634 will be raised if the component's identifier conflicts with any of 635 the identifiers within the glyph. 636 637 This will post a *Glyph.Changed* notification. 638 """ 639 assert component not in self._components 640 self.insertComponent(len(self._components), component) 641 642 def appendAnchor(self, anchor): 643 """ 644 Append **anchor** to the glyph. The anchor must be a defcon 645 :class:`Anchor` object or a subclass of that object. An error 646 will be raised if the anchor's identifier conflicts with any of 647 the identifiers within the glyph. 648 649 This will post a *Glyph.Changed* notification. 650 """ 651 assert anchor not in self._anchors 652 self.insertAnchor(len(self._anchors), anchor) 653 654 def appendGuideline(self, guideline): 655 """ 656 Append **guideline** to the glyph. The guideline must be a defcon 657 :class:`Guideline` object or a subclass of that object. An error 658 will be raised if the guideline's identifier conflicts with any of 659 the identifiers within the glyph. 660 661 This will post a *Glyph.Changed* notification. 662 """ 663 assert guideline not in self._guidelines 664 self.insertGuideline(len(self._guidelines), guideline) 665 666 def insertContour(self, index, contour): 667 """ 668 Insert **contour** into the glyph at index. The contour 669 must be a defcon :class:`Contour` object or a subclass 670 of that object. An error will be raised if the contour's 671 identifier or a point identifier conflicts with any of 672 the identifiers within the glyph. 673 674 This will post a *Glyph.Changed* notification. 675 """ 676 assert contour not in self._contours 677 identifiers = self._identifiers 678 if contour.identifier is not None: 679 assert contour.identifier not in identifiers 680 if contour.identifier is not None: 681 identifiers.add(contour.identifier) 682 for point in contour: 683 if point.identifier is not None: 684 assert point.identifier not in identifiers 685 self._identifiers.add(point.identifier) 686 if contour.glyph != self: 687 self._setParentDataInContour(contour) 688 self._contours.insert(index, contour) 689 self._destroyBoundsCache() 690 self.postNotification(notification="Glyph.ContoursChanged") 691 self.dirty = True 692 693 def insertComponent(self, index, component): 694 """ 695 Insert **component** into the glyph at index. The component 696 must be a defcon :class:`Component` object or a subclass 697 of that object. An error will be raised if the component's 698 identifier conflicts with any of the identifiers within 699 the glyph. 700 701 This will post a *Glyph.Changed* notification. 702 """ 703 assert component not in self._components 704 if component.identifier is not None: 705 identifiers = self._identifiers 706 assert component.identifier not in identifiers 707 if component.identifier is not None: 708 identifiers.add(component.identifier) 709 if component.glyph != self: 710 self._setParentDataInComponent(component) 711 self._components.insert(index, component) 712 self._destroyBoundsCache() 713 self.postNotification(notification="Glyph.ComponentsChanged") 714 self.dirty = True 715 716 def insertAnchor(self, index, anchor): 717 """ 718 Insert **anchor** into the glyph at index. The anchor 719 must be a defcon :class:`Anchor` object or a subclass 720 of that object. An error will be raised if the anchor's 721 identifier conflicts with any of the identifiers within 722 the glyph. 723 724 This will post a *Glyph.Changed* notification. 725 """ 726 assert anchor not in self._anchors 727 if not isinstance(anchor, self._anchorClass): 728 anchor = self._anchorClass(anchor) 729 if anchor.identifier is not None: 730 identifiers = self._identifiers 731 assert anchor.identifier not in identifiers 732 if anchor.identifier is not None: 733 identifiers.add(anchor.identifier) 734 if anchor.glyph != self: 735 self._setParentDataInAnchor(anchor) 736 self._anchors.insert(index, anchor) 737 self.postNotification(notification="Glyph.AnchorsChanged") 738 self.dirty = True 739 740 def insertGuideline(self, index, guideline): 741 """ 742 Insert **guideline** into the glyph at index. The guideline 743 must be a defcon :class:`Guideline` object or a subclass 744 of that object. An error will be raised if the guideline's 745 identifier conflicts with any of the identifiers within 746 the glyph. 747 748 This will post a *Glyph.Changed* notification. 749 """ 750 assert guideline not in self._guidelines 751 if not isinstance(guideline, self._guidelineClass): 752 guideline = self._guidelineClass(guideline) 753 if guideline.identifier is not None: 754 identifiers = self._identifiers 755 assert guideline.identifier not in identifiers 756 if guideline.identifier is not None: 757 identifiers.add(guideline.identifier) 758 if guideline.glyph != self: 759 self._setParentDataInGuideline(guideline) 760 self._guidelines.insert(index, guideline) 761 self.postNotification(notification="Glyph.GuidelinesChanged") 762 self.dirty = True 763 764 def removeContour(self, contour): 765 """ 766 Remove **contour** from the glyph. 767 768 This will post a *Glyph.Changed* notification. 769 """ 770 self.postNotification(notification="Glyph.ContourWillBeDeleted", data=dict(object=contour)) 771 identifiers = self._identifiers 772 if contour.identifier is not None: 773 identifiers.remove(contour.identifier) 774 for point in contour: 775 if point.identifier is not None: 776 identifiers.remove(point.identifier) 777 self._contours.remove(contour) 778 self._removeParentDataInContour(contour) 779 self._destroyBoundsCache() 780 self.postNotification(notification="Glyph.ContoursChanged") 781 self.dirty = True 782 783 def removeComponent(self, component): 784 """ 785 Remove **component** from the glyph. 786 787 This will post a *Glyph.Changed* notification. 788 """ 789 self.postNotification(notification="Glyph.ComponentWillBeDeleted", data=dict(object=component)) 790 if component.identifier is not None: 791 self._identifiers.remove(component.identifier) 792 self._components.remove(component) 793 self._removeParentDataInComponent(component) 794 self._destroyBoundsCache() 795 self.postNotification(notification="Glyph.ComponentsChanged") 796 self.dirty = True 797 798 def removeAnchor(self, anchor): 799 """ 800 Remove **anchor** from the glyph. 801 802 This will post a *Glyph.Changed* notification. 803 """ 804 self.postNotification(notification="Glyph.AnchorWillBeDeleted", data=dict(object=anchor)) 805 if anchor.identifier is not None: 806 self._identifiers.remove(anchor.identifier) 807 self._anchors.remove(anchor) 808 self._removeParentDataInAnchor(anchor) 809 self.postNotification(notification="Glyph.AnchorsChanged") 810 self.dirty = True 811 812 def removeGuideline(self, guideline): 813 """ 814 Remove **guideline** from the glyph. 815 816 This will post a *Glyph.Changed* notification. 817 """ 818 self.postNotification(notification="Glyph.GuidelineWillBeDeleted", data=dict(object=guideline)) 819 if guideline.identifier is not None: 820 self._identifiers.remove(guideline.identifier) 821 self._guidelines.remove(guideline) 822 self._removeParentDataInGuideline(guideline) 823 self.postNotification(notification="Glyph.GuidelinesChanged") 824 self.dirty = True 825 826 def contourIndex(self, contour): 827 """ 828 Get the index for **contour**. 829 """ 830 return self._contours.index(contour) 831 832 def componentIndex(self, component): 833 """ 834 Get the index for **component**. 835 """ 836 return self._components.index(component) 837 838 def anchorIndex(self, anchor): 839 """ 840 Get the index for **anchor**. 841 """ 842 return self._anchors.index(anchor) 843 844 def guidelineIndex(self, guideline): 845 """ 846 Get the index for **guideline**. 847 """ 848 return self._guidelines.index(guideline) 970 # ----- 971 # Clear 972 # ----- 849 973 850 974 def clear(self): … … 861 985 self.releaseHeldNotifications() 862 986 863 def clearContours(self): 864 """ 865 Clear all contours from the glyph. 866 867 This posts a *Glyph.Changed* notification. 868 """ 869 self.holdNotifications() 870 for contour in reversed(self._contours): 871 self.removeContour(contour) 872 self.releaseHeldNotifications() 873 874 def clearComponents(self): 875 """ 876 Clear all components from the glyph. 877 878 This posts a *Glyph.Changed* notification. 879 """ 880 self.holdNotifications() 881 for component in reversed(self._components): 882 self.removeComponent(component) 883 self.releaseHeldNotifications() 884 885 def clearAnchors(self): 886 """ 887 Clear all anchors from the glyph. 888 889 This posts a *Glyph.Changed* notification. 890 """ 891 self.holdNotifications() 892 for anchor in reversed(self._anchors): 893 self.removeAnchor(anchor) 894 self.releaseHeldNotifications() 895 896 def clearGuidelines(self): 897 """ 898 Clear all guidelines from the glyph. 899 900 This posts a *Glyph.Changed* notification. 901 """ 902 self.holdNotifications() 903 for guideline in reversed(self._guidelines): 904 self.removeGuideline(guideline) 905 self.releaseHeldNotifications() 987 # ---- 988 # Move 989 # ---- 906 990 907 991 def move(self, (x, y)): … … 935 1019 self._controlPointBoundsCache = (xMin, yMin, xMax, yMax) 936 1020 1021 # ------------ 1022 # Point Inside 1023 # ------------ 1024 937 1025 def pointInside(self, (x, y), evenOdd=False): 938 1026 """ … … 948 1036 # Notification Callbacks 949 1037 # ---------------------- 1038 1039 def endSelfNotificationObservation(self): 1040 if self.dispatcher is None: 1041 return 1042 for contour in self: 1043 self.endSelfContourNotificationObservation(contour) 1044 for component in self.components: 1045 self.endSelfComponentNotificationObservation(component) 1046 for anchor in self.anchors: 1047 self.endSelfAnchorNotificationObservation(anchor) 1048 for guideline in self.guidelines: 1049 self.endSelfGuidelinesNotificationObservation(guideline) 1050 self.endSelfLibNotificationObservation() 1051 self.endSelfImageNotificationObservation() 1052 super(Glyph, self).endSelfNotificationObservation() 1053 self._font = None 950 1054 951 1055 def _imageChanged(self, notification): … … 1041 1145 >>> glyph.bounds 1042 1146 (0, 0, 700, 700) 1147 1043 1148 >>> glyph = font['B'] 1044 1149 >>> glyph.bounds … … 1571 1676 1572 1677 >>> pointPen.beginPath(identifier="contour 1") 1573 >>> pointPen.endPath()1574 1678 Traceback (most recent call last): 1575 1679 ... 1576 1680 AssertionError 1681 >>> pointPen.endPath() 1577 1682 1578 1683 >>> pointPen.beginPath() 1579 1684 >>> pointPen.addPoint((0, 0)) 1580 1685 >>> pointPen.addPoint((0, 0), identifier="point 1") 1581 >>> pointPen.endPath()1582 1686 Traceback (most recent call last): 1583 1687 ... 1584 1688 AssertionError 1689 >>> pointPen.endPath() 1585 1690 1586 1691 >>> pointPen.addComponent("A", (1, 1, 1, 1, 1, 1), identifier="component 1") -
packages/defcon/branches/ufo3/Lib/defcon/objects/groups.py
r1014 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 … … 68 69 representationFactories = {} 69 70 71 def __init__(self, font=None): 72 self._font = None 73 if font is not None: 74 self._font = weakref.ref(font) 75 super(Groups, self).__init__() 76 self.beginSelfNotificationObservation() 77 78 # -------------- 79 # Parent Objects 80 # -------------- 81 82 def getParent(self): 83 return self.font 84 70 85 def _get_font(self): 71 return self.getParent() 86 if self._font is not None: 87 return self._font() 88 return None 72 89 73 90 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 91 92 # ------------------------ 93 # Notification Observation 94 # ------------------------ 95 96 def endSelfNotificationObservation(self): 97 super(Groups, self).endSelfNotificationObservation() 98 self._font = None 74 99 75 100 -
packages/defcon/branches/ufo3/Lib/defcon/objects/guideline.py
r1016 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 from defcon.objects.color import Color … … 30 31 representationFactories = {} 31 32 32 def __init__(self, guidelineDict=None): 33 def __init__(self, fontInfo=None, glyph=None, guidelineDict=None): 34 self._fontInfo = None 35 self._glyph = None 36 if fontInfo is not None: 37 self.fontInfo = fontInfo 38 if glyph is not None: 39 self.glyph = glyph 33 40 super(Guideline, self).__init__() 41 self.beginSelfNotificationObservation() 34 42 self._dirty = False 35 43 if guidelineDict is not None: … … 41 49 self.identifier = guidelineDict.get("identifier") 42 50 43 # ---------- 44 # Properties 45 # ---------- 46 47 # parents 51 # -------------- 52 # Parent Objects 53 # -------------- 54 55 def getParent(self): 56 if self._fontInfo is not None: 57 return self.fontInfo 58 elif self._glyph is not None: 59 return self.glyph 60 return None 48 61 49 62 def _get_font(self): 50 glyph = self.glyph 51 if glyph is not None: 52 return glyph.font 53 info = self.info 54 if info is not None: 55 return info.font 63 if self._fontInfo is not None: 64 return self.fontInfo.font 65 elif self._glyph is not None: 66 return self.glyph.font 56 67 return None 57 68 58 69 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 59 70 60 def _get_info(self): 61 from defcon.objects.info import Info 62 parent = self.getParent() 63 if not isinstance(parent, Info): 64 return None 65 return parent 66 67 info = property(_get_info, doc="The :class:`Info` that this object belongs to (if it is a font info guideline).") 71 def _get_fontInfo(self): 72 if self._fontInfo is not None: 73 return self._fontInfo() 74 return None 75 76 def _set_fontInfo(self, fontInfo): 77 assert self._fontInfo is None 78 assert self._glyph is None 79 if fontInfo is not None: 80 fontInfo = weakref.ref(fontInfo) 81 self._fontInfo = fontInfo 82 83 fontInfo = property(_get_fontInfo, _set_fontInfo, doc="The :class:`Info` that this object belongs to (if it is a font info guideline). This should not be set externally.") 68 84 69 85 def _get_layerSet(self): … … 73 89 return glyph.layerSet 74 90 75 layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this object belongs to (if it isn't a font aguideline).")91 layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this object belongs to (if it isn't a font info guideline).") 76 92 77 93 def _get_layer(self): … … 81 97 return glyph.layer 82 98 83 layer = property(_get_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font aguideline).")99 layer = property(_get_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font info guideline).") 84 100 85 101 def _get_glyph(self): 86 from defcon.objects.glyph import Glyph 87 parent = self.getParent() 88 if not isinstance(parent, Glyph): 89 return None 90 return parent 91 92 glyph = property(_get_glyph, doc="The :class:`Glyph` that this object belongs to (if it isn't a font a guideline).") 102 if self._glyph is not None: 103 return self._glyph() 104 return None 105 106 def _set_glyph(self, glyph): 107 assert self._fontInfo is None 108 assert self._glyph is None 109 if glyph is not None: 110 glyph = weakref.ref(glyph) 111 self._glyph = glyph 112 113 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this object belongs to (if it isn't a font info guideline). This should not be set externally.") 114 115 # ---------- 116 # Attributes 117 # ---------- 118 119 # x 93 120 94 121 def _get_x(self): … … 104 131 x = property(_get_x, _set_x, doc="The x coordinate. Setting this will post *Guideline.XChanged* and *Guideline.Changed* notifications.") 105 132 133 # y 134 106 135 def _get_y(self): 107 136 return self.get("y") … … 116 145 y = property(_get_y, _set_y, doc="The y coordinate. Setting this will post *Guideline.YChanged* and *Guideline.Changed* notifications.") 117 146 147 # angle 148 118 149 def _get_angle(self): 119 150 return self.get("angle") … … 128 159 angle = property(_get_angle, _set_angle, doc="The angle. Setting this will post *Guideline.AngleChanged* and *Guideline.Changed* notifications.") 129 160 161 # name 162 130 163 def _get_name(self): 131 164 return self.get("name") … … 139 172 140 173 name = property(_get_name, _set_name, doc="The name. Setting this will post *Guideline.NameChanged* and *Guideline.Changed* notifications.") 174 175 # color 141 176 142 177 def _get_color(self): … … 156 191 color = property(_get_color, _set_color, doc="The guideline's :class:`Color` object. When setting, the value can be a UFO color string, a sequence of (r, g, b, a) or a :class:`Color` object. Setting this posts *Guideline.ColorChanged* and *Guideline.Changed* notifications.") 157 192 158 # ------- 159 # Methods160 # ------- 193 # ---------- 194 # Identifier 195 # ---------- 161 196 162 197 def _get_identifiers(self): … … 164 199 parent = self.glyph 165 200 if parent is None: 166 parent = self. info201 parent = self.fontInfo 167 202 if parent is not None: 168 203 identifiers = parent.identifiers … … 203 238 self.identifier = identifier 204 239 240 # ------------------------ 241 # Notification Observation 242 # ------------------------ 243 244 def endSelfNotificationObservation(self): 245 super(Guideline, self).endSelfNotificationObservation() 246 self._fontInfo = None 247 self._glyph = None 248 205 249 206 250 def _test(): … … 267 311 False 268 312 269 >>> g = Guideline( dict(x=1, y=2, angle=3, name="4", identifier="5", color="1,1,1,1"))313 >>> g = Guideline(guidelineDict=dict(x=1, y=2, angle=3, name="4", identifier="5", color="1,1,1,1")) 270 314 >>> g.x, g.y, g.angle, g.name, g.identifier, g.color 271 315 (1, 2, 3, '4', '5', '1,1,1,1') -
packages/defcon/branches/ufo3/Lib/defcon/objects/image.py
r1014 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 from defcon.objects.color import Color … … 36 37 representationFactories = {} 37 38 38 def __init__(self, imageDict=None): 39 def __init__(self, glyph=None, imageDict=None): 40 self._glyph = None 41 self.glyph = glyph 39 42 super(Image, self).__init__() 43 self.beginSelfNotificationObservation() 40 44 self["fileName"] = None 41 45 self["color"] = None … … 47 51 self._dirty = False 48 52 49 # ---------- 50 # Properties 51 # ---------- 52 53 # parents 53 # -------------- 54 # Parent Objects 55 # -------------- 56 57 def getParent(self): 58 return self.glyph 54 59 55 60 def _get_font(self): … … 78 83 79 84 def _get_glyph(self): 80 return self.getParent() 81 82 glyph = property(_get_glyph, doc="The :class:`Glyph` that this image belongs to.") 85 if self._glyph is None: 86 return None 87 return self._glyph() 88 89 def _set_glyph(self, glyph): 90 assert self._glyph is None 91 if glyph is not None: 92 glyph = weakref.ref(glyph) 93 self._glyph = glyph 94 95 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this image belongs to. This should not be set externally.") 96 97 # ---------- 98 # Attributes 99 # ---------- 100 101 # file name 83 102 84 103 def _get_fileName(self): … … 93 112 94 113 fileName = property(_get_fileName, _set_fileName, doc="The file name the image. Setting this will posts *Image.Changed* and *Image.FileNameChanged* notifications.") 114 115 # transformation 95 116 96 117 def _get_transformation(self): … … 117 138 transformation = property(_get_transformation, _set_transformation, doc="The transformation matrix for the image. Setting this will posts *Image.Changed* and *Image.TransformationChanged* notifications.") 118 139 140 # color 141 119 142 def _get_color(self): 120 143 return self.get("color") … … 133 156 color = property(_get_color, _set_color, doc="The image's :class:`Color` object. When setting, the value can be a UFO color string, a sequence of (r, g, b, a) or a :class:`Color` object. Setting this posts *Image.ColorChanged* and *Image.Changed* notifications.") 134 157 158 # ------------------------ 159 # Notification Observation 160 # ------------------------ 161 162 def endSelfNotificationObservation(self): 163 super(Image, self).endSelfNotificationObservation() 164 self._glyph = None 165 135 166 136 167 def _testAttributes(): … … 160 191 True 161 192 162 >>> i = Image( dict(fileName="foo.png", xScale="1", xyScale="2", yxScale="3", yScale="4", xOffset="5", yOffset="6", color="0,0,0,0"))193 >>> i = Image(imageDict=dict(fileName="foo.png", xScale="1", xyScale="2", yxScale="3", yScale="4", xOffset="5", yOffset="6", color="0,0,0,0")) 163 194 >>> i.fileName, i.transformation, i.color 164 195 ('foo.png', ('1', '2', '3', '4', '5', '6'), '0,0,0,0') … … 187 218 >>> font = Font(path) 188 219 >>> glyph = font.layers[None]["A"] 189 >>> glyph.image = Image()220 >>> glyph.image = glyph.instantiateImage() 190 221 >>> glyph.image.color = "1,1,1,1" 191 222 >>> glyph.image.fileName = "foo.png" -
packages/defcon/branches/ufo3/Lib/defcon/objects/imageSet.py
r1016 r1022 1 1 import os 2 2 import hashlib 3 import weakref 3 4 from ufoLib import UFOReader, UFOLibError 4 5 from defcon.objects.base import BaseObject … … 18 19 Name 19 20 =========================== 21 ImageSet.Changed 20 22 ImageSet.FileNamesChanged 21 23 ImageSet.ImageChanged … … 49 51 """ 50 52 53 changeNotificationName = "ImageSet.Changed" 51 54 representationFactories = {} 52 55 53 def __init__(self, fileNames=None): 56 def __init__(self, font=None): 57 self._font = None 58 if font is not None: 59 self._font = weakref.ref(font) 54 60 super(ImageSet, self).__init__() 61 self.beginSelfNotificationObservation() 55 62 self._data = {} 56 63 self._scheduledForDeletion = {} 57 64 65 # -------------- 66 # Parent Objects 67 # -------------- 68 69 def getParent(self): 70 return self.font 71 58 72 def _get_font(self): 59 return self.getParent() 73 if self._font is not None: 74 return self._font() 75 return None 60 76 61 77 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 78 79 # ---------- 80 # File Names 81 # ---------- 62 82 63 83 def _get_fileNames(self): … … 126 146 self.dirty = True 127 147 128 # ---- -----------129 # File Management130 # ---- -----------148 # ---- 149 # Save 150 # ---- 131 151 132 152 def getSaveProgressBarTickCount(self, formatVersion): … … 178 198 self.dirty = False 179 199 200 # --------------- 201 # File Management 202 # --------------- 203 180 204 def makeFileName(self, fileName): 181 205 """ … … 258 282 image = self[fileName] 259 283 284 # ------------------------ 285 # Notification Observation 286 # ------------------------ 287 288 def endSelfNotificationObservation(self): 289 super(ImageSet, self).endSelfNotificationObservation() 290 self._font = None 291 260 292 261 293 def _imageDict(data=None, dirty=False, digest=None, onDisk=True, onDiskModTime=None): -
packages/defcon/branches/ufo3/Lib/defcon/objects/info.py
r1016 r1022 2 2 # this file should not be edited by hand. 3 3 4 import weakref 4 5 from warnings import warn 5 6 import ufoLib … … 31 32 representationFactories = {} 32 33 33 def __init__(self, guidelineClass=None): 34 def __init__(self, font=None, guidelineClass=None): 35 if font is not None: 36 font = weakref.ref(font) 37 self._font = font 34 38 super(Info, self).__init__() 39 self.beginSelfNotificationObservation() 35 40 self._identifiers = set() 36 41 if guidelineClass is None: … … 146 151 self._year = None 147 152 153 def __del__(self): 154 super(Info, self).__del__() 155 self._guidelines = None 156 157 def getParent(self): 158 return self.font 159 148 160 149 161 def _get_font(self): 150 return self.getParent() 162 if self._font is not None: 163 return self._font() 164 return None 151 165 152 166 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") … … 2321 2335 guidelines = property(_get_guidelines, _set_guidelines, doc="An ordered list of :class:`Guideline` objects stored in the info. Setting this will post a *Info.Changed* notification along with any notifications posted by the :py:meth:`Info.appendGuideline` and :py:meth:`Info.clearGuidelines` methods.") 2322 2336 2323 def _setParentDataInGuideline(self, guideline): 2324 guideline.setParent(self) 2325 if self.dispatcher is not None: 2326 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 2327 2328 def _removeParentDataInGuideline(self, guideline): 2329 if self.dispatcher is not None: 2330 guideline.removeObserver(observer=self, notification="Guideline.Changed") 2331 guideline.setParent(None) 2337 def instantiateGuideline(self, guidelineDict=None): 2338 guideline = self._guidelineClass( 2339 fontInfo=self, 2340 guidelineDict=guidelineDict 2341 ) 2342 return guideline 2343 2344 def beginSelfGuidelineNotificationObservation(self, guideline): 2345 if guideline.dispatcher is None: 2346 return 2347 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 2348 2349 def endSelfGuidelineNotificationObservation(self, guideline): 2350 if guideline.dispatcher is None: 2351 return 2352 guideline.endSelfNotificationObservation() 2353 guideline.removeObserver(observer=self, notification="Guideline.Changed") 2332 2354 2333 2355 def appendGuideline(self, guideline): … … 2355 2377 assert guideline not in self._guidelines 2356 2378 if not isinstance(guideline, self._guidelineClass): 2357 guideline = self._guidelineClass(guideline) 2358 if guideline.identifier is not None: 2359 identifiers = self._identifiers 2360 assert guideline.identifier not in identifiers 2379 guideline = self.instantiateGuideline(guidelineDict=guideline) 2380 assert guideline.fontInfo in (self, None), "This guideline belongs to another font." 2381 if guideline.fontInfo is None: 2382 assert guideline.glyph is None, "This guideline belongs to a glyph." 2383 if guideline.fontInfo is None: 2361 2384 if guideline.identifier is not None: 2362 identifiers.add(guideline.identifier) 2363 if guideline.info != self: 2364 self._setParentDataInGuideline(guideline) 2385 identifiers = self._identifiers 2386 assert guideline.identifier not in identifiers 2387 if guideline.identifier is not None: 2388 identifiers.add(guideline.identifier) 2389 guideline.fontInfo = self 2390 guideline.beginSelfNotificationObservation() 2391 self.beginSelfGuidelineNotificationObservation(guideline) 2365 2392 self._guidelines.insert(index, guideline) 2366 2393 self.postNotification("Info.GuidelinesChanged") … … 2377 2404 self._identifiers.remove(guideline.identifier) 2378 2405 self._guidelines.remove(guideline) 2379 self. _removeParentDataInGuideline(guideline)2406 self.endSelfGuidelineNotificationObservation(guideline) 2380 2407 self.postNotification("Info.GuidelinesChanged") 2381 2408 self.dirty = True … … 2398 2425 self.releaseHeldNotifications() 2399 2426 2400 # ---------------------- 2401 # Notification Callbacks 2402 # ---------------------- 2427 # ------------------------ 2428 # Notification Observation 2429 # ------------------------ 2430 2431 def endSelfNotificationObservation(self): 2432 if self.dispatcher is None: 2433 return 2434 for guideline in self.guidelines: 2435 self.endSelfGuidelinesNotificationObservation(guideline) 2436 super(Info, self).endSelfNotificationObservation() 2437 self._font = None 2403 2438 2404 2439 def _guidelineChanged(self, notification): -
packages/defcon/branches/ufo3/Lib/defcon/objects/kerning.py
r1014 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 … … 48 49 representationFactories = {} 49 50 51 def __init__(self, font=None): 52 self._font = None 53 if font is not None: 54 self._font = weakref.ref(font) 55 super(Kerning, self).__init__() 56 self.beginSelfNotificationObservation() 57 58 # -------------- 59 # Parent Objects 60 # -------------- 61 62 def getParent(self): 63 return self.font 64 50 65 def _get_font(self): 51 return self.getParent() 66 if self._font is not None: 67 return self._font() 68 return None 52 69 53 70 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 54 71 72 # ------------- 73 # Pair Handling 74 # ------------- 75 55 76 def get(self, pair, default=0): 56 77 return super(Kerning, self).get(pair, default) 78 79 # ------------------------ 80 # Notification Observation 81 # ------------------------ 82 83 def endSelfNotificationObservation(self): 84 super(Kerning, self).endSelfNotificationObservation() 85 self._font = None 57 86 58 87 -
packages/defcon/branches/ufo3/Lib/defcon/objects/layer.py
r1016 r1022 53 53 representationFactories = {} 54 54 55 def __init__(self, glyphSet=None, libClass=None, unicodeDataClass=None,55 def __init__(self, layerSet=None, glyphSet=None, libClass=None, unicodeDataClass=None, 56 56 guidelineClass=None, glyphClass=None, 57 glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None): 57 glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None, glyphImageClass=None): 58 59 if layerSet is not None: 60 layerSet = weakref.ref(layerSet) 61 self._layerSet = layerSet 58 62 super(Layer, self).__init__() 63 self.beginSelfNotificationObservation() 59 64 60 65 self._name = None … … 66 71 if unicodeDataClass is None: 67 72 unicodeDataClass = UnicodeData 68 69 73 self._glyphClass = glyphClass 70 74 self._glyphContourClass = glyphContourClass … … 72 76 self._glyphComponentClass = glyphComponentClass 73 77 self._glyphAnchorClass = glyphAnchorClass 78 self._glyphImageClass = glyphImageClass 74 79 self._libClass = libClass 75 80 self._guidelineClass = guidelineClass 76 77 self._dispatcher = None 81 self._unicodeDataClass = unicodeDataClass 82 78 83 self._color = None 79 84 self._lib = None 80 self._unicodeData = unicodeDataClass()81 self. _unicodeData.setParent(self)85 self._unicodeData = self.instantiateUnicodeData() 86 self.beginSelfUnicodeDataNotificationObservation() 82 87 83 88 self._directory = None … … 91 96 92 97 if glyphSet is not None: 98 self._unicodeData.disableNotifications() 93 99 self._keys = set(self._glyphSet.keys()) 94 100 cmap = {} … … 100 106 cmap[code] = [glyphName] 101 107 self._unicodeData.update(cmap) 102 103 # ------------- 104 # Dict Behavior 105 # ------------- 106 107 def _instantiateGlyphObject(self): 108 self._unicodeData.enableNotifications() 109 110 def __del__(self): 111 super(Layer, self).__del__() 112 self._glyphs = None 113 self._lib = None 114 self._unicodeData = None 115 116 # -------------- 117 # Parent Objects 118 # -------------- 119 120 def getParent(self): 121 return self.layerSet 122 123 def _get_font(self): 124 layerSet = self.layerSet 125 if layerSet is None: 126 return None 127 return layerSet.font 128 129 font = property(_get_font, doc="The :class:`Font` that this layer belongs to.") 130 131 def _get_layerSet(self): 132 if self._layerSet is None: 133 return None 134 return self._layerSet() 135 136 layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this layer belongs to.") 137 138 # -------------- 139 # Glyph Creation 140 # -------------- 141 142 def instantiateGlyphObject(self): 108 143 glyph = self._glyphClass( 144 layer=self, 109 145 contourClass=self._glyphContourClass, 110 146 pointClass=self._glyphPointClass, … … 112 148 anchorClass=self._glyphAnchorClass, 113 149 guidelineClass=self._guidelineClass, 114 libClass=self._libClass 150 libClass=self._libClass, 151 imageClass=self._glyphImageClass 115 152 ) 116 153 return glyph 117 154 118 def _loadGlyph(self, name): 155 def beginSelfGlyphNotificationObservation(self, glyph): 156 glyph.addObserver(observer=self, methodName="_glyphDirtyStateChange", notification="Glyph.Changed") 157 glyph.addObserver(observer=self, methodName="_glyphNameChange", notification="Glyph.NameChanged") 158 glyph.addObserver(observer=self, methodName="_glyphUnicodesChange", notification="Glyph.UnicodesChanged") 159 160 def endSelfGlyphNotificationObservation(self, glyph): 161 if glyph.dispatcher is None: 162 return 163 glyph.removeObserver(observer=self, notification="Glyph.Changed") 164 glyph.removeObserver(observer=self, notification="Glyph.NameChanged") 165 glyph.removeObserver(observer=self, notification="Glyph.UnicodesChanged") 166 glyph.endSelfNotificationObservation() 167 168 def loadGlyph(self, name): 169 """ 170 Load a glyph from the glyph set. This should not be called 171 externally, but subclasses may overrode it for custom behavior. 172 """ 119 173 if self._glyphSet is None or not self._glyphSet.has_key(name): 120 174 raise KeyError, "%s not in layer" % name 121 glyph = self._instantiateGlyphObject() 175 glyph = self.instantiateGlyphObject() 176 self.beginSelfGlyphNotificationObservation(glyph) 177 glyph.disableNotifications() 122 178 pointPen = glyph.getPointPen() 123 179 self._glyphSet.readGlyph(glyphName=name, glyphObject=glyph, pointPen=pointPen) 124 180 glyph.dirty = False 181 glyph.enableNotifications() 125 182 self._glyphs[name] = glyph 126 self._setParentDataInGlyph(glyph)127 183 self._stampGlyphDataState(glyph) 128 184 return glyph 129 130 def _setParentDataInGlyph(self, glyph):131 # the parent of a glyph is always the font, not the layer132 font = self.font133 if font is not None:134 glyph.setParent(font)135 glyph.layer = self136 glyph.addObserver(observer=self, methodName="_glyphDirtyStateChange", notification="Glyph.Changed")137 glyph.addObserver(observer=self, methodName="_glyphNameChange", notification="Glyph.NameChanged")138 glyph.addObserver(observer=self, methodName="_glyphUnicodesChange", notification="Glyph.UnicodesChanged")139 140 def _removeParentDataInGlyph(self, glyph):141 glyph.removeObserver(observer=self, notification="Glyph.Changed")142 glyph.removeObserver(observer=self, notification="Glyph.NameChanged")143 glyph.removeObserver(observer=self, notification="Glyph.UnicodesChanged")144 glyph.layer = None145 glyph.setParent(None)146 185 147 186 def newGlyph(self, name): … … 155 194 if name in self: 156 195 self._unicodeData.removeGlyphData(name, self[name].unicodes) 157 glyph = self._instantiateGlyphObject() 196 glyph = self.instantiateGlyphObject() 197 self.beginSelfGlyphNotificationObservation(glyph) 198 glyph.disableNotifications() 158 199 glyph.name = name 200 glyph.enableNotifications() 159 201 self._glyphs[name] = glyph 160 self._setParentDataInGlyph(glyph)161 202 if name in self._scheduledForDeletion: 162 203 del self._scheduledForDeletion[name] … … 194 235 return dest 195 236 237 # ------------- 238 # Dict Behavior 239 # ------------- 240 196 241 def __iter__(self): 197 242 names = self.keys() … … 203 248 def __getitem__(self, name): 204 249 if name not in self._glyphs: 205 self. _loadGlyph(name)250 self.loadGlyph(name) 206 251 return self._glyphs[name] 207 252 … … 215 260 if name in self._glyphs: 216 261 glyph = self._glyphs.pop(name) 217 self. _removeParentDataInGlyph(glyph)262 self.endSelfGlyphNotificationObservation(glyph) 218 263 dataOnDiskTimeStamp = glyph._dataOnDiskTimeStamp 219 264 dataOnDisk = glyph._dataOnDisk … … 245 290 # ---------- 246 291 247 def _get_font(self): 248 layerSet = self.layerSet 249 if layerSet is None: 250 return None 251 return layerSet.font 252 253 font = property(_get_font, doc="The :class:`Font` that this layer belongs to.") 254 255 def _get_layerSet(self): 256 return self.getParent() 257 258 layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this layer belongs to.") 292 # name 259 293 260 294 def _set_name(self, value): … … 270 304 271 305 name = property(_get_name, _set_name, doc="The name of the layer. Setting this posts *Layer.NameChanged* and *Layer.Changed* notifications.") 306 307 # color 272 308 273 309 def _get_color(self): … … 287 323 288 324 color = property(_get_color, _set_color, doc="The layer's :class:`Color` object. When setting, the value can be a UFO color string, a sequence of (r, g, b, a) or a :class:`Color` object. Setting this posts *Layer.ColorChanged* and *Layer.Changed* notifications.") 325 326 # ------------- 327 # Data Skimmers 328 # ------------- 329 330 # outlines 289 331 290 332 def _get_glyphsWithOutlines(self): … … 308 350 309 351 glyphsWithOutlines = property(_get_glyphsWithOutlines, doc="A list of glyphs containing outlines.") 352 353 # component references 310 354 311 355 def _get_componentReferences(self): … … 334 378 componentReferences = property(_get_componentReferences, doc="A dict of describing the component relationships in the layer. The dictionary is of form ``{base glyph : [references]}``.") 335 379 380 # image references 381 336 382 def _get_imageReferences(self): 337 383 found = {} … … 357 403 imageReferences = property(_get_imageReferences, doc="A dict of describing the image file references in the layer. The dictionary is of form ``{image file name : [references]}``.") 358 404 405 # bounds 406 359 407 def _get_bounds(self): 360 408 fontRect = None … … 370 418 371 419 bounds = property(_get_bounds, doc="The bounds of all glyphs in the layer. This can be an expensive operation.") 420 421 # control point bounds 372 422 373 423 def _get_controlPointBounds(self): … … 437 487 # ----------- 438 488 489 # lib 490 491 def instantiateLib(self): 492 lib = self._libClass( 493 layer=self 494 ) 495 return lib 496 497 def beginSelfLibNotificationObservation(self): 498 self._lib.addObserver(observer=self, methodName="_libDirtyStateChange", notification="Lib.Changed") 499 500 def endSelfLibNotificationObservation(self): 501 if self._lib is None: 502 return 503 if self._lib.dispatcher is None: 504 return 505 self._lib.removeObserver(observer=self, notification="Lib.Changed") 506 self._lib.endSelfNotificationObservation() 507 439 508 def _get_lib(self): 440 509 if self._lib is None: 441 self._lib = self._libClass() 442 self._lib.setParent(self) 443 self._lib.addObserver(observer=self, methodName="_libDirtyStateChange", notification="Lib.Changed") 510 self._lib = self.instantiateLib() 511 self.beginSelfLibNotificationObservation() 444 512 return self._lib 445 513 … … 451 519 lib = property(_get_lib, _set_lib, doc="The layer's :class:`Lib` object.") 452 520 521 # unicode data 522 523 def instantiateUnicodeData(self): 524 unicodeData = self._unicodeDataClass( 525 layer=self 526 ) 527 return unicodeData 528 529 def beginSelfUnicodeDataNotificationObservation(self): 530 pass 531 532 def endSelfUnicodeDataNotificationObservation(self): 533 if self._unicodeData.dispatcher is None: 534 return 535 self._unicodeData.endSelfNotificationObservation() 536 453 537 def _get_unicodeData(self): 454 538 return self._unicodeData … … 456 540 unicodeData = property(_get_unicodeData, doc="The layer's :class:`UnicodeData` object.") 457 541 458 # ---- ---459 # Methods460 # ---- ---542 # ---- 543 # Save 544 # ---- 461 545 462 546 def getSaveProgressBarTickCount(self, formatVersion): … … 566 650 for glyphName in glyphNames: 567 651 if glyphName not in self._glyphs: 568 self. _loadGlyph(glyphName)652 self.loadGlyph(glyphName) 569 653 else: 570 654 glyph = self._glyphs[glyphName] … … 595 679 referenceChanges.add(reference) 596 680 597 # ---------------------- 598 # Notification Callbacks 599 # ---------------------- 681 # ------------------------ 682 # Notification Observation 683 # ------------------------ 684 685 def endSelfNotificationObservation(self): 686 if self.dispatcher is None: 687 return 688 for glyph in self._glyphs.values(): 689 self.endSelfGlyphNotificationObservation(glyph) 690 self.endSelfLibNotificationObservation() 691 self.endSelfUnicodeDataNotificationObservation() 692 super(Layer, self).endSelfNotificationObservation() 693 self._layerSet = None 600 694 601 695 def _glyphDirtyStateChange(self, notification): … … 853 947 >>> glyph = layer['A'] 854 948 >>> del layer['A'] 855 >>> glyph.getParent()856 949 >>> layer.dirty 857 950 True -
packages/defcon/branches/ufo3/Lib/defcon/objects/layerSet.py
r1016 r1022 1 import weakref 1 2 from ufoLib import UFOReader 2 3 from defcon.objects.base import BaseObject … … 38 39 representationFactories = {} 39 40 40 def __init__(self, layerClass=None, libClass=None, unicodeDataClass=None,41 def __init__(self, font=None, layerClass=None, libClass=None, unicodeDataClass=None, 41 42 guidelineClass=None, glyphClass=None, 42 glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None): 43 glyphContourClass=None, glyphPointClass=None, glyphComponentClass=None, glyphAnchorClass=None, 44 glyphImageClass=None): 45 46 if font is not None: 47 font = weakref.ref(font) 48 self._font = font 43 49 super(LayerSet, self).__init__() 50 self.beginSelfNotificationObservation() 51 44 52 if layerClass is None: 45 53 layerClass = Layer 46 47 54 self._layerClass = layerClass 48 55 self._libClass = libClass … … 53 60 self._glyphComponentClass = glyphComponentClass 54 61 self._glyphAnchorClass = glyphAnchorClass 62 self._glyphImageClass = glyphImageClass 55 63 self._guidelineClass = guidelineClass 56 64 … … 61 69 self._layerActionHistory = [] 62 70 71 def __del__(self): 72 super(LayerSet, self).__del__() 73 self._layers = None 74 75 # -------------- 76 # Parent Objects 77 # -------------- 78 79 def getParent(self): 80 return self.font 81 63 82 def _get_font(self): 64 return self.getParent() 83 if self._font is None: 84 return None 85 return self._font() 65 86 66 87 font = property(_get_font, doc="The :class:`Font` that this layer set belongs to.") 88 89 # ------------- 90 # Default Layer 91 # ------------- 67 92 68 93 def _get_defaultLayerName(self): … … 92 117 defaultLayer = property(_get_defaultLayer, _set_defaultLayer, doc="The default :class:`Layer` object. Setting this will post *LayerSet.DefaultLayerChanged* and *LayerSet.Changed* notifications.") 93 118 119 # ----------- 120 # Layer Order 121 # ----------- 122 94 123 def _get_layerOrder(self): 95 124 return list(self._layerOrder) … … 108 137 109 138 # ------------- 110 # Dict Behavior139 # Layer Creation 111 140 # ------------- 112 141 113 def _instantiateLayerObject(self, glyphSet):142 def instantiateLayer(self, glyphSet): 114 143 layer = self._layerClass( 144 layerSet=self, 115 145 glyphSet=glyphSet, 116 146 libClass=self._libClass, … … 121 151 glyphComponentClass=self._glyphComponentClass, 122 152 glyphAnchorClass=self._glyphAnchorClass, 123 guidelineClass=self._guidelineClass 153 guidelineClass=self._guidelineClass, 154 glyphImageClass=self._glyphImageClass 124 155 ) 125 156 return layer 126 157 127 def _setParentDataInLayer(self, layer): 128 layer.setParent(self) 158 def beginSelfLayerNotificationObservation(self, layer): 129 159 layer.addObserver(observer=self, methodName="_layerDirtyStateChange", notification="Layer.Changed") 130 160 layer.addObserver(observer=self, methodName="_layerNameChange", notification="Layer.NameChanged") 131 161 132 def _removeParentDataInLayer(self, layer): 162 def endSelfLayerNotificationObservation(self, layer): 163 if layer.dispatcher is None: 164 return 133 165 layer.removeObserver(observer=self, notification="Layer.Changed") 134 166 layer.removeObserver(observer=self, notification="Layer.NameChanged") 135 layer. setParent(None)167 layer.endSelfNotificationObservation() 136 168 137 169 def newLayer(self, name, glyphSet=None): … … 146 178 raise KeyError("A layer named \"%s\" already exists." % name) 147 179 assert name is not None 148 layer = self._instantiateLayerObject(glyphSet) 180 layer = self.instantiateLayer(glyphSet) 181 self.beginSelfLayerNotificationObservation(layer) 182 layer.disableNotifications() 149 183 layer.name = name 150 self._setParentDataInLayer(layer)151 184 if glyphSet is None: 152 185 layer.dirty = True 153 186 else: 154 187 glyphSet.readLayerInfo(layer) 188 layer.dirty = False 189 layer.enableNotifications() 155 190 self._stampLayerInfoDataState(layer) 156 191 self._layers[name] = layer … … 162 197 return layer 163 198 199 # ------------- 200 # Dict Behavior 201 # ------------- 202 164 203 def __iter__(self): 165 204 names = self.layerOrder … … 180 219 raise KeyError("%s not in layers" % name) 181 220 self.postNotification("LayerSet.LayerWillBeDeleted", data=dict(name=name)) 221 layer = self._layers[name] 222 self.endSelfLayerNotificationObservation(layer) 182 223 del self._layers[name] 183 224 self._layerOrder.remove(name) … … 195 236 return name in self._layers 196 237 197 # ---- ---198 # Methods199 # ---- ---238 # ---- 239 # Save 240 # ---- 200 241 201 242 def getSaveProgressBarTickCount(self, formatVersion): … … 281 322 self._layerActionHistory.append(dict(action="new", name=layer.name)) 282 323 283 # ---------------------- 284 # Notification Callbacks 285 # ---------------------- 324 # ------------------------ 325 # Notification Observation 326 # ------------------------ 327 328 def endSelfNotificationObservation(self): 329 if self.dispatcher is None: 330 return 331 for layer in self_layers.values(): 332 self.endSelfLayerNotificationObservation(layer) 333 super(LayerSet, self).endSelfNotificationObservation() 334 self._font = None 286 335 287 336 def _layerDirtyStateChange(self, notification): -
packages/defcon/branches/ufo3/Lib/defcon/objects/lib.py
r1014 r1022 1 import weakref 1 2 from defcon.objects.base import BaseDictObject 2 3 … … 47 48 representationFactories = {} 48 49 49 # parents 50 def __init__(self, font=None, layer=None, glyph=None): 51 self._font = None 52 self._layer = None 53 self._glyph = None 54 if font is not None: 55 self.font = font 56 if layer is not None: 57 self.layer = layer 58 if glyph is not None: 59 self.glyph = glyph 60 super(Lib, self).__init__() 61 self.beginSelfNotificationObservation() 62 63 # -------------- 64 # Parent Objects 65 # -------------- 66 67 def getParent(self): 68 if self._font is not None: 69 return self.font 70 elif self._layer is not None: 71 return self.layer 72 elif self._glyph is not None: 73 return self.glyph 74 return None 50 75 51 76 def _get_font(self): 52 from defcon.objects.glyph import Glyph 53 parent = self.getParent() 54 if isinstance(parent, Glyph): 55 return parent.font 56 return parent 77 if self._font is not None: 78 return self._font() 79 elif self._layer is not None: 80 return self.layer.font 81 elif self._glyph is not None: 82 return self.glyph.font 83 return None 57 84 58 font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 85 def _set_font(self, font): 86 assert self._font is None 87 assert self._layer is None 88 assert self._glyph is None 89 if font is not None: 90 font = weakref.ref(font) 91 self._font = font 92 93 font = property(_get_font, _set_font, doc="The :class:`Font` that this object belongs to. This should not be set externally.") 59 94 60 95 def _get_layerSet(self): … … 67 102 68 103 def _get_layer(self): 69 glyph = self.glyph 70 if glyph is None: 71 return None 72 return glyph.layer 104 if self._layer is not None: 105 return self._layer() 106 elif self._glyph is not None: 107 return self.glyph.layer 108 return None 73 109 74 layer = property(_get_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font lib).") 110 def _set_layer(self, layer): 111 assert self._font is None 112 assert self._layer is None 113 assert self._glyph is None 114 if layer is not None: 115 layer = weakref.ref(layer) 116 self._layer = layer 117 118 layer = property(_get_layer, _set_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font lib). This should not be set externally.") 75 119 76 120 def _get_glyph(self): 77 from defcon.objects.font import Font 78 parent = self.getParent() 79 if isinstance(parent, Font): 80 return None 81 return parent 121 if self._glyph is not None: 122 return self._glyph() 123 return None 82 124 83 glyph = property(_get_glyph, doc="The :class:`Glyph` that this object belongs to (if it isn't a font lib).") 125 def _set_glyph(self, glyph): 126 assert self._font is None 127 assert self._layer is None 128 assert self._glyph is None 129 if glyph is not None: 130 glyph = weakref.ref(glyph) 131 self._glyph = glyph 84 132 133 glyph = property(_get_glyph, _set_glyph, doc="The :class:`Glyph` that this object belongs to (if it isn't a font or layer lib). This should not be set externally.") 134 135 # ------------------------ 136 # Notification Observation 137 # ------------------------ 138 139 def endSelfNotificationObservation(self): 140 super(Lib, self).endSelfNotificationObservation() 141 self._font = None 142 self._layer = None 143 self._glyph =None 85 144 86 145 -
packages/defcon/branches/ufo3/Lib/defcon/objects/uniData.py
r1014 r1022 1 import weakref 1 2 import unicodedata 2 3 from fontTools.agl import AGL2UV … … 44 45 representationFactories = {} 45 46 46 def __init__(self): 47 def __init__(self, layer=None): 48 self._layer = None 49 if layer is not None: 50 self._layer = weakref.ref(layer) 47 51 super(UnicodeData, self).__init__() 52 self.beginSelfNotificationObservation() 48 53 self._glyphNameToForcedUnicode = {} 49 54 self._forcedUnicodeToGlyphName = {} 55 56 # -------------- 57 # Parent Objects 58 # -------------- 59 60 def getParent(self): 61 return self.layer 50 62 51 63 def _get_font(self): … … 963 975 return glyphNames 964 976 977 # ------------------------ 978 # Notification Observation 979 # ------------------------ 980 981 def endSelfNotificationObservation(self): 982 super(UnicodeData, self).endSelfNotificationObservation() 983 self._layer = None 984 965 985 966 986 # ----- -
packages/defcon/branches/ufo3/Lib/defcon/pens/glyphObjectPointPen.py
r957 r1022 8 8 9 9 def beginPath(self, identifier=None, **kwargs): 10 self._contour = self._glyph.contourClass(pointClass=self._glyph.pointClass) 10 self._contour = self._glyph.instantiateContour() 11 self._contour.disableNotifications() 11 12 self._contour.identifier = identifier 12 13 … … 14 15 self._contour.dirty = False 15 16 self._glyph.appendContour(self._contour) 17 self._contour.enableNotifications() 16 18 self._contour = None 17 19 -
packages/defcon/branches/ufo3/Lib/defcon/test/testAll.py
r967 r1022 17 17 from defcon.objects import imageSet 18 18 from defcon.objects import image 19 from defcon.objects import dataSet 19 20 20 21 test = [ … … 34 35 groups, 35 36 imageSet, 36 image 37 image, 38 dataSet 37 39 ] 38 40 -
packages/defcon/branches/ufo3/tools/infoObjectGenerator.py
r1016 r1022 68 68 print 69 69 70 print "import weakref" 70 71 print "from warnings import warn" 71 72 print "import ufoLib" … … 102 103 print 103 104 104 print " def __init__(self, guidelineClass=None):" 105 print " def __init__(self, font=None, guidelineClass=None):" 106 print " if font is not None:" 107 print " font = weakref.ref(font)" 108 print " self._font = font" 105 109 print " super(Info, self).__init__()" 110 print " self.beginSelfNotificationObservation()" 106 111 print " self._identifiers = set()" 107 112 print " if guidelineClass is None:" 108 113 print " guidelineClass = Guideline" 109 114 print " self._guidelineClass = guidelineClass" 110 111 115 112 116 defaults = dict( … … 127 131 print 128 132 133 print " def __del__(self):" 134 print " super(Info, self).__del__()" 135 print " self._guidelines = None" 136 print 137 138 print " def getParent(self):" 139 print " return self.font" 140 print 141 129 142 print 130 143 print " def _get_font(self):" 131 print " return self.getParent()" 144 print " if self._font is not None:" 145 print " return self._font()" 146 print " return None" 132 147 print 133 148 print " font = property(_get_font, doc=\"The :class:`Font` that this object belongs to.\")" … … 192 207 guidelines = property(_get_guidelines, _set_guidelines, doc="An ordered list of :class:`Guideline` objects stored in the info. Setting this will post a *Info.Changed* notification along with any notifications posted by the :py:meth:`Info.appendGuideline` and :py:meth:`Info.clearGuidelines` methods.") 193 208 194 def _setParentDataInGuideline(self, guideline): 195 guideline.setParent(self) 196 if self.dispatcher is not None: 197 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 198 199 def _removeParentDataInGuideline(self, guideline): 200 if self.dispatcher is not None: 201 guideline.removeObserver(observer=self, notification="Guideline.Changed") 202 guideline.setParent(None) 209 def instantiateGuideline(self, guidelineDict=None): 210 guideline = self._guidelineClass( 211 fontInfo=self, 212 guidelineDict=guidelineDict 213 ) 214 return guideline 215 216 def beginSelfGuidelineNotificationObservation(self, guideline): 217 if guideline.dispatcher is None: 218 return 219 guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed") 220 221 def endSelfGuidelineNotificationObservation(self, guideline): 222 if guideline.dispatcher is None: 223 return 224 guideline.endSelfNotificationObservation() 225 guideline.removeObserver(observer=self, notification="Guideline.Changed") 203 226 204 227 def appendGuideline(self, guideline): … … 226 249 assert guideline not in self._guidelines 227 250 if not isinstance(guideline, self._guidelineClass): 228 guideline = self._guidelineClass(guideline) 229 if guideline.identifier is not None: 230 identifiers = self._identifiers 231 assert guideline.identifier not in identifiers 251 guideline = self.instantiateGuideline(guidelineDict=guideline) 252 assert guideline.fontInfo in (self, None), "This guideline belongs to another font." 253 if guideline.fontInfo is None: 254 assert guideline.glyph is None, "This guideline belongs to a glyph." 255 if guideline.fontInfo is None: 232 256 if guideline.identifier is not None: 233 identifiers.add(guideline.identifier) 234 if guideline.info != self: 235 self._setParentDataInGuideline(guideline) 257 identifiers = self._identifiers 258 assert guideline.identifier not in identifiers 259 if guideline.identifier is not None: 260 identifiers.add(guideline.identifier) 261 guideline.fontInfo = self 262 guideline.beginSelfNotificationObservation() 263 self.beginSelfGuidelineNotificationObservation(guideline) 236 264 self._guidelines.insert(index, guideline) 237 265 self.postNotification("Info.GuidelinesChanged") … … 248 276 self._identifiers.remove(guideline.identifier) 249 277 self._guidelines.remove(guideline) 250 self. _removeParentDataInGuideline(guideline)278 self.endSelfGuidelineNotificationObservation(guideline) 251 279 self.postNotification("Info.GuidelinesChanged") 252 280 self.dirty = True … … 269 297 self.releaseHeldNotifications() 270 298 271 # ---------------------- 272 # Notification Callbacks 273 # ---------------------- 299 # ------------------------ 300 # Notification Observation 301 # ------------------------ 302 303 def endSelfNotificationObservation(self): 304 if self.dispatcher is None: 305 return 306 for guideline in self.guidelines: 307 self.endSelfGuidelinesNotificationObservation(guideline) 308 super(Info, self).endSelfNotificationObservation() 309 self._font = None 274 310 275 311 def _guidelineChanged(self, notification):
Note: See TracChangeset
for help on using the changeset viewer.
