/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 479 by jonathan, Thu Mar 6 16:46:07 2003 UTC revision 2688 by frank, Fri Jun 30 12:27:20 2006 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2001, 2003, 2005, 2006 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
4    # Jan-Oliver Wagner <[email protected]> (2005)
5    # Frank Koormann <[email protected]> (2006)
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 16  an input value falls with a range that d Line 18  an input value falls with a range that d
18  If no mapping can be found then default data will  If no mapping can be found then default data will
19  be returned. Input values must be hashable objects  be returned. Input values must be hashable objects
20    
21  See the description of GetGroup() for more information  See the description of FindGroup() for more information
22  on the mapping algorithm.  on the mapping algorithm.
23  """  """
24        
25  # fix for people using python2.1  import copy, operator, types
26  from __future__ import nested_scopes  import re
27    
28  from types import *  from Thuban import _
29    
30  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  from messages import \
31       LAYER_VISIBILITY_CHANGED      LAYER_PROJECTION_CHANGED, \
32        LAYER_LEGEND_CHANGED, \
33        LAYER_VISIBILITY_CHANGED,\
34        CLASS_CHANGED
35    
36  from Thuban import _  from Thuban.Model.color import Color, Transparent, Black
37  from Thuban.Model.color import Color  from Thuban.Model.range import Range
38    
39  import Thuban.Model.layer  import Thuban.Model.layer
40    
41  # constants  from Thuban.Lib.connector import Publisher
 RANGE_MIN  = 0  
 RANGE_MAX  = 1  
 RANGE_DATA = 2  
   
 class Classification:  
     """Encapsulates the classification of layer. The Classification  
     divides some kind of data into Groups which are associated with  
     properties. Later the properties can be retrieved by matching  
     data values to the appropriate group."""  
   
     def __init__(self, layer = None, field = None):  
         """Initialize a classification.  
42    
43             layer -- the Layer object who owns this classification  class Classification(Publisher):
44        """Encapsulates the classification of layer.
45        
46        The Classification divides some kind of data into Groups which
47        are associated with properties. Later the properties can be
48        retrieved by matching data values to the appropriate group.
49        """
50    
51             field -- the name of the data table field that      def __init__(self):
52                      is to be used to classify layer properties          """Initialize a classification."""
         """  
53    
54          self.layer = None          self.__groups = []
         self.field = None  
         self.fieldType = None  
         self.groups = []  
         self.__sendMessages = False  
55    
         self.__ToggleMessages(False)  
56          self.SetDefaultGroup(ClassGroupDefault())          self.SetDefaultGroup(ClassGroupDefault())
         self.SetLayer(layer)  
         self.SetField(field)  
   
         self.__ToggleMessages(True)  
57    
58      def __iter__(self):      def __iter__(self):
59          return ClassIterator(self.groups)          return ClassIterator(self.__groups)
   
     def __ToggleMessages(self, on):  
         self.__sendMessages = on  
   
     def __SendMessage(self, message):  
         """Send the message 'message' to the parent layer."""  
         if self.__sendMessages and self.layer is not None:  
             self.layer.changed(message, self.layer)  
       
     def SetField(self, field = None):  
         """Set the name of the data table field to use.  
           
            If there is no layer then the field type is set to None,  
            otherwise the layer is queried to find the type of the  
            field data  
   
            field -- if None then all values map to the default data  
         """  
   
         if field == "":  
             field = None  
   
         self.field = field  
   
         if self.layer is not None:  
             fieldType = self.layer.GetFieldType(field)  
         else:  
             fieldType = None  
   
         self.SetFieldType(fieldType)  
   
         # XXX: if fieldType comes back None then field isn't in the table!  
   
         self.__SendMessage(LAYER_LEGEND_CHANGED)  
   
     def GetField(self):  
         """Return the name of the field."""  
         return self.field  
   
     def GetFieldType(self):  
         """Return the field type."""  
         return self.fieldType  
   
     def SetFieldType(self, type):  
         self.fieldType = type  
60    
61      def SetLayer(self, layer):      def __deepcopy__(self, memo):
62          """Set the owning Layer of this classification."""          clazz = Classification()
63    
64          if __debug__:          clazz.__groups[0] = copy.deepcopy(self.__groups[0])
             if layer is not None:  
                 assert(isinstance(layer, Thuban.Model.layer.Layer))  
