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

Legend:
Removed from v.449  
changed lines
  Added in v.2374

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26