/[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 428 by jonathan, Mon Feb 24 18:46:35 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 GetClassData() 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  
   
 from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  
      LAYER_VISIBILITY_CHANGED  
24    
25  from Thuban import _  from Thuban import _
 from Thuban.Model.color import Color  
26    
27  from wxPython.wx import *  from messages import \
28        LAYER_PROJECTION_CHANGED, \
29        LAYER_LEGEND_CHANGED, \
30        LAYER_VISIBILITY_CHANGED,\
31        CLASS_CHANGED
32    
33  # constants  from Thuban.Model.color import Color, Transparent, Black
34  RANGE_MIN  = 0  from Thuban.Model.range import Range
 RANGE_MAX  = 1  
 RANGE_DATA = 2  
35    
36  class Classification:  import Thuban.Model.layer
37    
38      def __init__(self, layer = None, field = None):  from Thuban.Lib.connector import Publisher
         """Initialize a classification.  
39    
40             layer -- the layer object who owns this classification  class Classification(Publisher):
41        """Encapsulates the classification of layer.
42        
43        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             field -- the name of the data table field that      def __init__(self):
49                      is to be used to classify layer properties          """Initialize a classification."""
         """  
50    
51          self.layer = layer          self.__groups = []
52          self.points = {}  
53          self.ranges = []          self.SetDefaultGroup(ClassGroupDefault())
         self.maps   = []  
         self.DefaultData = ClassDataDefault()  
         self.field = field  
         #self.SetField(field)  
54    
55      def __iter__(self):      def __iter__(self):
56          return ClassIterator(self.DefaultData,          return ClassIterator(self.__groups)
57                               self.points.values(),  
58                               self.ranges,      def __deepcopy__(self, memo):
59                               self.maps)          clazz = Classification()
60    
61      def __SendMessage(self, message):          clazz.__groups[0] = copy.deepcopy(self.__groups[0])
62          if self.layer is not None:  
63              self.layer.changed(message, self.layer)          for i in range(1, len(self.__groups)):
64                    clazz.__groups.append(copy.deepcopy(self.__groups[i]))
65      def SetField(self, field):  
66          """Set the name of the data table field to use.          return clazz
           
            field -- if None then all values map to the default data  
         """  
67    
68          self.field = field      def __SendNotification(self):
69          self.__SendMessage(LAYER_LEGEND_CHANGED)          """Notify the layer that this class has changed."""
70            self.issue(CLASS_CHANGED)
71    
72      def GetField(self):      def __getattr__(self, attr):
73          return self.field          """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 SetLayer(self, layer):      def _compile_classification(self):
80          self.layer = layer          """Generate the compiled classification
         self.__SendMessage(LAYER_LEGEND_CHANGED)  
81    
82      def GetLayer(self):          The compiled classification is a more compact representation of
83          return layer.self          the classification groups that is also more efficient for
84            performing the classification.
85    
86      def SetDefaultData(self, data):          The compiled classification is a list of tuples. The first
87          """Set the data to be used when a value can't be classified.          element of the tuple is a string which describes the rest of the
88            tuple. There are two kinds of tuples:
89    
90             data -- data that the value maps to. See class description.            'singletons'
91    
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                    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(data.GetType() == ClassData.DEFAULT)      def _clear_compiled_classification(self):
136          self.DefaultData = data          """Reset the compiled classification.
137    
138      def GetDefaultData(self):          If will be created on demand when self._compiled_classification
139          return self.DefaultData          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 103  class Classification: Line 152  class Classification:
152      #      #
153    
154      def SetDefaultFill(self, fill):      def SetDefaultFill(self, fill):
155          self.DefaultData.SetFill(fill)          """Set the default fill color.
156          self.__SendMessage(LAYER_LEGEND_CHANGED)  
157            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.DefaultData.GetFill()          """Return the default fill color."""
164            return self.GetDefaultGroup().GetProperties().GetFill()
165                    
166      def SetDefaultStroke(self, stroke):      def SetDefaultLineColor(self, color):
167          self.DefaultData.SetStroke(stroke)          """Set the default line color.
168          self.__SendMessage(LAYER_LEGEND_CHANGED)  
169            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.DefaultData.GetStroke()          """Return the default line color."""
176            return self.GetDefaultGroup().GetProperties().GetLineColor()
177                    
178      def SetDefaultStrokeWidth(self, strokeWidth):      def SetDefaultLineWidth(self, lineWidth):
179          self.DefaultData.SetStrokeWidth(strokeWidth)          """Set the default line width.
180          self.__SendMessage(LAYER_LEGEND_CHANGED)  
181            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.DefaultData.GetStrokeWidth()          """Return the default line width."""
189            return self.GetDefaultGroup().GetProperties().GetLineWidth()
190                    
     def AddClassData(self, item):  
         type = item.GetType()  
191    
192          if type == ClassData.POINT:      #
193              self.points[item.GetValue()] = item      # The methods that manipulate self.__groups have to be kept in
194          elif type == ClassData.RANGE:      # sync. We store the default group in index 0 to make it
195              self.ranges.append(item)      # convienent to iterate over the classification's groups, but
196          elif type == ClassData.MAP:      # from the user's perspective the first (non-default) group is
197              self.maps.append(item)      # at index 0 and the DefaultGroup is a special entity.
198          elif type == ClassData.DEFAULT:      #
199              self.DefaultData = 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 ClassData type %s") % type)              self.__groups.append(group)
210            self.__SendNotification()
211    
212        def GetDefaultGroup(self):
213            """Return the default group."""
214            return self.__groups[0]
215    
216        def AppendGroup(self, item):
217            """Append a new ClassGroup item to the classification.
218    
219            item -- this must be a valid ClassGroup object
220            """
221    
222            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          self.__SendMessage(LAYER_LEGEND_CHANGED)      def FindGroup(self, value):
250            """Return the group that matches the value.
251    
252      def GetClassData(self, value):          Groups are effectively checked in the order the were added to
253          """Return the associated data, or the default data.          the Classification.
254    
255             The following search technique is used:          value -- the value to classify. If there is no mapping or value
256                 (1) if the field is None, return the default data                   is None, return the default properties
257                 (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:  
             #  
             # check the discrete values  
             #  
             if self.points.has_key(value):  
                 return self.points[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  
258    
259          return self.DefaultData          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):
273            """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)" %
294                      (text, color.red, color.green, color.blue),                      (text, color.red, color.green, color.blue),
295                      color)                      color)
296    
297          def build_item(data, string):          def build_item(group, string):
298              label = data.GetLabel()              label = group.GetLabel()
299              if label == "":              if label == "":
300                  label = string                  label = string
301              else:              else:
302                  label += " (%s)" % string                  label += " (%s)" % string
303    
304                props = group.GetProperties()
305              i = []              i = []
306              v = data.GetStroke()              v = props.GetLineColor()
307              i.append(build_color_item(_("Stroke"), v))              i.append(build_color_item(_("Line Color"), v))
308              v = data.GetStrokeWidth()              v = props.GetLineWidth()
309              i.append(_("Stroke Width: %s") % v)              i.append(_("Line Width: %s") % v)
310              v = data.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              type = p.GetType()              items.append(build_item(p, p.GetDisplayText()))
             if type == ClassData.DEFAULT:  
                 items.append(build_item(self.DefaultData, _("'DEFAULT'")))  
             elif type == ClassData.POINT:  
                 items.append(build_item(p, str(p.GetValue())))  
             elif type == ClassData.RANGE:  
                 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())))  
316    
317  #       for p in self.ranges:          return (_("Classification"), items)
 #           items.append(build_item(p, "%s - %s" % (p.GetMin(), p.GetMax())))  
318    
         return (_("Classifications"), items)  
   
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            default -- the default group
330    
331            points -- a list of singleton groups
332    
333      def __init__(self, default, points, ranges, maps):          ranges -- a list of range groups
334          self.data = [default, points, ranges, maps]  
335          self.data_iter = iter(self.data)          maps -- a list of map groups
336          self.iter = None          """
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."""
             try:  
                 self.data_item = self.data_iter.next()  
                 self.iter = iter(self.data_item)  
             except TypeError:  
                 return self.data_item  
346    
347          try:          if self.data_index >= len(self.data):
348              return self.iter.next()              raise StopIteration
         except StopIteration:  
             self.iter = None  
             return self.next()  
         
 class ClassData:  
   
     INVALID = -1  
     DEFAULT = 0  
     POINT = 1  
     RANGE = 2  
     MAP   = 3  
   
     def __init__(self, classData = None, type = INVALID):  
   
         if classData is not None:  
             self.SetStroke(classData.GetStroke())  
             self.SetStrokeWidth(classData.GetStrokeWidth())  
             self.SetFill(classData.GetFill())  
349          else:          else:
350              self.SetStroke(Color.None)              d = self.data[self.data_index]
351              self.SetStrokeWidth(1)              self.data_index += 1
352              self.SetFill(Color.None)              return d
353    
354          self.type = type  class ClassGroupProperties:
355          self.label = ""      """Represents the properties of a single Classification Group.
       
     def GetType(self):  
         return self.type  
356    
357      def GetStroke(self):      These are used when rendering a layer."""
         return self.stroke  
358    
359      def SetStroke(self, stroke):      # TODO: Actually, size is only relevant for point objects.
360          assert(isinstance(stroke, Color))      # Eventually it should be spearated, e.g. when introducing symbols.
         self.stroke = stroke  
361    
362      def GetStrokeWidth(self):      def __init__(self, props = None):
363          return self.stroke_width          """Constructor.
364    
365      def SetStrokeWidth(self, stroke_width):          props -- a ClassGroupProperties object. The class is copied if
366          if (stroke_width < 1):                   prop is not None. Otherwise, a default set of properties
367              raise ValueError(_("stroke_width < 1"))                   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:
374                self.SetLineColor(Black)
375                self.SetLineWidth(1)
376                self.SetSize(5)
377                self.SetFill(Transparent)
378    
379        def SetProperties(self, props):
380            """Set this class's properties to those in class props."""
381    
382            assert isinstance(props, ClassGroupProperties)
383            self.SetLineColor(props.GetLineColor())
384            self.SetLineWidth(props.GetLineWidth())
385            self.SetSize(props.GetSize())
386            self.SetFill(props.GetFill())
387    
388        def GetLineColor(self):
389            """Return the line color as a Color object."""
390            return self.__stroke
391    
392          self.stroke_width = stroke_width      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        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:
471        """A base class for all Groups within a Classification"""
472    
473        def __init__(self, label = "", props = None, group = None):
474            """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  class ClassDataDefault(ClassData):      def GetDisplayText(self):
502      def __init__(self, classData = None):          assert False, "GetDisplay must be overridden by subclass!"
503          ClassData.__init__(self, classData, ClassData.DEFAULT)          return ""
504    
505        def Matches(self, value):
506            """Determines if this Group is associated with the given value.
507    
508            Returns False. This needs to be overridden by all subclasses.
509            """
510            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 ClassDataPoint(ClassData):  class ClassGroupSingleton(ClassGroup):
547        """A Group that is associated with a single value."""
548    
549        def __init__(self, value = 0, props = None, label = "", group = None):
550            """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)
562    
563      def __init__(self, value = 0, classData = None):      def __copy__(self):
564          ClassData.__init__(self, classData, ClassData.POINT)          return ClassGroupSingleton(self.GetValue(),
565                                       self.GetProperties(),
566                                       self.GetLabel())
567    
568          self.value = value      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):
580            """Determine if the given value matches the associated Group value."""
581    
582            """Returns True if the value matches, False otherwise."""
583    
584            return self.__value == value
585    
586        def GetDisplayText(self):
587            label = self.GetLabel()
588    
589            if label != "": return label
590    
591            return str(self.GetValue())
592    
593        def __eq__(self, other):
594            return ClassGroup.__eq__(self, other) \
595                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  class ClassDataRange(ClassData):      def __init__(self, props = None, label = "", group = None):
607            """Constructor.
608    
609      def __init__(self, min = 0, max = 1, classData = None):          prop -- a ClassGroupProperites object. If prop is None a default
610          ClassData.__init__(self, classData, ClassData.RANGE)                   set of properties is created.
611    
612          if min >= max:          label -- a label for this group.
613              raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %          """
614                               (min, max))  
615            ClassGroup.__init__(self, label, props, group)
616    
617        def __copy__(self):
618            return ClassGroupDefault(self.GetProperties(), self.GetLabel())
619    
620        def __deepcopy__(self, memo):
621            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          self.SetRange(min, max)  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, _range = (0,1), props = None, label = "", group=None):
646            """Constructor.
647    
648            The minumum value must be strictly less than the maximum.
649    
650            _range -- either a tuple (min, max) where min < max or
651                      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):
663            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      def SetRange(self, min, max):          _range -- Either a tuple (min, max) where min < max or
700          self.min = min                    a Range object.
701          self.max = max  
702          if min >= max:          Raises ValueError on error.
703              raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %          """
704                               (min, max))  
705            if isinstance(_range, Range):
706                self.__range = _range
707            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):
720            """Determine if the given value lies with the current range.
721    
722            The following check is used: min <= value < max.
723            """
724    
725            return operator.contains(self.__range, value)
726    
727        def GetDisplayText(self):
728            label = self.GetLabel()
729    
730            if label != "": return label
731    
732            return self.__range.string(self.__range.GetRange())
733    
734      def InRange(self, value):      def __eq__(self, other):
735          return self.min <= value < self.max          return ClassGroup.__eq__(self, other) \
736                and isinstance(other, ClassGroupRange) \
737                and self.__range == other.__range
738    
739  class ClassDataMap(ClassData):      def __repr__(self):
740            return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"
741    
742    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, classData = None):      def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
748          ClassData.__init__(self, classData, ClassData.MAP)          ClassGroup.__init__(self, label)
749    
750          self.map_type = map_type          self.map_type = map_type
751          self.func = func          self.func = func
# Line 369  class ClassDataMap(ClassData): Line 756  class ClassDataMap(ClassData):
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.428  
changed lines
  Added in v.2374

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26