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

Legend:
Removed from v.484  
changed lines
  Added in v.2657

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26