65    
66          # prevent infinite recursion when calling SetClassification()          for i in range(1, len(self.__groups)):
67          if self.layer is not None and layer == self.layer:              clazz.__groups.append(copy.deepcopy(self.__groups[i]))
68              return  
69            return clazz
70          self.layer = layer  
71          self.SetField(self.GetField()) # XXX: this sync's the fieldType      def __SendNotification(self):
72            """Notify the layer that this class has changed."""
73          if self.layer is not None:          self.issue(CLASS_CHANGED)
74              self.layer.SetClassification(self)  
75        def __getattr__(self, attr):
76          #self.__SendMessage(LAYER_LEGEND_CHANGED)          """Generate the compiled classification on demand"""
77            if attr == "_compiled_classification":
78                self._compile_classification()
79                return self._compiled_classification
80            raise AttributeError(attr)
81    
82        def _compile_classification(self):
83            """Generate the compiled classification
84    
85            The compiled classification is a more compact representation of
86            the classification groups that is also more efficient for
87            performing the classification.
88    
89            The compiled classification is a list of tuples. The first
90            element of the tuple is a string which describes the rest of the
91            tuple. There are two kinds of tuples:
92    
93              'singletons'
94    
95                The second element of the tuple is a dictionary which
96                combines several consecutive ClassGroupSingleton instances.
97                The dictionary maps the values of the singletons (as
98                returned by the GetValue() method) to the corresponding
99                group.
100    
101              'range'
102    
103                The tuple describes a ClassGroupRange instance. The tuples
104                second element is a tuple fo the form (lfunc, min, max,
105                rfunc, group) where group is the original group object,
106                lfunc and rfuct are comparison functions and min and max are
107                lower and upper bounds of the range. Given a value and such
108                a tuple the group matches if and only if
109    
110                    lfunc(min, value) and rfunc(max, value)
111    
112                is true.
113    
114              'pattern'
115                
116                The tuple contains the compiled regular expression object and
117                the original group object.
118    
119            The compiled classification is bound to
120            self._compile_classification.
121            """
122            compiled = []
123            for group in self.__groups[1:]:
124                if isinstance(group, ClassGroupSingleton):
125                    if not compiled or compiled[-1][0] != "singletons":
126                        compiled.append(("singletons", {}))
127                    compiled[-1][1].setdefault(group.GetValue(), group)
128                elif isinstance(group, ClassGroupPattern):
129                    pattern = re.compile(group.GetPattern())
130                    compiled.append(("pattern", (pattern, group)))
131                elif isinstance(group, ClassGroupRange):
132                    left, min, max, right = group.GetRangeTuple()
133                    if left == "[":
134                        lfunc = operator.le
135                    elif left == "]":
136                        lfunc = operator.lt
137                    if right == "[":
138                        rfunc = operator.gt
139                    elif right == "]":
140                        rfunc = operator.ge
141                    compiled.append(("range", (lfunc, min, max, rfunc, group)))
142                else:
143                    raise TypeError("Unknown group type %s", group)
144            self._compiled_classification = compiled
145    
146      def GetLayer(self):      def _clear_compiled_classification(self):
147          """Return the parent layer."""          """Reset the compiled classification.
         return self.layer  
148    
149      def SetDefaultGroup(self, group):          If will be created on demand when self._compiled_classification
150          """Set the group to be used when a value can't be classified.          is accessed again.
151    
152             group -- group that the value maps to.          Call this method whenever self.__groups is modified.
153          """          """
154            try:
155          assert(isinstance(group, ClassGroupDefault))              del self._compiled_classification
156          self.AddGroup(group)          except:
157                pass
     def GetDefaultGroup(self):  
         """Return the default group."""  
         return self.groups[0]  
158    
159      #      #
160      # these SetDefault* methods are really only provided for      # these SetDefault* methods are really only provided for
# Line 161  class Classification: Line 167  class Classification:
167    
168          fill -- a Color object.          fill -- a Color object.
169          """          """
         assert(isinstance(fill, Color))  
170          self.GetDefaultGroup().GetProperties().SetFill(fill)          self.GetDefaultGroup().GetProperties().SetFill(fill)
171          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
172                    
173      def GetDefaultFill(self):      def GetDefaultFill(self):
174          """Return the default fill color."""          """Return the default fill color."""
# Line 174  class Classification: Line 179  class Classification:
179    
180          color -- a Color object.          color -- a Color object.
181          """          """
         assert(isinstance(color, Color))  
