Changeset 1022


Ignore:
Timestamp:
11/30/11 17:32:55 (18 months ago)
Author:
tal
Message:
Massive rewrite of the parenting system. getParent is now deprecated (though it won't issue warnings). Each object has a tree of parent objects (currently) retrieved dynamically by using only one weakref to the object that directly owns the sub-object.

Additionally, and the reason the above changes were made, objects now have a built in self-observation system. These self-observations are removed from the dispatcher whenever the object goes away. This should hopefully resolve a real/theoretical problem of the dispatcher overflowing with dead objects.

This is all a bit slow. Speed is the next step.
Location:
packages/defcon/branches/ufo3
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • packages/defcon/branches/ufo3/Lib/defcon/objects/anchor.py

    r1016 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23from defcon.objects.color import Color 
     
    2930    representationFactories = {} 
    3031 
    31     def __init__(self, anchorDict=None): 
     32    def __init__(self, glyph=None, anchorDict=None): 
     33        self._glyph = None 
     34        self.glyph = glyph 
    3235        super(Anchor, self).__init__() 
     36        self.beginSelfNotificationObservation() 
    3337        self._dirty = False 
    3438        if anchorDict is not None: 
     
    4145    # parents 
    4246 
     47    def getParent(self): 
     48        return self.glyph 
     49 
    4350    def _get_font(self): 
    4451        glyph = self.glyph 
     
    6673 
    6774    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.") 
    7186 
    7287    # coordinates 
     
    171186        self.identifier = identifier 
    172187 
    173     # ------- 
    174     # Methods 
    175     # ------- 
     188    # ---- 
     189    # Move 
     190    # ---- 
    176191 
    177192    def move(self, (x, y)): 
     
    183198        self.x += x 
    184199        self.y += y 
     200 
     201    # ------------------------ 
     202    # Notification Observation 
     203    # ------------------------ 
     204 
     205    def endSelfNotificationObservation(self): 
     206        super(Anchor, self).endSelfNotificationObservation() 
     207        self._glyph = None 
    185208 
    186209 
     
    229252    False 
    230253 
    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")) 
    232255    >>> a.x, a.y, a.name, a.identifier, a.color 
    233256    (1, 2, '3', '4', '1,1,1,1') 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/base.py

    r1005 r1022  
    3939 
    4040    def _init(self): 
    41         self._parent = None 
    4241        self._dispatcher = None 
    4342        self._dataOnDisk = None 
     
    4544        self._representations = {} 
    4645 
     46    def __del__(self): 
     47        self.endSelfNotificationObservation() 
     48 
    4749    # ------ 
    4850    # Parent 
    4951    # ------ 
    5052 
    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 = None 
    57         else: 
    58             self._parent = weakref.ref(obj) 
    59  
    6053    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 
    7055 
    7156    # ------------- 
     
    7459 
    7560    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: 
    7865            return None 
    79         return parent.dispatcher 
     66        return font.dispatcher 
    8067 
    8168    dispatcher = property(_get_dispatcher, doc="The :class:`defcon.tools.notifications.NotificationCenter` assigned to the parent of this object.") 
     
    214201        if dispatcher is not None: 
    215202            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 
    216220 
    217221    # --------------- 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/component.py

    r1017 r1022  
    2727    representationFactories = {} 
    2828 
    29     def __init__(self): 
     29    def __init__(self, glyph=None): 
     30        self._glyph = None 
     31        self.glyph = glyph 
    3032        super(Component, self).__init__() 
     33        self.beginSelfNotificationObservation() 
    3134        self._dirty = False 
    3235        self._baseGlyph = None 
     
    4043    # parents 
    4144 
     45    def getParent(self): 
     46        return self.glyph 
     47 
    4248    def _get_font(self): 
    4349        glyph = self.glyph 
     
    6571 
    6672    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.") 
    7084 
    7185    # bounds 
     
    151165            warn("The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.", DeprecationWarning) 
    152166 
    153     # ------- 
    154     # Methods 
    155     # ------- 
     167    # ---- 
     168    # Move 
     169    # ---- 
    156170 
    157171    def move(self, (x, y)): 
     
    165179        yOffset += y 
    166180        self.transformation = (xScale, xyScale, yxScale, yScale, xOffset, yOffset) 
     181 
     182    # ------------ 
     183    # Point Inside 
     184    # ------------ 
    167185 
    168186    def pointInside(self, (x, y), evenOdd=False): 
     
    228246        self.identifier = identifier 
    229247 
     248    # ------------------------ 
     249    # Notification Observation 
     250    # ------------------------ 
     251 
     252    def endSelfNotificationObservation(self): 
     253        super(Component, self).endSelfNotificationObservation() 
     254        self._glyph = None 
     255 
    230256 
    231257def _testIdentifier(): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/contour.py

    r1018 r1022  
    4242    representationFactories = {} 
    4343 
    44     def __init__(self, pointClass=None): 
     44    def __init__(self, glyph=None, pointClass=None): 
     45        self._glyph = None 
     46        self.glyph = glyph 
    4547        super(Contour, self).__init__() 
     48        self.beginSelfNotificationObservation() 
    4649        self._points = [] 
    4750        self._boundsCache = None 
     
    5861        self._controlPointBoundsCache = None 
    5962 
    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 
    6573 
    6674    def _get_font(self): 
     
    8997 
    9098    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    # ------ 
    94114 
    95115    def _get_pointClass(self): 
     
    98118    pointClass = property(_get_pointClass, doc="The class used for point.") 
    99119 
    100     def _get_bounds(self): 
    101         from robofab.pens.boundsPen import BoundsPen 
    102         if self._boundsCache is None: 
    103             pen = BoundsPen(None) 
    104             self.draw(pen) 
    105             self._boundsCache = pen.bounds 
    106         return self._boundsCache 
    107  
    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 ControlBoundsPen 
    112         if self._controlPointBoundsCache is None: 
    113             pen = ControlBoundsPen(None) 
    114             self.draw(pen) 
    115             self._controlPointBoundsCache = pen.bounds 
    116         return self._controlPointBoundsCache 
    117  
    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 ClockwiseTestPointPen 
    122         if self._clockwiseCache is None: 
    123             pen = ClockwiseTestPointPen() 
    124             self.drawPoints(pen) 
    125             self._clockwiseCache = pen.getIsClockwise() 
    126         return self._clockwiseCache 
    127  
    128     def _set_clockwise(self, value): 
    129         if self.clockwise != value: 
    130             self.reverse() 
    131             self._clockwiseCache = None 
    132  
    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 True 
    138         return self._points[0].segmentType == 'move' 
    139  
    140     open = property(_get_open, doc="A boolean indicating if the contour is open or not.") 
    141  
    142120    def _get_onCurvePoints(self): 
    143121        return [point for point in self._points if point.segmentType] 
    144122 
    145123    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    # -------- 
    146253 
    147254    def _get_segments(self): 
     
    169276 
    170277    segments = property(_get_segments, doc="A list of all points in the contour organized into segments.") 
    171  
    172     # ------- 
    173     # Methods 
    174     # ------- 
    175  
    176     def __len__(self): 
    177         return len(self._points) 
    178  
    179     def __getitem__(self, index): 
    180         if index > len(self._points): 
    181             raise IndexError 
    182         return self._points[index] 
    183  
    184     def __iter__(self): 
    185         pointCount = len(self) 
    186         index = 0 
    187         while index < pointCount: 
    188             point = self[index] 
    189             yield point 
    190             index += 1 
    191  
    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 storage 
    202         self._points = [] 
    203         # reset the clockwise cache 
    204         self._clockwiseCache = None 
    205         # post a dirty notification 
    206         if postNotification: 
    207             self.postNotification("Contour.PointsChanged") 
    208             self.dirty = True 
    209  
    210     def appendPoint(self, point): 
    211         """ 
    212         Append **point** to the glyph. The point must be a defcon 
    213         :class:`Point` object or a subclass of that object. An error 
    214         will be raised if the point's identifier conflicts with any of 
    215         the identifiers within the glyph. 
    216  
    217         This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 
    218         """ 
    219         assert point not in self._points 
    220         self.insertPoint(len(self._points), point) 
    221  
    222     def insertPoint(self, index, point): 
    223         """ 
    224         Insert **point** into the contour at index. The point 
    225         must be a defcon :class:`Point` object or a subclass 
    226         of that object. An error will be raised if the points's 
    227         identifier conflicts with any of the identifiers within 
    228         the glyph. 
    229  
    230         This will post *Contour.PointsChanged* and *Contour.Changed* notifications. 
    231         """ 
    232         assert point not in self._points 
    233         if point.identifier is not None: 
    234             identifiers = self.identifiers 
    235             assert point.identifier not in identifiers 
    236             if point.identifier is not None: 
    237                 identifiers.add(point.identifier) 
    238         self._points.insert(index, point) 
    239         self._destroyBoundsCache() 
    240         self._clockwiseCache = None 
    241         self.postNotification("Contour.PointsChanged") 
    242         self.dirty = True 
    243  
    244     def reverse(self): 
    245         """ 
    246         Reverse the direction of the contour. It's important to note 
    247         that the actual points stored in this object will be completely 
    248         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 ReverseContourPointPen 
    254         oldDirection = self.clockwise 
    255         # put the current points in another contour 
    256         otherContour = self.__class__(self.pointClass) 
    257         # draw the points in this contour through 
    258         # the reversing pen. 
    259         reversePen = ReverseContourPointPen(otherContour) 
    260         self.drawPoints(reversePen) 
    261         # clear the points in this contour 
    262         self._clear(postNotification=False) 
    263         # draw the points back into this contour 
    264         self.disableNotifications() 
    265         otherContour.drawPoints(self) 
    266         self.enableNotifications() 
    267         # post a notification 
    268         self.postNotification("Contour.WindingDirectionChanged", data=dict(oldValue=oldDirection, newValue=self.clockwise)) 
    269         self.postNotification("Contour.PointsChanged") 
    270         self.dirty = True 
    271  
    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 cache 
    281         if self._boundsCache: 
    282             xMin, yMin, xMax, yMax = self._boundsCache 
    283             xMin += x 
    284             yMin += y 
    285             xMax += x 
    286             yMax += y 
    287             self._boundsCache = (xMin, yMin, xMax, yMax) 
    288         if self._controlPointBoundsCache: 
    289             xMin, yMin, xMax, yMax = self._controlPointBoundsCache 
    290             xMin += x 
    291             yMin += y 
    292             xMax += x 
    293             yMax += y 
    294             self._controlPointBoundsCache = (xMin, yMin, xMax, yMax) 
    295         self.postNotification("Contour.PointsChanged") 
    296         self.dirty = True 
    297  
    298     def pointInside(self, (x, y), evenOdd=False): 
    299         """ 
    300         Returns a boolean indicating if **(x, y)** is in the 
    301         "black" area of the contour. 
    302         """ 
    303         from fontTools.pens.pointInsidePen import PointInsidePen 
    304         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.onCurvePoints 
    322         if len(onCurvePoints) < 2: 
    323             return 
    324         if self.open: 
    325             return 
    326         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:] + before 
    330         self.postNotification("Contour.PointsChanged") 
    331         self.dirty = True 
    332  
    333     def positionForProspectivePointInsertionAtSegmentAndT(self, segmentIndex, t): 
    334         """ 
    335         Get the precise coordinates and a boolean indicating 
    336         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 given 
    344         **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.segments 
    352         segment = segments[segmentIndex] 
    353         segment.insert(0, segments[segmentIndex-1][-1]) 
    354         firstPoint = segment[0] 
    355         lastPoint = segment[-1] 
    356         segmentType = lastPoint.segmentType 
    357         segment = [(point.x, point.y) for point in segment] 
    358         if segmentType == "line": 
    359             (x1, y1), (x2, y2) = segment 
    360             x = x1 + (x2 - x1) * t 
    361             y = y1 + (y2 - y1) * t 
    362             pointsToInsert = [((x, y), "line", False)] 
    363             insertionPoint =  (x, y) 
    364             pointWillBeSmooth = False 
    365         elif segmentType == "curve": 
    366             pt1, pt2, pt3, pt4 = segment 
    367             (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 = True 
    371         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 + lastPoints 
    387             self.dirty = True 
    388         return insertionPoint, pointWillBeSmooth 
    389278 
    390279    def removeSegment(self, segmentIndex, preserveCurve=False): 
     
    472361        self.dirty = True 
    473362 
     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 
    474522    # ----------- 
    475523    # Pen methods 
     
    577625        point.identifier = identifier 
    578626        self.dirty = True 
     627 
     628    # ------------------------ 
     629    # Notification Observation 
     630    # ------------------------ 
     631 
     632    def endSelfNotificationObservation(self): 
     633        super(Contour, self).endSelfNotificationObservation() 
     634        self._glyph = None 
     635 
    579636 
    580637# ----- 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/dataSet.py

    r1016 r1022  
    11import os 
     2import weakref 
    23from ufoLib import UFOReader, UFOLibError 
    34from defcon.objects.base import BaseObject 
     
    2122    """ 
    2223 
     24    changeNotificationName = "DataSet.Changed" 
    2325    representationFactories = {} 
    2426 
    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) 
    2631        super(DataSet, self).__init__() 
     32        self.beginSelfNotificationObservation() 
    2733        self._data = {} 
    2834        self._scheduledForDeletion = {} 
    2935 
     36    # -------------- 
     37    # Parent Objects 
     38    # -------------- 
     39 
     40    def getParent(self): 
     41        return self.font 
     42 
    3043    def _get_font(self): 
    31         return self.getParent() 
     44        if self._font is not None: 
     45            return self._font() 
     46        return None 
    3247 
    3348    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
     49 
     50    # ---------- 
     51    # File Names 
     52    # ---------- 
    3453 
    3554    def _get_fileNames(self): 
     
    7796        self.dirty = True 
    7897 
    79     # --------------- 
    80     # File Management 
    81     # --------------- 
     98    # ---- 
     99    # Save 
     100    # ---- 
    82101 
    83102    def getSaveProgressBarTickCount(self, formatVersion): 
     
    170189            data = self[fileName] 
    171190 
     191    # ------------------------ 
     192    # Notification Observation 
     193    # ------------------------ 
     194 
     195    def endSelfNotificationObservation(self): 
     196        super(DataSet, self).endSelfNotificationObservation() 
     197        self._font = None 
     198 
    172199 
    173200def _dataDict(data=None, dirty=False, onDisk=True, onDiskModTime=None): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/features.py

    r1014 r1022  
     1import weakref 
    12from defcon.objects.base import BaseObject 
    23 
     
    2021    representationFactories = {} 
    2122 
    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) 
    2327        super(Features, self).__init__() 
     28        self.beginSelfNotificationObservation() 
    2429        self._dirty = False 
    2530        self._text = None 
    2631 
     32    # -------------- 
     33    # Parent Objects 
     34    # -------------- 
     35 
     36    def getParent(self): 
     37        return self.font 
     38 
    2739    def _get_font(self): 
    28         return self.getParent() 
     40        if self._font is not None: 
     41            return self._font() 
     42        return None 
    2943 
    3044    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
     45 
     46    # ---- 
     47    # Text 
     48    # ---- 
    3149 
    3250    def _set_text(self, value): 
     
    4361    text = property(_get_text, _set_text, doc="The raw feature text. Setting this post *Features.TextChanged* and *Features.Changed* notifications.") 
    4462 
     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  
    7070                    layerSetClass=None, layerClass=None, imageSetClass=None, dataSetClass=None, 
    7171                    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 
    7374        super(Font, self).__init__() 
     75        self._dispatcher = NotificationCenter() 
     76        self.beginSelfNotificationObservation() 
     77 
    7478        if infoClass is None: 
    7579            infoClass = Info 
     
    8892        if dataSetClass is None: 
    8993            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 
    93103        self._kerningClass = kerningClass 
    94104        self._infoClass = infoClass 
     
    97107        self._libClass = libClass 
    98108        self._guidelineClass = guidelineClass 
     109        self._imageSetClass = imageSetClass 
     110        self._dataSetClass = dataSetClass 
    99111 
    100112        self._path = path 
     
    107119        self._lib = None 
    108120 
    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() 
    125127 
    126128        self._dirty = False 
     
    272274    # ----------- 
    273275 
     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 
    274309    def _get_layers(self): 
    275310        return self._layers 
     
    277312    layers = property(_get_layers, doc="The font's :class:`LayerSet` object.") 
    278313 
     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 
    279335    def _get_info(self): 
    280336        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() 
    283339            reader = None 
    284340            if self._path is not None: 
     341                self._info.disableNotifications() 
    285342                reader = UFOReader(self._path) 
    286343                reader.readInfo(self._info) 
    287344                self._info.dirty = False 
    288             self._info.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Info.Changed") 
     345                self._info.enableNotifications() 
    289346            self._stampInfoDataState(reader) 
    290347        return self._info 
     
    292349    info = property(_get_info, doc="The font's :class:`Info` object.") 
    293350 
     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 
    294371    def _get_kerning(self): 
    295372        if self._kerning is None: 
    296             self._kerning = self._kerningClass() 
    297             self._kerning.setParent(self) 
     373            self._kerning = self.instantiateKerning() 
     374            self.beginSelfKerningNotificationObservation() 
    298375            reader = None 
    299376            if self._path is not None: 
     377                self._kerning.disableNotifications() 
    300378                # the _reader attribute may be present during __init__ 
    301379                # but only under certain conditions. 
     
    307385                self._kerning.update(d) 
    308386                self._kerning.dirty = False 
    309             self._kerning.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Kerning.Changed") 
     387                self._kerning.enableNotifications() 
    310388            self._stampKerningDataState(reader) 
    311389        return self._kerning 
     
    313391    kerning = property(_get_kerning, doc="The font's :class:`Kerning` object.") 
    314392 
     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 
    315413    def _get_groups(self): 
    316414        if self._groups is None: 
    317             self._groups = self._groupsClass() 
    318             self._groups.setParent(self) 
     415            self._groups = self.instantiateGroups() 
     416            self.beginSelfGroupsNotificationObservation() 
    319417            reader = None 
    320418            if self._path is not None: 
     419                self._groups.disableNotifications() 
    321420                # the _reader attribute may be present during __init__ 
    322421                # but only under certain conditions. 
     
    328427                self._groups.update(d) 
    329428                self._groups.dirty = False 
    330             self._groups.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Groups.Changed") 
     429                self._groups.enableNotifications() 
    331430            self._stampGroupsDataState(reader) 
    332431        return self._groups 
     
    334433    groups = property(_get_groups, doc="The font's :class:`Groups` object.") 
    335434 
     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 
    336455    def _get_features(self): 
    337456        if self._features is None: 
    338             self._features = self._featuresClass() 
    339             self._features.setParent(self) 
     457            self._features = self.instantiateFeatures() 
     458            self.beginSelfFeaturesNotificationObservation() 
    340459            reader = None 
    341460            if self._path is not None: 
     461                self._features.disableNotifications() 
    342462                reader = UFOReader(self._path) 
    343463                t = reader.readFeatures() 
    344464                self._features.text = t 
    345465                self._features.dirty = False 
    346             self._features.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Features.Changed") 
     466                self._features.enableNotifications() 
    347467            self._stampFeaturesDataState(reader) 
    348468        return self._features 
     
    350470    features = property(_get_features, doc="The font's :class:`Features` object.") 
    351471 
     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 
    352491    def _get_lib(self): 
    353492        if self._lib is None: 
    354             self._lib = self._libClass() 
    355             self._lib.setParent(self) 
     493            self._lib = self.instantiateLib() 
     494            self.beginSelfLibNotificationObservation() 
    356495            reader = None 
    357496            if self._path is not None: 
     497                self._lib.disableNotifications() 
    358498                reader = UFOReader(self._path) 
    359499                d = reader.readLib() 
    360500                self._lib.update(d) 
    361             self._lib.addObserver(observer=self, methodName="_objectDirtyStateChange", notification="Lib.Changed") 
     501                self._lib.enableNotifications() 
    362502            self._stampLibDataState(reader) 
    363503        return self._lib 
     
    365505    lib = property(_get_lib, doc="The font's :class:`Lib` object.") 
    366506 
     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 
    367553    def _get_unicodeData(self): 
    368554        return self._glyphSet._unicodeData 
    369555 
    370556    unicodeData = property(_get_unicodeData, doc="The font's :class:`UnicodeData` object.") 
    371  
    372     def _get_images(self): 
    373         return self._images 
    374  
    375     images = property(_get_images, doc="The font's :class:`ImageSet` object.") 
    376  
    377     def _get_data(self): 
    378         return self._data 
    379  
    380     data = property(_get_data, doc="The font's :class:`DataSet` object.") 
    381557 
    382558    # glyph order 
     
    627803            progressBar.update() 
    628804 
    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() 
    632821 
    633822    def _objectDirtyStateChange(self, notification): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/glyph.py

    r1016 r1022  
    7373    representationFactories = {} 
    7474 
    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 
    7682        super(Glyph, self).__init__() 
    77  
    78         self._layer = None 
    79         self._parent = None 
     83        self.beginSelfNotificationObservation() 
     84 
    8085        self._dirty = False 
    8186        self._name = None 
     
    106111        if libClass is None: 
    107112            libClass = Lib 
    108  
     113        if imageClass is None: 
     114            imageClass = Image 
    109115        self._contourClass = contourClass 
    110116        self._pointClass = pointClass 
     
    112118        self._anchorClass = anchorClass 
    113119        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() 
    145125 
    146126    def _destroyBoundsCache(self): 
     
    148128        self._controlPointBoundsCache = None 
    149129 
    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 
    153145 
    154146    def _get_font(self): 
    155         return self.getParent() 
     147        layerSet = self.layerSet 
     148        if layerSet is None: 
     149            return None 
     150        return layerSet.font 
    156151 
    157152    font = property(_get_font, doc="The :class:`Font` that this glyph belongs to.") 
     
    170165        return self._layer() 
    171166 
    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    # ---------------- 
    205172 
    206173    # identifiers 
     
    255222 
    256223    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    # ------- 
    257228 
    258229    # bounds 
     
    348319    height = property(_get_height, _set_height, doc="The height of the glyph. Setting this posts *Glyph.HeightChanged* and *Glyph.Changed* notifications.") 
    349320 
    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    # ---------------------- 
    433324 
    434325    # mark color 
     
    461352    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.") 
    462353 
    463     # ----------- 
    464     # Pen Methods 
    465     # ----------- 
     354    # ------- 
     355    # Pen API 
     356    # ------- 
    466357 
    467358    def draw(self, pen): 
     
    496387        return GlyphObjectPointPen(self) 
    497388 
    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): 
    553907        if self._image is None: 
    554908            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: 
    561910            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    # ------------- 
    569917 
    570918    def __len__(self): 
     
    584932    def _getContourIndex(self, contour): 
    585933        return self._contours.index(contour) 
     934 
     935    # ---------------- 
     936    # Glyph Absorption 
     937    # ---------------- 
    586938 
    587939    def copyDataFromGlyph(self, glyph): 
     
    609961        self.unicodes = list(glyph.unicodes) 
    610962        self.note = glyph.note 
    611         self.guidelines = glyph.guidelines 
    612         self.anchors = glyph.anchors 
     963        self.guidelines = [self.instantiateGuideline(g) for g in glyph.guidelines] 
     964        self.anchors = [self.instantiateAnchor(a) for a in glyph.anchors] 
    613965        self.image = glyph.image 
    614966        pointPen = self.getPointPen() 
     
    616968        self.lib = deepcopy(glyph.lib) 
    617969 
    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    # ----- 
    849973 
    850974    def clear(self): 
     
    861985        self.releaseHeldNotifications() 
    862986 
    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    # ---- 
    906990 
    907991    def move(self, (x, y)): 
     
    9351019            self._controlPointBoundsCache = (xMin, yMin, xMax, yMax) 
    9361020 
     1021    # ------------ 
     1022    # Point Inside 
     1023    # ------------ 
     1024 
    9371025    def pointInside(self, (x, y), evenOdd=False): 
    9381026        """ 
     
    9481036    # Notification Callbacks 
    9491037    # ---------------------- 
     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 
    9501054 
    9511055    def _imageChanged(self, notification): 
     
    10411145    >>> glyph.bounds 
    10421146    (0, 0, 700, 700) 
     1147 
    10431148    >>> glyph = font['B'] 
    10441149    >>> glyph.bounds 
     
    15711676 
    15721677    >>> pointPen.beginPath(identifier="contour 1") 
    1573     >>> pointPen.endPath() 
    15741678    Traceback (most recent call last): 
    15751679        ... 
    15761680    AssertionError 
     1681    >>> pointPen.endPath() 
    15771682 
    15781683    >>> pointPen.beginPath() 
    15791684    >>> pointPen.addPoint((0, 0)) 
    15801685    >>> pointPen.addPoint((0, 0), identifier="point 1") 
    1581     >>> pointPen.endPath() 
    15821686    Traceback (most recent call last): 
    15831687        ... 
    15841688    AssertionError 
     1689    >>> pointPen.endPath() 
    15851690 
    15861691    >>> pointPen.addComponent("A", (1, 1, 1, 1, 1, 1), identifier="component 1") 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/groups.py

    r1014 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23 
     
    6869    representationFactories = {} 
    6970 
     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 
    7085    def _get_font(self): 
    71         return self.getParent() 
     86        if self._font is not None: 
     87            return self._font() 
     88        return None 
    7289 
    7390    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 
    7499 
    75100 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/guideline.py

    r1016 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23from defcon.objects.color import Color 
     
    3031    representationFactories = {} 
    3132 
    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 
    3340        super(Guideline, self).__init__() 
     41        self.beginSelfNotificationObservation() 
    3442        self._dirty = False 
    3543        if guidelineDict is not None: 
     
    4149            self.identifier = guidelineDict.get("identifier") 
    4250 
    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 
    4861 
    4962    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 
    5667        return None 
    5768 
    5869    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
    5970 
    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.") 
    6884 
    6985    def _get_layerSet(self): 
     
    7389        return glyph.layerSet 
    7490 
    75     layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this object belongs to (if it isn't a font a guideline).") 
     91    layerSet = property(_get_layerSet, doc="The :class:`LayerSet` that this object belongs to (if it isn't a font info guideline).") 
    7692 
    7793    def _get_layer(self): 
     
    8197        return glyph.layer 
    8298 
    83     layer = property(_get_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font a guideline).") 
     99    layer = property(_get_layer, doc="The :class:`Layer` that this object belongs to (if it isn't a font info guideline).") 
    84100 
    85101    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 
    93120 
    94121    def _get_x(self): 
     
    104131    x = property(_get_x, _set_x, doc="The x coordinate. Setting this will post *Guideline.XChanged* and *Guideline.Changed* notifications.") 
    105132 
     133    # y 
     134 
    106135    def _get_y(self): 
    107136        return self.get("y") 
     
    116145    y = property(_get_y, _set_y, doc="The y coordinate. Setting this will post *Guideline.YChanged* and *Guideline.Changed* notifications.") 
    117146 
     147    # angle 
     148 
    118149    def _get_angle(self): 
    119150        return self.get("angle") 
     
    128159    angle = property(_get_angle, _set_angle, doc="The angle. Setting this will post *Guideline.AngleChanged* and *Guideline.Changed* notifications.") 
    129160 
     161    # name 
     162 
    130163    def _get_name(self): 
    131164        return self.get("name") 
     
    139172 
    140173    name = property(_get_name, _set_name, doc="The name. Setting this will post *Guideline.NameChanged* and *Guideline.Changed* notifications.") 
     174 
     175    # color 
    141176 
    142177    def _get_color(self): 
     
    156191    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.") 
    157192 
    158     # ------- 
    159     # Methods 
    160     # ------- 
     193    # ---------- 
     194    # Identifier 
     195    # ---------- 
    161196 
    162197    def _get_identifiers(self): 
     
    164199        parent = self.glyph 
    165200        if parent is None: 
    166             parent = self.info 
     201            parent = self.fontInfo 
    167202        if parent is not None: 
    168203            identifiers = parent.identifiers 
     
    203238        self.identifier = identifier 
    204239 
     240    # ------------------------ 
     241    # Notification Observation 
     242    # ------------------------ 
     243 
     244    def endSelfNotificationObservation(self): 
     245        super(Guideline, self).endSelfNotificationObservation() 
     246        self._fontInfo = None 
     247        self._glyph = None 
     248 
    205249 
    206250def _test(): 
     
    267311    False 
    268312 
    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")) 
    270314    >>> g.x, g.y, g.angle, g.name, g.identifier, g.color 
    271315    (1, 2, 3, '4', '5', '1,1,1,1') 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/image.py

    r1014 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23from defcon.objects.color import Color 
     
    3637    representationFactories = {} 
    3738 
    38     def __init__(self, imageDict=None): 
     39    def __init__(self, glyph=None, imageDict=None): 
     40        self._glyph = None 
     41        self.glyph = glyph 
    3942        super(Image, self).__init__() 
     43        self.beginSelfNotificationObservation() 
    4044        self["fileName"] = None 
    4145        self["color"] = None 
     
    4751        self._dirty = False 
    4852 
    49     # ---------- 
    50     # Properties 
    51     # ---------- 
    52  
    53     # parents 
     53    # -------------- 
     54    # Parent Objects 
     55    # -------------- 
     56 
     57    def getParent(self): 
     58        return self.glyph 
    5459 
    5560    def _get_font(self): 
     
    7883 
    7984    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 
    83102 
    84103    def _get_fileName(self): 
     
    93112 
    94113    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 
    95116 
    96117    def _get_transformation(self): 
     
    117138    transformation = property(_get_transformation, _set_transformation, doc="The transformation matrix for the image. Setting this will posts *Image.Changed* and *Image.TransformationChanged* notifications.") 
    118139 
     140    # color 
     141 
    119142    def _get_color(self): 
    120143        return self.get("color") 
     
    133156    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.") 
    134157 
     158    # ------------------------ 
     159    # Notification Observation 
     160    # ------------------------ 
     161 
     162    def endSelfNotificationObservation(self): 
     163        super(Image, self).endSelfNotificationObservation() 
     164        self._glyph = None 
     165 
    135166 
    136167def _testAttributes(): 
     
    160191    True 
    161192 
    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")) 
    163194    >>> i.fileName, i.transformation, i.color 
    164195    ('foo.png', ('1', '2', '3', '4', '5', '6'), '0,0,0,0') 
     
    187218    >>> font = Font(path) 
    188219    >>> glyph = font.layers[None]["A"] 
    189     >>> glyph.image = Image() 
     220    >>> glyph.image = glyph.instantiateImage() 
    190221    >>> glyph.image.color = "1,1,1,1" 
    191222    >>> glyph.image.fileName = "foo.png" 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/imageSet.py

    r1016 r1022  
    11import os 
    22import hashlib 
     3import weakref 
    34from ufoLib import UFOReader, UFOLibError 
    45from defcon.objects.base import BaseObject 
     
    1819    Name 
    1920    =========================== 
     21    ImageSet.Changed 
    2022    ImageSet.FileNamesChanged 
    2123    ImageSet.ImageChanged 
     
    4951    """ 
    5052 
     53    changeNotificationName = "ImageSet.Changed" 
    5154    representationFactories = {} 
    5255 
    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) 
    5460        super(ImageSet, self).__init__() 
     61        self.beginSelfNotificationObservation() 
    5562        self._data = {} 
    5663        self._scheduledForDeletion = {} 
    5764 
     65    # -------------- 
     66    # Parent Objects 
     67    # -------------- 
     68 
     69    def getParent(self): 
     70        return self.font 
     71 
    5872    def _get_font(self): 
    59         return self.getParent() 
     73        if self._font is not None: 
     74            return self._font() 
     75        return None 
    6076 
    6177    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
     78 
     79    # ---------- 
     80    # File Names 
     81    # ---------- 
    6282 
    6383    def _get_fileNames(self): 
     
    126146        self.dirty = True 
    127147 
    128     # --------------- 
    129     # File Management 
    130     # --------------- 
     148    # ---- 
     149    # Save 
     150    # ---- 
    131151 
    132152    def getSaveProgressBarTickCount(self, formatVersion): 
     
    178198        self.dirty = False 
    179199 
     200    # --------------- 
     201    # File Management 
     202    # --------------- 
     203 
    180204    def makeFileName(self, fileName): 
    181205        """ 
     
    258282            image = self[fileName] 
    259283 
     284    # ------------------------ 
     285    # Notification Observation 
     286    # ------------------------ 
     287 
     288    def endSelfNotificationObservation(self): 
     289        super(ImageSet, self).endSelfNotificationObservation() 
     290        self._font = None 
     291 
    260292 
    261293def _imageDict(data=None, dirty=False, digest=None, onDisk=True, onDiskModTime=None): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/info.py

    r1016 r1022  
    22# this file should not be edited by hand. 
    33 
     4import weakref 
    45from warnings import warn 
    56import ufoLib 
     
    3132    representationFactories = {} 
    3233 
    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 
    3438        super(Info, self).__init__() 
     39        self.beginSelfNotificationObservation() 
    3540        self._identifiers = set() 
    3641        if guidelineClass is None: 
     
    146151        self._year = None 
    147152 
     153    def __del__(self): 
     154        super(Info, self).__del__() 
     155        self._guidelines = None 
     156 
     157    def getParent(self): 
     158        return self.font 
     159 
    148160 
    149161    def _get_font(self): 
    150         return self.getParent() 
     162        if self._font is not None: 
     163            return self._font() 
     164        return None 
    151165 
    152166    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
     
    23212335    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.") 
    23222336 
    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") 
    23322354 
    23332355    def appendGuideline(self, guideline): 
     
    23552377        assert guideline not in self._guidelines 
    23562378        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: 
    23612384            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) 
    23652392        self._guidelines.insert(index, guideline) 
    23662393        self.postNotification("Info.GuidelinesChanged") 
     
    23772404            self._identifiers.remove(guideline.identifier) 
    23782405        self._guidelines.remove(guideline) 
    2379         self._removeParentDataInGuideline(guideline) 
     2406        self.endSelfGuidelineNotificationObservation(guideline) 
    23802407        self.postNotification("Info.GuidelinesChanged") 
    23812408        self.dirty = True 
     
    23982425        self.releaseHeldNotifications() 
    23992426 
    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 
    24032438 
    24042439    def _guidelineChanged(self, notification): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/kerning.py

    r1014 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23 
     
    4849    representationFactories = {} 
    4950 
     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 
    5065    def _get_font(self): 
    51         return self.getParent() 
     66        if self._font is not None: 
     67            return self._font() 
     68        return None 
    5269 
    5370    font = property(_get_font, doc="The :class:`Font` that this object belongs to.") 
    5471 
     72    # ------------- 
     73    # Pair Handling 
     74    # ------------- 
     75 
    5576    def get(self, pair, default=0): 
    5677        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 
    5786 
    5887 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/layer.py

    r1016 r1022  
    5353    representationFactories = {} 
    5454 
    55     def __init__(self, glyphSet=None, libClass=None, unicodeDataClass=None, 
     55    def __init__(self, layerSet=None, glyphSet=None, libClass=None, unicodeDataClass=None, 
    5656                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 
    5862        super(Layer, self).__init__() 
     63        self.beginSelfNotificationObservation() 
    5964 
    6065        self._name = None 
     
    6671        if unicodeDataClass is None: 
    6772            unicodeDataClass = UnicodeData 
    68  
    6973        self._glyphClass = glyphClass 
    7074        self._glyphContourClass = glyphContourClass 
     
    7276        self._glyphComponentClass = glyphComponentClass 
    7377        self._glyphAnchorClass = glyphAnchorClass 
     78        self._glyphImageClass = glyphImageClass 
    7479        self._libClass = libClass 
    7580        self._guidelineClass = guidelineClass 
    76  
    77         self._dispatcher = None 
     81        self._unicodeDataClass = unicodeDataClass 
     82 
    7883        self._color = None 
    7984        self._lib = None 
    80         self._unicodeData = unicodeDataClass() 
    81         self._unicodeData.setParent(self) 
     85        self._unicodeData = self.instantiateUnicodeData() 
     86        self.beginSelfUnicodeDataNotificationObservation() 
    8287 
    8388        self._directory = None 
     
    9196 
    9297        if glyphSet is not None: 
     98            self._unicodeData.disableNotifications() 
    9399            self._keys = set(self._glyphSet.keys()) 
    94100            cmap = {} 
     
    100106                        cmap[code] = [glyphName] 
    101107            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): 
    108143        glyph = self._glyphClass( 
     144            layer=self, 
    109145            contourClass=self._glyphContourClass, 
    110146            pointClass=self._glyphPointClass, 
     
    112148            anchorClass=self._glyphAnchorClass, 
    113149            guidelineClass=self._guidelineClass, 
    114             libClass=self._libClass 
     150            libClass=self._libClass, 
     151            imageClass=self._glyphImageClass 
    115152        ) 
    116153        return glyph 
    117154 
    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        """ 
    119173        if self._glyphSet is None or not self._glyphSet.has_key(name): 
    120174            raise KeyError, "%s not in layer" % name 
    121         glyph = self._instantiateGlyphObject() 
     175        glyph = self.instantiateGlyphObject() 
     176        self.beginSelfGlyphNotificationObservation(glyph) 
     177        glyph.disableNotifications() 
    122178        pointPen = glyph.getPointPen() 
    123179        self._glyphSet.readGlyph(glyphName=name, glyphObject=glyph, pointPen=pointPen) 
    124180        glyph.dirty = False 
     181        glyph.enableNotifications() 
    125182        self._glyphs[name] = glyph 
    126         self._setParentDataInGlyph(glyph) 
    127183        self._stampGlyphDataState(glyph) 
    128184        return glyph 
    129  
    130     def _setParentDataInGlyph(self, glyph): 
    131         # the parent of a glyph is always the font, not the layer 
    132         font = self.font 
    133         if font is not None: 
    134             glyph.setParent(font) 
    135         glyph.layer = self 
    136         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 = None 
    145         glyph.setParent(None) 
    146185 
    147186    def newGlyph(self, name): 
     
    155194        if name in self: 
    156195            self._unicodeData.removeGlyphData(name, self[name].unicodes) 
    157         glyph = self._instantiateGlyphObject() 
     196        glyph = self.instantiateGlyphObject() 
     197        self.beginSelfGlyphNotificationObservation(glyph) 
     198        glyph.disableNotifications() 
    158199        glyph.name = name 
     200        glyph.enableNotifications() 
    159201        self._glyphs[name] = glyph 
    160         self._setParentDataInGlyph(glyph) 
    161202        if name in self._scheduledForDeletion: 
    162203            del self._scheduledForDeletion[name] 
     
    194235        return dest 
    195236 
     237    # ------------- 
     238    # Dict Behavior 
     239    # ------------- 
     240 
    196241    def __iter__(self): 
    197242        names = self.keys() 
     
    203248    def __getitem__(self, name): 
    204249        if name not in self._glyphs: 
    205             self._loadGlyph(name) 
     250            self.loadGlyph(name) 
    206251        return self._glyphs[name] 
    207252 
     
    215260        if name in self._glyphs: 
    216261            glyph = self._glyphs.pop(name) 
    217             self._removeParentDataInGlyph(glyph) 
     262            self.endSelfGlyphNotificationObservation(glyph) 
    218263            dataOnDiskTimeStamp = glyph._dataOnDiskTimeStamp 
    219264            dataOnDisk = glyph._dataOnDisk 
     
    245290    # ---------- 
    246291 
    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 
    259293 
    260294    def _set_name(self, value): 
     
    270304 
    271305    name = property(_get_name, _set_name, doc="The name of the layer. Setting this posts *Layer.NameChanged* and *Layer.Changed* notifications.") 
     306 
     307    # color 
    272308 
    273309    def _get_color(self): 
     
    287323 
    288324    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 
    289331 
    290332    def _get_glyphsWithOutlines(self): 
     
    308350 
    309351    glyphsWithOutlines = property(_get_glyphsWithOutlines, doc="A list of glyphs containing outlines.") 
     352 
     353    # component references 
    310354 
    311355    def _get_componentReferences(self): 
     
    334378    componentReferences = property(_get_componentReferences, doc="A dict of describing the component relationships in the layer. The dictionary is of form ``{base glyph : [references]}``.") 
    335379 
     380    # image references 
     381 
    336382    def _get_imageReferences(self): 
    337383        found = {} 
     
    357403    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]}``.") 
    358404 
     405    # bounds 
     406 
    359407    def _get_bounds(self): 
    360408        fontRect = None 
     
    370418 
    371419    bounds = property(_get_bounds, doc="The bounds of all glyphs in the layer. This can be an expensive operation.") 
     420 
     421    # control point bounds 
    372422 
    373423    def _get_controlPointBounds(self): 
     
    437487    # ----------- 
    438488 
     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 
    439508    def _get_lib(self): 
    440509        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() 
    444512        return self._lib 
    445513 
     
    451519    lib = property(_get_lib, _set_lib, doc="The layer's :class:`Lib` object.") 
    452520 
     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 
    453537    def _get_unicodeData(self): 
    454538        return self._unicodeData 
     
    456540    unicodeData = property(_get_unicodeData, doc="The layer's :class:`UnicodeData` object.") 
    457541 
    458     # ------- 
    459     # Methods 
    460     # ------- 
     542    # ---- 
     543    # Save 
     544    # ---- 
    461545 
    462546    def getSaveProgressBarTickCount(self, formatVersion): 
     
    566650        for glyphName in glyphNames: 
    567651            if glyphName not in self._glyphs: 
    568                 self._loadGlyph(glyphName) 
     652                self.loadGlyph(glyphName) 
    569653            else: 
    570654                glyph = self._glyphs[glyphName] 
     
    595679                referenceChanges.add(reference) 
    596680 
    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 
    600694 
    601695    def _glyphDirtyStateChange(self, notification): 
     
    853947    >>> glyph = layer['A'] 
    854948    >>> del layer['A'] 
    855     >>> glyph.getParent() 
    856949    >>> layer.dirty 
    857950    True 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/layerSet.py

    r1016 r1022  
     1import weakref 
    12from ufoLib import UFOReader 
    23from defcon.objects.base import BaseObject 
     
    3839    representationFactories = {} 
    3940 
    40     def __init__(self, layerClass=None, libClass=None, unicodeDataClass=None,  
     41    def __init__(self, font=None, layerClass=None, libClass=None, unicodeDataClass=None,  
    4142            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 
    4349        super(LayerSet, self).__init__() 
     50        self.beginSelfNotificationObservation() 
     51 
    4452        if layerClass is None: 
    4553            layerClass = Layer 
    46  
    4754        self._layerClass = layerClass 
    4855        self._libClass = libClass 
     
    5360        self._glyphComponentClass = glyphComponentClass 
    5461        self._glyphAnchorClass = glyphAnchorClass 
     62        self._glyphImageClass = glyphImageClass 
    5563        self._guidelineClass = guidelineClass 
    5664 
     
    6169        self._layerActionHistory = [] 
    6270 
     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 
    6382    def _get_font(self): 
    64         return self.getParent() 
     83        if self._font is None: 
     84            return None 
     85        return self._font() 
    6586 
    6687    font = property(_get_font, doc="The :class:`Font` that this layer set belongs to.") 
     88 
     89    # ------------- 
     90    # Default Layer 
     91    # ------------- 
    6792 
    6893    def _get_defaultLayerName(self): 
     
    92117    defaultLayer = property(_get_defaultLayer, _set_defaultLayer, doc="The default :class:`Layer` object. Setting this will post *LayerSet.DefaultLayerChanged* and *LayerSet.Changed* notifications.") 
    93118 
     119    # ----------- 
     120    # Layer Order 
     121    # ----------- 
     122 
    94123    def _get_layerOrder(self): 
    95124        return list(self._layerOrder) 
     
    108137 
    109138    # ------------- 
    110     # Dict Behavior 
     139    # Layer Creation 
    111140    # ------------- 
    112141 
    113     def _instantiateLayerObject(self, glyphSet): 
     142    def instantiateLayer(self, glyphSet): 
    114143        layer = self._layerClass( 
     144            layerSet=self, 
    115145            glyphSet=glyphSet, 
    116146            libClass=self._libClass, 
     
    121151            glyphComponentClass=self._glyphComponentClass, 
    122152            glyphAnchorClass=self._glyphAnchorClass, 
    123             guidelineClass=self._guidelineClass 
     153            guidelineClass=self._guidelineClass, 
     154            glyphImageClass=self._glyphImageClass 
    124155        ) 
    125156        return layer 
    126157 
    127     def _setParentDataInLayer(self, layer): 
    128         layer.setParent(self) 
     158    def beginSelfLayerNotificationObservation(self, layer): 
    129159        layer.addObserver(observer=self, methodName="_layerDirtyStateChange", notification="Layer.Changed") 
    130160        layer.addObserver(observer=self, methodName="_layerNameChange", notification="Layer.NameChanged") 
    131161 
    132     def _removeParentDataInLayer(self, layer): 
     162    def endSelfLayerNotificationObservation(self, layer): 
     163        if layer.dispatcher is None: 
     164            return 
    133165        layer.removeObserver(observer=self, notification="Layer.Changed") 
    134166        layer.removeObserver(observer=self, notification="Layer.NameChanged") 
    135         layer.setParent(None) 
     167        layer.endSelfNotificationObservation() 
    136168 
    137169    def newLayer(self, name, glyphSet=None): 
     
    146178            raise KeyError("A layer named \"%s\" already exists." % name) 
    147179        assert name is not None 
    148         layer = self._instantiateLayerObject(glyphSet) 
     180        layer = self.instantiateLayer(glyphSet) 
     181        self.beginSelfLayerNotificationObservation(layer) 
     182        layer.disableNotifications() 
    149183        layer.name = name 
    150         self._setParentDataInLayer(layer) 
    151184        if glyphSet is None: 
    152185            layer.dirty = True 
    153186        else: 
    154187            glyphSet.readLayerInfo(layer) 
     188            layer.dirty = False 
     189        layer.enableNotifications() 
    155190        self._stampLayerInfoDataState(layer) 
    156191        self._layers[name] = layer 
     
    162197        return layer 
    163198 
     199    # ------------- 
     200    # Dict Behavior 
     201    # ------------- 
     202 
    164203    def __iter__(self): 
    165204        names = self.layerOrder 
     
    180219            raise KeyError("%s not in layers" % name) 
    181220        self.postNotification("LayerSet.LayerWillBeDeleted", data=dict(name=name)) 
     221        layer = self._layers[name] 
     222        self.endSelfLayerNotificationObservation(layer) 
    182223        del self._layers[name] 
    183224        self._layerOrder.remove(name) 
     
    195236        return name in self._layers 
    196237 
    197     # ------- 
    198     # Methods 
    199     # ------- 
     238    # ---- 
     239    # Save 
     240    # ---- 
    200241 
    201242    def getSaveProgressBarTickCount(self, formatVersion): 
     
    281322            self._layerActionHistory.append(dict(action="new", name=layer.name)) 
    282323 
    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 
    286335 
    287336    def _layerDirtyStateChange(self, notification): 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/lib.py

    r1014 r1022  
     1import weakref 
    12from defcon.objects.base import BaseDictObject 
    23 
     
    4748    representationFactories = {} 
    4849 
    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 
    5075 
    5176    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 
    5784 
    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.") 
    5994 
    6095    def _get_layerSet(self): 
     
    67102 
    68103    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 
    73109 
    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.") 
    75119 
    76120    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 
    82124 
    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 
    84132 
     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 
    85144 
    86145 
  • packages/defcon/branches/ufo3/Lib/defcon/objects/uniData.py

    r1014 r1022  
     1import weakref 
    12import unicodedata 
    23from fontTools.agl import AGL2UV 
     
    4445    representationFactories = {} 
    4546 
    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) 
    4751        super(UnicodeData, self).__init__() 
     52        self.beginSelfNotificationObservation() 
    4853        self._glyphNameToForcedUnicode = {} 
    4954        self._forcedUnicodeToGlyphName = {} 
     55 
     56    # -------------- 
     57    # Parent Objects 
     58    # -------------- 
     59 
     60    def getParent(self): 
     61        return self.layer 
    5062 
    5163    def _get_font(self): 
     
    963975        return glyphNames 
    964976 
     977    # ------------------------ 
     978    # Notification Observation 
     979    # ------------------------ 
     980 
     981    def endSelfNotificationObservation(self): 
     982        super(UnicodeData, self).endSelfNotificationObservation() 
     983        self._layer = None 
     984 
    965985 
    966986# ----- 
  • packages/defcon/branches/ufo3/Lib/defcon/pens/glyphObjectPointPen.py

    r957 r1022  
    88 
    99    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() 
    1112        self._contour.identifier = identifier 
    1213 
     
    1415        self._contour.dirty = False 
    1516        self._glyph.appendContour(self._contour) 
     17        self._contour.enableNotifications() 
    1618        self._contour = None 
    1719 
  • packages/defcon/branches/ufo3/Lib/defcon/test/testAll.py

    r967 r1022  
    1717from defcon.objects import imageSet 
    1818from defcon.objects import image 
     19from defcon.objects import dataSet 
    1920 
    2021test = [ 
     
    3435    groups, 
    3536    imageSet, 
    36     image 
     37    image, 
     38    dataSet 
    3739] 
    3840 
  • packages/defcon/branches/ufo3/tools/infoObjectGenerator.py

    r1016 r1022  
    6868print 
    6969 
     70print "import weakref" 
    7071print "from warnings import warn" 
    7172print "import ufoLib" 
     
    102103print 
    103104 
    104 print "    def __init__(self, guidelineClass=None):" 
     105print "    def __init__(self, font=None, guidelineClass=None):" 
     106print "        if font is not None:" 
     107print "            font = weakref.ref(font)" 
     108print "        self._font = font" 
    105109print "        super(Info, self).__init__()" 
     110print "        self.beginSelfNotificationObservation()" 
    106111print "        self._identifiers = set()" 
    107112print "        if guidelineClass is None:" 
    108113print "            guidelineClass = Guideline" 
    109114print "        self._guidelineClass = guidelineClass" 
    110  
    111115 
    112116defaults = dict( 
     
    127131print 
    128132 
     133print "    def __del__(self):" 
     134print "        super(Info, self).__del__()" 
     135print "        self._guidelines = None" 
     136print 
     137 
     138print "    def getParent(self):" 
     139print "        return self.font" 
     140print 
     141 
    129142print 
    130143print "    def _get_font(self):" 
    131 print "        return self.getParent()" 
     144print "        if self._font is not None:" 
     145print "            return self._font()" 
     146print "        return None" 
    132147print 
    133148print "    font = property(_get_font, doc=\"The :class:`Font` that this object belongs to.\")" 
     
    192207    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.") 
    193208 
    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") 
    203226 
    204227    def appendGuideline(self, guideline): 
     
    226249        assert guideline not in self._guidelines 
    227250        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: 
    232256            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) 
    236264        self._guidelines.insert(index, guideline) 
    237265        self.postNotification("Info.GuidelinesChanged") 
     
    248276            self._identifiers.remove(guideline.identifier) 
    249277        self._guidelines.remove(guideline) 
    250         self._removeParentDataInGuideline(guideline) 
     278        self.endSelfGuidelineNotificationObservation(guideline) 
    251279        self.postNotification("Info.GuidelinesChanged") 
    252280        self.dirty = True 
     
    269297        self.releaseHeldNotifications() 
    270298 
    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 
    274310 
    275311    def _guidelineChanged(self, notification): 
Note: See TracChangeset for help on using the changeset viewer.