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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26