182          self.GetDefaultGroup().GetProperties().SetLineColor(color)          self.GetDefaultGroup().GetProperties().SetLineColor(color)
183          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
184                    
185      def GetDefaultLineColor(self):      def GetDefaultLineColor(self):
186          """Return the default line color."""          """Return the default line color."""
# Line 187  class Classification: Line 191  class Classification:
191    
192          lineWidth -- an integer > 0.          lineWidth -- an integer > 0.
193          """          """
194          assert(isinstance(lineWidth, IntType))          assert isinstance(lineWidth, types.IntType)
195          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)
196          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
197                    
198      def GetDefaultLineWidth(self):      def GetDefaultLineWidth(self):
199          """Return the default line width."""          """Return the default line width."""
200          return self.GetDefaultGroup().GetProperties().GetLineWidth()          return self.GetDefaultGroup().GetProperties().GetLineWidth()
201                    
     def AddGroup(self, item):  
         """Add a new ClassGroup item to the classification.  
202    
203          item -- this must be a valid ClassGroup object      #
204          """      # The methods that manipulate self.__groups have to be kept in
205        # sync. We store the default group in index 0 to make it
206        # convienent to iterate over the classification's groups, but
207        # from the user's perspective the first (non-default) group is
208        # at index 0 and the DefaultGroup is a special entity.
209        #
210    
211          assert(isinstance(item, ClassGroup))      def SetDefaultGroup(self, group):
212            """Set the group to be used when a value can't be classified.
213    
214          if len(self.groups) > 0 and isinstance(item, ClassGroupDefault):          group -- group that the value maps to.
215              self.groups[0] = item          """
216              #self.SetDefaultGroup(item)          assert isinstance(group, ClassGroupDefault)
217            if len(self.__groups) > 0:
218                self.__groups[0] = group
219          else:          else:
220              self.groups.append(item)              self.__groups.append(group)
221            self.__SendNotification()
222    
223          self.__SendMessage(LAYER_LEGEND_CHANGED)      def GetDefaultGroup(self):
224            """Return the default group."""
225      def GetGroup(self, value):          return self.__groups[0]
         """Return the associated group, or the default group.  
226    
227             Groups are checked in the order the were added to the      def AppendGroup(self, item):
228             Classification.          """Append a new ClassGroup item to the classification.
229    
230             value -- the value to classify. If there is no mapping,          item -- this must be a valid ClassGroup object
                     the field is None or value is None,  
                     return the default properties  
231          """          """
232    
233          if self.GetField() is not None and value is not None:          self.InsertGroup(self.GetNumGroups(), item)
234    
235              for i in range(1, len(self.groups)):      def InsertGroup(self, index, group):
236                  group = self.groups[i]          assert isinstance(group, ClassGroup)
237                  if group.Matches(value):          self.__groups.insert(index + 1, group)
238                      return group          self._clear_compiled_classification()
239            self.__SendNotification()
240    
241        def RemoveGroup(self, index):
242            """Remove the classification group with the given index"""
243            self.__groups.pop(index + 1)
244            self._clear_compiled_classification()
245            self.__SendNotification()
246    
247        def ReplaceGroup(self, index, group):
248            assert isinstance(group, ClassGroup)
249            self.__groups[index + 1] = group
250            self._clear_compiled_classification()
251            self.__SendNotification()
252    
253        def GetGroup(self, index):
254            return self.__groups[index + 1]
255    
256        def GetNumGroups(self):
257            """Return the number of non-default groups in the classification."""
258            return len(self.__groups) - 1
259    
260        def FindGroup(self, value):
261            """Return the group that matches the value.
262    
263            Groups are effectively checked in the order the were added to
264            the Classification.
265    
266            value -- the value to classify. If there is no mapping or value
267                     is None, return the default properties
268            """
269    
270            if value is not None:
271                for typ, params in self._compiled_classification:
272                    if typ == "singletons":
273                        group = params.get(value)
274                        if group is not None:
275                            return group
276                    elif typ == "range":
277                        lfunc, min, max, rfunc, g = params
278                        if lfunc(min, value) and rfunc(max, value):
279                            return g
280                    elif typ == "pattern":
281                        # TODO: make pattern more robust. The following chrashes
282                        # if accidently be applied on non-string columns.
283                        # Usually the UI prevents this.
284                        p, g = params
285                        if p.match(value):
286                            return g
287    
288          return self.GetDefaultGroup()          return self.GetDefaultGroup()
289    
290      def GetProperties(self, value):      def GetProperties(self, value):
291          """Return the properties associated with the given value."""          """Return the properties associated with the given value.
292          
293            Use this function rather than Classification.FindGroup().GetProperties()
294            since the returned group may be a ClassGroupMap which doesn't support
295            a call to GetProperties().
296            """
297    
298          group = self.GetGroup(value)          group = self.FindGroup(value)
299          if isinstance(group, ClassGroupMap):          if isinstance(group, ClassGroupMap):
300              return group.GetPropertiesFromValue(value)              return group.GetPropertiesFromValue(value)
301          else:          else:
# Line 244  class Classification: Line 305  class Classification:
305          items = []          items = []
306    
307          def build_color_item(text, color):          def build_color_item(text, color):
308              if color is Color.None:              if color is Transparent:
309                  return ("%s: %s" % (text, _("None")), None)                  return ("%s: %s" % (text, _("None")), None)
310    
311              return ("%s: (%.3f, %.3f, %.3f)" %              return ("%s: (%.3f, %.3f, %.3f)" %
# Line 264  class Classification: Line 325  class Classification:
325              i.append(build_color_item(_("Line Color"), v))              i.append(build_color_item(_("Line Color"), v))
326              v = props.GetLineWidth()              v = props.GetLineWidth()
327              i.append(_("Line Width: %s") % v)              i.append(_("Line Width: %s") % v)
328    
329                # Note: Size is owned by all properties, so
330                # a size will also appear where it does not
331                # make sense like for lines and polygons.
332                v = props.GetSize()
333                i.append(_("Size: %s") % v)
334    
335              v = props.GetFill()              v = props.GetFill()
336              i.append(build_color_item(_("Fill"), v))              i.append(build_color_item(_("Fill"), v))
337              return (label, i)              return (label, i)
338    
339          for p in self:          for p in self:
340              if isinstance(p, ClassGroupDefault):              items.append(build_item(p, p.GetDisplayText()))
                 items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))  
             elif isinstance(p, ClassGroupSingleton):  
                 items.append(build_item(p, str(p.GetValue())))  
             elif isinstance(p, ClassGroupRange):  
                 items.append(build_item(p, "%s - %s" %  
                                            (p.GetMin(), p.GetMax())))  
341    
342          return (_("Classification"), items)          return (_("Classification"), items)
343    
344  class ClassIterator:  class ClassIterator:
345      """Allows the Groups in a Classifcation to be interated over.      """Allows the Groups in a Classifcation to be interated over.
346    
# Line 290  class ClassIterator: Line 352  class ClassIterator:
352          """Constructor.          """Constructor.
353    
354          default -- the default group          default -- the default group
355    
356          points -- a list of singleton groups          points -- a list of singleton groups
357    
358          ranges -- a list of range groups          ranges -- a list of range groups
359    
360          maps -- a list of map groups          maps -- a list of map groups
361          """          """
362    
363          self.data = data #[default, points, ranges, maps]          self.data = data
364          self.data_index = 0          self.data_index = 0
         #self.data_iter = iter(self.data)  
         #self.iter = None  
365    
366      def __iter__(self):      def __iter__(self):
367          return self          return self
# Line 315  class ClassIterator: Line 375  class ClassIterator:
375              d = self.data[self.data_index]              d = self.data[self.data_index]
376              self.data_index += 1              self.data_index += 1
377              return d              return d
378            
 #       if self.iter is None:  
 #           try:  
 #               self.data_item = self.data_iter.next()  
 #               self.iter = iter(self.data_item)  
 #           except TypeError:  
 #               return self.data_item  
   
 #       try:  
 #           return self.iter.next()  
 #       except StopIteration:  
 #           self.iter = None  
 #           return self.next()  
         
379  class ClassGroupProperties:  class ClassGroupProperties:
380      """Represents the properties of a single Classification Group.      """Represents the properties of a single Classification Group.
381      
382      These are used when rendering a layer."""      These are used when rendering a layer."""
383    
384        # TODO: Actually, size is only relevant for point objects.
385        # Eventually it should be spearated, e.g. when introducing symbols.
386    
387      def __init__(self, props = None):      def __init__(self, props = None):
388          """Constructor.          """Constructor.
389    
390          props -- a ClassGroupProperties object. The class is copied if          props -- a ClassGroupProperties object. The class is copied if
391                   prop is not None. Otherwise, a default set of properties                   prop is not None. Otherwise, a default set of properties
392                   is created such that: line color = Color.Black, line width = 1,                   is created such that: line color = Black, line width = 1,
393                   and fill color = Color.None                   size = 5 and fill color = Transparent
394          """          """
395    
         self.stroke = None  
         self.strokeWidth = 0  
         self.fill = None  
   
396          if props is not None:          if props is not None:
397              self.SetProperties(props)              self.SetProperties(props)
398          else:          else:
399              self.SetLineColor(Color.None)              self.SetLineColor(Black)
400              self.SetLineWidth(1)              self.SetLineWidth(1)
401              self.SetFill(Color.None)              self.SetSize(5)
402                self.SetFill(Transparent)
403    
404      def SetProperties(self, props):      def SetProperties(self, props):
405          """Set this class's properties to those in class props."""          """Set this class's properties to those in class props."""
406    
407          assert(isinstance(props, ClassGroupProperties))          assert isinstance(props, ClassGroupProperties)
408          self.SetLineColor(props.GetLineColor())          self.SetLineColor(props.GetLineColor())
409          self.SetLineWidth(props.GetLineWidth())          self.SetLineWidth(props.GetLineWidth())
410            self.SetSize(props.GetSize())
411          self.SetFill(props.GetFill())          self.SetFill(props.GetFill())
412            
413      def GetLineColor(self):      def GetLineColor(self):
414          """Return the line color as a Color object."""          """Return the line color as a Color object."""
415          return self.stroke          return self.__stroke
416    
417      def SetLineColor(self, color):      def SetLineColor(self, color):
418          """Set the line color.          """Set the line color.
# Line 372  class ClassGroupProperties: Line 420  class ClassGroupProperties:
420          color -- the color of the line. This must be a Color object.          color -- the color of the line. This must be a Color object.
421          """          """
422    
423          assert(isinstance(color, Color))          self.__stroke = color
         self.stroke = color  
424    
425      def GetLineWidth(self):      def GetLineWidth(self):
426          """Return the line width."""          """Return the line width."""
427          return self.strokeWidth          return self.__strokeWidth
428    
429      def SetLineWidth(self, lineWidth):      def SetLineWidth(self, lineWidth):
430          """Set the line width.          """Set the line width.
431    
432          lineWidth -- the new line width. This must be > 0.          lineWidth -- the new line width. This must be > 0.
433          """          """
434          assert(isinstance(lineWidth, IntType))          assert isinstance(lineWidth, types.IntType)
435          if (lineWidth < 1):          if (lineWidth < 1):
436              raise ValueError(_("lineWidth < 1"))              raise ValueError(_("lineWidth < 1"))
437    
438          self.strokeWidth = lineWidth          self.__strokeWidth = lineWidth
439    
440        def GetSize(self):
441            """Return the size."""
442            return self.__size
443    
444        def SetSize(self, size):
445            """Set the size.
446    
447            size -- the new size. This must be > 0.
448            """
449            assert isinstance(size, types.IntType)
450            if (size < 1):
451                raise ValueError(_("size < 1"))
452    
453            self.__size = size
454    
455      def GetFill(self):      def GetFill(self):
456          """Return the fill color as a Color object."""          """Return the fill color as a Color object."""
457          return self.fill          return self.__fill
458    
459      def SetFill(self, fill):      def SetFill(self, fill):
460          """Set the fill color.          """Set the fill color.
461    
462          fill -- the color of the fill. This must be a Color object.          fill -- the color of the fill. This must be a Color object.
463          """          """
464    
465          assert(isinstance(fill, Color))          self.__fill = fill
         self.fill = fill  
466    
467      def __eq__(self, other):      def __eq__(self, other):
468          """Return true if 'props' has the same attributes as this class"""          """Return true if 'props' has the same attributes as this class"""
469    
470            #
471            # using 'is' over '==' results in a huge performance gain
472            # in the renderer
473            #
474          return isinstance(other, ClassGroupProperties)   \          return isinstance(other, ClassGroupProperties)   \
475              and self.stroke      == other.GetLineColor() \              and (self.__stroke is other.__stroke or      \
476              and self.strokeWidth == other.GetLineWidth() \                   self.__stroke == other.__stroke)        \
477              and self.fill        == other.GetFill()              and (self.__fill is other.__fill or          \
478                     self.__fill == other.__fill)            \
479                and self.__strokeWidth == other.__strokeWidth\
480                and self.__size == other.__size
481    
482      def __ne__(self, other):      def __ne__(self, other):
483          return not self.__eq__(other)          return not self.__eq__(other)
484    
485        def __copy__(self):
486            return ClassGroupProperties(self)
487    
488        def __deepcopy__(self):
489            return ClassGroupProperties(self)
490    
491        def __repr__(self):
492            return repr((self.__stroke, self.__strokeWidth, self.__size,
493                        self.__fill))
494    
495  class ClassGroup:  class ClassGroup:
496      """A base class for all Groups within a Classification"""      """A base class for all Groups within a Classification"""
497    
498      def __init__(self, label = ""):      def __init__(self, label = "", props = None, group = None):
499          """Constructor.          """Constructor.
500    
501          label -- A string representing the Group's label          label -- A string representing the Group's label
502          """          """
503    
504          self.label = None          if group is not None:
505                self.SetLabel(copy.copy(group.GetLabel()))
506          self.SetLabel(label)              self.SetProperties(copy.copy(group.GetProperties()))
507                self.SetVisible(group.IsVisible())
508            else:
509                self.SetLabel(label)
510                self.SetProperties(props)
511                self.SetVisible(True)
512    
513      def GetLabel(self):      def GetLabel(self):
514          """Return the Group's label."""          """Return the Group's label."""
515          return self.label          return self.label
516    
517      def SetLabel(self, label):      def SetLabel(self, label):
518          """Set the Group's label.          """Set the Group's label.
519    
520          label -- a string representing the Group's label. This must          label -- a string representing the Group's label. This must
521                   not be None.                   not be None.
522          """          """
523          assert(isinstance(label, StringType))          assert isinstance(label, types.StringTypes)
524          self.label = label          self.label = label
525    
526        def GetDisplayText(self):
527            assert False, "GetDisplay must be overridden by subclass!"
528            return ""
529    
530      def Matches(self, value):      def Matches(self, value):
531          """Determines if this Group is associated with the given value.          """Determines if this Group is associated with the given value.
532    
533          Returns False. This needs to be overridden by all subclasses.          Returns False. This needs to be overridden by all subclasses.
534          """          """
535            assert False, "GetMatches must be overridden by subclass!"
536          return False          return False
537    
538      def GetProperties(self):      def GetProperties(self):
539          """Return the properties associated with the given value.          """Return the properties associated with the given value."""
540    
541          Returns None. This needs to be overridden by all subclasses.          return self.prop
         """  
         return None  
542    
543        def SetProperties(self, prop):
544            """Set the properties associated with this Group.
545    
546            prop -- a ClassGroupProperties object. if prop is None,
547                    a default set of properties is created.
548            """
549    
550            if prop is None: prop = ClassGroupProperties()
551            assert isinstance(prop, ClassGroupProperties)
552            self.prop = prop
553    
554        def IsVisible(self):
555            return self.visible
556    
557        def SetVisible(self, visible):
558            self.visible = visible
559    
560        def __eq__(self, other):
561            return isinstance(other, ClassGroup) \
562                and self.label == other.label \
563                and self.GetProperties() == other.GetProperties()
564    
565        def __ne__(self, other):
566            return not self.__eq__(other)
567    
568        def __repr__(self):
569            return repr(self.label) + ", " + repr(self.GetProperties())
570            
571  class ClassGroupSingleton(ClassGroup):  class ClassGroupSingleton(ClassGroup):
572      """A Group that is associated with a single value."""      """A Group that is associated with a single value."""
573    
574      def __init__(self, value = 0, prop = None, label = ""):      def __init__(self, value = 0, props = None, label = "", group = None):
575          """Constructor.          """Constructor.
576    
577          value -- the associated value.          value -- the associated value.
# Line 468  class ClassGroupSingleton(ClassGroup): Line 581  class ClassGroupSingleton(ClassGroup):
581    
582          label -- a label for this group.          label -- a label for this group.
583          """          """
584          ClassGroup.__init__(self, label)          ClassGroup.__init__(self, label, props, group)
   
         self.prop = None  
         self.value = None  
585    
586          self.SetValue(value)          self.SetValue(value)
         self.SetProperties(prop)  
587    
588      def __copy__(self):      def __copy__(self):
589          return ClassGroupSingleton(self.GetValue(),          return ClassGroupSingleton(self.GetValue(),
590                                     self.GetProperties(),                                     self.GetProperties(),
591                                     self.GetLabel())                                     self.GetLabel())
592    
593        def __deepcopy__(self, memo):
594            return ClassGroupSingleton(self.GetValue(), group = self)
595    
596      def GetValue(self):      def GetValue(self):
597          """Return the associated value."""          """Return the associated value."""
598          return self.value          return self.__value
599    
600      def SetValue(self, value):      def SetValue(self, value):
601          """Associate this Group with the given value."""          """Associate this Group with the given value."""
602          self.value = value          self.__value = value
603    
604      def Matches(self, value):      def Matches(self, value):
605          """Determine if the given value matches the associated Group value."""          """Determine if the given value matches the associated Group value."""
606    
607          """Returns True if the value matches, False otherwise."""          """Returns True if the value matches, False otherwise."""
608    
609          return self.value == value          return self.__value == value
610    
611      def GetProperties(self):      def GetDisplayText(self):
612          """Return the Properties associated with this Group."""          label = self.GetLabel()
613    
614          return self.prop          if label != "": return label
615    
616      def SetProperties(self, prop):          return str(self.GetValue())
         """Set the properties associated with this Group.  
   
         prop -- a ClassGroupProperties object. if prop is None,  
                 a default set of properties is created.  
         """  
   
         if prop is None: prop = ClassGroupProperties()  
         assert(isinstance(prop, ClassGroupProperties))  
         self.prop = prop  
617    
618      def __eq__(self, other):      def __eq__(self, other):
619          return isinstance(other, ClassGroupSingleton) \          return ClassGroup.__eq__(self, other) \
620              and self.GetProperties() == other.GetProperties() \              and isinstance(other, ClassGroupSingleton) \
621              and self.GetValue() == other.GetValue()              and self.__value == other.__value
622    
623      def __ne__(self, other):      def __repr__(self):
624          return not self.__eq__(other)          return "(" + repr(self.__value) + ", " + ClassGroup.__repr__(self) + ")"
625    
626  class ClassGroupDefault(ClassGroup):  class ClassGroupDefault(ClassGroup):
627      """The default Group. When values do not match any other      """The default Group. When values do not match any other
628         Group within a Classification, the properties from this         Group within a Classification, the properties from this
629         class are used."""         class are used."""
630    
631      def __init__(self, prop = None, label = ""):      def __init__(self, props = None, label = "", group = None):
632          """Constructor.          """Constructor.
633    
634          prop -- a ClassGroupProperites object. If prop is None a default          prop -- a ClassGroupProperites object. If prop is None a default
# Line 534  class ClassGroupDefault(ClassGroup): Line 637  class ClassGroupDefault(ClassGroup):
637          label -- a label for this group.          label -- a label for this group.
638          """          """
639    
640          ClassGroup.__init__(self, label)          ClassGroup.__init__(self, label, props, group)
         self.SetProperties(prop)  
641    
642      def __copy__(self):      def __copy__(self):
643          return ClassGroupDefault(self.GetProperties(), self.GetLabel())          return ClassGroupDefault(self.GetProperties(), self.GetLabel())
644    
645        def __deepcopy__(self, memo):
646            return ClassGroupDefault(label = self.GetLabel(), group = self)
647    
648      def Matches(self, value):      def Matches(self, value):
649          return True          return True
650    
651      def GetProperties(self):      def GetDisplayText(self):
652          """Return the Properties associated with this Group."""          label = self.GetLabel()
         return self.prop  
   
     def SetProperties(self, prop):  
         """Set the properties associated with this Group.  
653    
654          prop -- a ClassGroupProperties object. if prop is None,          if label != "": return label
                 a default set of properties is created.  
         """  
655    
656          if prop is None: prop = ClassGroupProperties()          return _("DEFAULT")
         assert(isinstance(prop, ClassGroupProperties))  
         self.prop = prop  
657    
658      def __eq__(self, other):      def __eq__(self, other):
659          return isinstance(other, ClassGroupDefault) \          return ClassGroup.__eq__(self, other) \
660                and isinstance(other, ClassGroupDefault) \
661              and self.GetProperties() == other.GetProperties()              and self.GetProperties() == other.GetProperties()
662    
663      def __ne__(self, other):      def __repr__(self):
664          return not self.__eq__(other)          return "(" + ClassGroup.__repr__(self) + ")"
665    
666  class ClassGroupRange(ClassGroup):  class ClassGroupRange(ClassGroup):
667      """A Group that represents a range of values that map to the same      """A Group that represents a range of values that map to the same
668         set of properties."""         set of properties."""
669    
670      def __init__(self, min = 0, max = 1, prop = None, label = ""):      def __init__(self, _range = (0,1), props = None, label = "", group=None):
671          """Constructor.          """Constructor.
672    
673          The minumum value must be strictly less than the maximum.          The minumum value must be strictly less than the maximum.
674    
675          min -- the minimum range value          _range -- either a tuple (min, max) where min < max or
676                      a Range object
         max -- the maximum range value  
677    
678          prop -- a ClassGroupProperites object. If prop is None a default          prop -- a ClassGroupProperites object. If prop is None a default
679                   set of properties is created.                   set of properties is created.
# Line 584  class ClassGroupRange(ClassGroup): Line 681  class ClassGroupRange(ClassGroup):
681          label -- a label for this group.          label -- a label for this group.
682          """          """
683    
684          ClassGroup.__init__(self, label)          ClassGroup.__init__(self, label, props, group)
685            self.SetRange(_range)
         self.min = self.max = 0  
         self.prop = None  
   
         self.SetRange(min, max)  
         self.SetProperties(prop)  
686    
687      def __copy__(self):      def __copy__(self):
688          return ClassGroupRange(self.GetMin(),          return ClassGroupRange(self.__range,
689                                 self.GetMax(),                                 props = self.GetProperties(),
690                                 self.GetProperties(),                                 label = self.GetLabel())
691                                 self.GetLabel())  
692        def __deepcopy__(self, memo):
693            return ClassGroupRange(copy.copy(self.__range),
694                                   group = self)
695    
696      def GetMin(self):      def GetMin(self):
697          """Return the range's minimum value."""          """Return the range's minimum value."""
698          return self.min          return self.__range.GetRange()[1]
699    
700      def SetMin(self, min):      def SetMin(self, min):
701          """Set the range's minimum value.          """Set the range's minimum value.
# Line 609  class ClassGroupRange(ClassGroup): Line 704  class ClassGroupRange(ClassGroup):
704                 maximum value. Use SetRange() to change both min and max values.                 maximum value. Use SetRange() to change both min and max values.
705          """          """
706            
707          self.SetRange(min, self.max)          self.SetRange((min, self.__range.GetRange()[2]))
708    
709      def GetMax(self):      def GetMax(self):
710          """Return the range's maximum value."""          """Return the range's maximum value."""
711          return self.max          return self.__range.GetRange()[2]
712    
713      def SetMax(self, max):      def SetMax(self, max):
714          """Set the range's maximum value.          """Set the range's maximum value.
# Line 621  class ClassGroupRange(ClassGroup): Line 716  class ClassGroupRange(ClassGroup):
716          max -- the new maximum. Note that this must be greater than the current          max -- the new maximum. Note that this must be greater than the current
717                 minimum value. Use SetRange() to change both min and max values.                 minimum value. Use SetRange() to change both min and max values.
718          """          """
719          self.SetRange(self.min, max)          self.SetRange((self.__range.GetRange()[1], max))
720    
721      def SetRange(self, min, max):      def SetRange(self, _range):
722          """Set a new range.          """Set a new range.
723    
724          Note that min must be strictly less than max.          _range -- Either a tuple (min, max) where min < max or
725                      a Range object.
726    
727          min -- the new minimum value          Raises ValueError on error.
         min -- the new maximum value  
728          """          """
729    
730          if min >= max:          if isinstance(_range, Range):
731              raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %              self.__range = _range
732                               (min, max))          elif isinstance(_range, types.TupleType) and len(_range) == 2:
733          self.min = min              self.__range = Range(("[", _range[0], _range[1], "["))
734          self.max = max          else:
735                raise ValueError()
736    
737      def GetRange(self):      def GetRange(self):
738          """Return the range as a tuple (min, max)"""          """Return the range as a string"""
739          return (self.min, self.max)          return self.__range.string(self.__range.GetRange())
740    
741        def GetRangeTuple(self):
742            return self.__range.GetRange()
743    
744      def Matches(self, value):      def Matches(self, value):
745          """Determine if the given value lies with the current range.          """Determine if the given value lies with the current range.
# Line 648  class ClassGroupRange(ClassGroup): Line 747  class ClassGroupRange(ClassGroup):
747          The following check is used: min <= value < max.          The following check is used: min <= value < max.
748          """          """
749    
750          return self.min <= value < self.max          return operator.contains(self.__range, value)
751    
752      def GetProperties(self):      def GetDisplayText(self):
753          """Return the Properties associated with this Group."""          label = self.GetLabel()
         return self.prop  
754    
755      def SetProperties(self, prop):          if label != "": return label
         """Set the properties associated with this Group.  
756    
757          prop -- a ClassGroupProperties object. if prop is None,          return self.__range.string(self.__range.GetRange())
758                  a default set of properties is created.  
759        def __eq__(self, other):
760            return ClassGroup.__eq__(self, other) \
761                and isinstance(other, ClassGroupRange) \
762                and self.__range == other.__range
763    
764        def __repr__(self):
765            return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"
766    
767    class ClassGroupPattern(ClassGroup):
768        """A Group that is associated with a reg exp pattern."""
769    
770        def __init__(self, pattern = "", props = None, label = "", group = None):
771            """Constructor.
772    
773            pattern -- the associated pattern.
774    
775            props   -- a ClassGroupProperites object. If props is None a default
776                       set of properties is created.
777    
778            label   -- a label for this group.
779          """          """
780          if prop is None: prop = ClassGroupProperties()          ClassGroup.__init__(self, label, props, group)
781          assert(isinstance(prop, ClassGroupProperties))  
782          self.prop = prop          self.SetPattern(pattern)
783    
784        def __copy__(self):
785            return ClassGroupPattern(self.GetPattern(),
786                                       self.GetProperties(),
787                                       self.GetLabel())
788    
789        def __deepcopy__(self, memo):
790            return ClassGroupPattern(self.GetPattern(), group = self)
791    
792        def GetPattern(self):
793            """Return the associated pattern."""
794            return self.__pattern
795    
796        def SetPattern(self, pattern):
797            """Associate this Group with the given pattern."""
798            self.__pattern = pattern
799    
800        def Matches(self, pattern):
801            """Check if the given pattern matches the associated Group pattern."""
802    
803            """Returns True if the value matches, False otherwise."""
804    
805            if re.match(self.__pattern, pattern):
806                return True
807            else:
808                return False
809    
810        def GetDisplayText(self):
811            label = self.GetLabel()
812    
813            if label != "": return label
814    
815            return str(self.GetPattern())
816    
817      def __eq__(self, other):      def __eq__(self, other):
818          return isinstance(other, ClassGroupRange) \          return ClassGroup.__eq__(self, other) \
819              and self.GetProperties() == other.GetProperties() \              and isinstance(other, ClassGroupPattern) \
820              and self.GetRange() == other.GetRange()              and self.__pattern == other.__pattern
821    
822      def __ne__(self, other):      def __repr__(self):
823          return not self.__eq__(other)          return "(" + repr(self.__pattern) + ", " + ClassGroup.__repr__(self) + ")"
824    
825  class ClassGroupMap(ClassGroup):  class ClassGroupMap(ClassGroup):
826      """Currently, this class is not used."""      """Currently, this class is not used."""
# Line 695  class ClassGroupMap(ClassGroup): Line 845  class ClassGroupMap(ClassGroup):
845      def GetPropertiesFromValue(self, value):      def GetPropertiesFromValue(self, value):
846          pass          pass
847    
848        def GetDisplayText(self):
849            return "Map: " + self.map_type
850    
851      #      #
852      # built-in mappings      # built-in mappings
853      #      #

Legend:
Removed from v.479  
changed lines
  Added in v.2688

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26