/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Diff of /trunk/thuban/Thuban/UI/classifier.py

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

revision 430 by jonathan, Mon Feb 24 18:47:06 2003 UTC revision 615 by jonathan, Mon Apr 7 08:57:20 2003 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 11  __version__ = "$Revision$" Line 11  __version__ = "$Revision$"
11    
12  import copy  import copy
13    
14    from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
15         FIELDTYPE_STRING
16    
17  from wxPython.wx import *  from wxPython.wx import *
18  from wxPython.grid import *  from wxPython.grid import *
19    
20  from Thuban import _  from Thuban import _
21  from Thuban.common import *  from Thuban.common import *
22    from Thuban.UI.common import *
23    
24  from Thuban.Model.classification import * #Classification, ClassData  from Thuban.Model.classification import *
25    
26  from Thuban.Model.color import Color  from Thuban.Model.color import Color
27    
28  from Thuban.Model.layer import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
29    
30    from Thuban.UI.classgen import ClassGenDialog, ClassGenerator
31    
32    from dialogs import NonModalDialog
33    
34    # widget id's
35  ID_PROPERTY_SELECT = 4010  ID_PROPERTY_SELECT = 4010
36  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
37    
38  ID_CLASSIFY_OK = 4001  ID_CLASSIFY_OK = 4001
39  ID_CLASSIFY_CANCEL = 4002  ID_CLASSIFY_CANCEL = 4002
40  ID_CLASSIFY_ADD = 4003  ID_CLASSIFY_ADD = 4003
41  ID_CLASSIFY_GENRANGE = 4004  ID_CLASSIFY_GENCLASS = 4004
42    ID_CLASSIFY_REMOVE = 4005
43    ID_CLASSIFY_MOVEUP = 4006
44    ID_CLASSIFY_MOVEDOWN = 4007
45    ID_CLASSIFY_APPLY = 4008
46    ID_CLASSIFY_EDITPROPS = 4009
47    ID_CLASSIFY_CLOSE = 4010
48    
49  COL_VISUAL = 0  # table columns
50    COL_SYMBOL = 0
51  COL_VALUE  = 1  COL_VALUE  = 1
52  COL_LABEL  = 2  COL_LABEL  = 2
53    
54    # indices into the client data lists in Classifier.fields
55    FIELD_CLASS = 0
56    FIELD_TYPE = 1
57    FIELD_NAME = 2
58    
59  #  #
60  # this is a silly work around to ensure that the table that is  # this is a silly work around to ensure that the table that is
61  # passed into SetTable is the same that is returned by GetTable  # passed into SetTable is the same that is returned by GetTable
# Line 42  COL_LABEL  = 2 Line 63  COL_LABEL  = 2
63  import weakref  import weakref
64  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
65    
66      def __init__(self, parent, layer):  
67          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (300, 150))      def __init__(self, parent, classifier):
68          self.SetTable(          """Constructor.
69              ClassTable(layer.GetClassification(), layer.ShapeType(), self),  
70              true)          parent -- the parent window
71    
72            clazz -- the working classification that this grid should
73                     use for display.
74            """
75    
76            #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
77            wxGrid.__init__(self, parent, ID_CLASS_TABLE)
78            #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)
79    
80            self.classifier = classifier
81    
82            self.currentSelection = []
83    
84            EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
85            EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
86            EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
87            EVT_GRID_COL_SIZE(self, self._OnCellResize)
88            EVT_GRID_ROW_SIZE(self, self._OnCellResize)
89    
90            #print "123123123: ", ('Show' in dir(self))
91    
92        #def Show(self):
93            #print "SHOW!"
94    
95        #def Refresh(self):
96            #self.Show()
97        #def Update(self):
98            #self.Show()
99    
100        def CreateTable(self, clazz, shapeType, group = None):
101    
102            assert isinstance(clazz, Classification)
103    
104            table = self.GetTable()
105            if table is None:
106                w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()
107                h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()
108                self.SetDimensions(-1, -1, w, h)
109                self.SetSizeHints(w, h, -1, -1)
110                table = ClassTable(self)
111                self.SetTable(table, True)
112    
113    
114            self.SetSelectionMode(wxGrid.wxGridSelectRows)
115            self.ClearSelection()
116    
117            #print "8------------------"
118            table.Reset(clazz, shapeType, group)
119            #print "9------------------"
120    
121    #   def Show(self, show = True):
122    #       print "SHOW!"
123    #       wxGrid.Show(self, show)
124    
125    #       sel = self.GetCurrentSelection()
126    
127    #       print "( 1"
128    #       if len(sel) == 1:
129    #           print "( 2"
130    #           self.MakeCellVisible(sel[0], 0)
131    
132        def GetCurrentSelection(self):
133            """Return the currently highlighted rows as an increasing list
134               of row numbers."""
135            #print "@@ ", self.currentSelection
136            sel = copy.copy(self.currentSelection)
137            sel.sort()
138            return sel
139    
140        def GetSelectedRows(self):
141            return self.GetCurrentSelection()
142    
143      def SetCellRenderer(self, row, col):      def SetCellRenderer(self, row, col):
144          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
145    
146        #
147        # [Set|Get]Table is taken from http://wiki.wxpython.org
148        # they are needed as a work around to ensure that the table
149        # that is passed to SetTable is the one that is returned
150        # by GetTable.
151        #
152      def SetTable(self, object, *attributes):      def SetTable(self, object, *attributes):
153          self.tableRef = weakref.ref(object)          self.tableRef = weakref.ref(object)
154          return wxGrid.SetTable(self, object, *attributes)          return wxGrid.SetTable(self, object, *attributes)
155    
156      def GetTable(self):      def GetTable(self):
157          return self.tableRef()          try:
158                return self.tableRef()
159            except:
160                return None
161    
162        def DeleteSelectedRows(self):
163            """Deletes all highlighted rows.
164      
165            If only one row is highlighted then after it is deleted the
166            row that was below the deleted row is highlighted."""
167    
168            sel = self.GetCurrentSelection()
169    
170            # nothing to do
171            if len(sel) == 0: return
172    
173            # if only one thing is selected check if it is the default
174            # data row, because we can't remove that
175            if len(sel) == 1:
176                #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
177                group = self.GetTable().GetClassGroup(sel[0])
178                if isinstance(group, ClassGroupDefault):
179                    wxMessageDialog(self,
180                                    "The Default group cannot be removed.",
181                                    style = wxOK | wxICON_EXCLAMATION).ShowModal()
182                    return
183            
184    
185            self.ClearSelection()
186    
187            # we need to remove things from the bottom up so we don't
188            # change the indexes of rows that will be deleted next
189            sel.reverse()
190    
191            #
192            # actually remove the rows
193            #
194            table = self.GetTable()
195            for row in sel:
196                table.DeleteRows(row)
197    
198            #
199            # if there was only one row selected highlight the row
200            # that was directly below it, or move up one if the
201            # deleted row was the last row.
202            #
203            if len(sel) == 1:
204                r = sel[0]
205                if r > self.GetNumberRows() - 1:
206                    r = self.GetNumberRows() - 1
207                self.SelectRow(r)
208            
209    
210        def SelectGroup(self, group, makeVisible = True):
211            if group is None: return
212    
213            assert isinstance(group, ClassGroup)
214    
215            table = self.GetTable()
216    
217            assert table is not None
218    
219    
220            #print "-- ", group
221            for i in range(table.GetNumberRows()):
222                g = table.GetClassGroup(i)
223                #print "1", g
224                if g is group:
225                    #print "2"
226                    self.SelectRow(i)
227                    if makeVisible:
228                        #print "3"
229                        self.MakeCellVisible(i, 0)
230                    break
231    
232            
233    
234    #
235    # XXX: This isn't working, and there is no way to deselect rows wxPython!
236    #
237    #   def DeselectRow(self, row):
238    #       self.ProcessEvent(
239    #           wxGridRangeSelectEvent(-1,
240    #                                  wxEVT_GRID_RANGE_SELECT,
241    #                                  self,
242    #                                  (row, row), (row, row),
243    #                                  sel = False))
244    
245        def _OnCellDClick(self, event):
246            """Handle a double on a cell."""
247    
248            r = event.GetRow()
249            c = event.GetCol()
250            if c == COL_SYMBOL:
251                self.classifier.EditGroupProperties(r)
252    
253    
254        #
255        # _OnSelectedRange() and _OnSelectedCell() were borrowed
256        # from http://wiki.wxpython.org to keep track of which
257        # cells are currently highlighted
258        #
259        def _OnSelectedRange(self, event):
260            """Internal update to the selection tracking list"""
261            if event.Selecting():
262                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
263                    if index not in self.currentSelection:
264                        #print "    ", index
265                        self.currentSelection.append( index )
266            else:
267                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
268                    while index in self.currentSelection:
269                        #print "    ", index
270                        self.currentSelection.remove( index )
271            #self.ConfigureForSelection()
272    
273            #print self.GetCurrentSelection()
274            event.Skip()
275    
276        def _OnSelectedCell( self, event ):
277            """Internal update to the selection tracking list"""
278            #print "selecting cell: ", event.GetRow()
279            self.currentSelection = [ event.GetRow() ]
280            #self.ConfigureForSelection()
281            event.Skip()
282    
283        def _OnCellResize(self, event):
284            self.FitInside()
285    
286  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
287        """Represents the underlying data structure for the grid."""
288    
289      NUM_COLS = 3      NUM_COLS = 3
290    
291      __col_labels = [_("Visual"), _("Value"), _("Label")]      __col_labels = [_("Symbol"), _("Value"), _("Label")]
292    
     # this is tied to the values of classification.ClassData  
     __row_labels = [_("Default"), _("Point"), _("Range"), _("Map")]  
293    
294      def __init__(self, clazz, shapeType, view = None):      def __init__(self, view = None):
295        #def __init__(self, clazz, shapeType, view = None):
296            """Constructor.
297    
298            shapeType -- the type of shape that the layer uses
299    
300            view -- a wxGrid object that uses this class for its table
301            """
302    
303          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
304    
305          self.SetView(view)          self.SetView(view)
306          self.tdata = []          self.clazz = None
307    
308            #self.Reset(clazz, shapeType)
309    
310        def Reset(self, clazz, shapeType, group = None):
311            """Reset the table with the given data.
312    
313          self.Reset(clazz, shapeType)          This is necessary because wxWindows does not allow a grid's
314            table to change once it has been intially set and so we
315            need a way of modifying the data.
316    
317      def Reset(self, clazz, shapeType):          clazz -- the working classification that this table should
318                     use for display. This may be different from the
319                     classification in the layer.
320    
321            shapeType -- the type of shape that the layer uses
322            """
323    
324            assert isinstance(clazz, Classification)
325    
326          self.GetView().BeginBatch()          self.GetView().BeginBatch()
327    
328            self.fieldType = clazz.GetFieldType()
329          self.shapeType = shapeType          self.shapeType = shapeType
         self.renderer = ClassRenderer(self.shapeType)  
330    
331          old_tdata = self.tdata          self.SetClassification(clazz, group)
332            self.__Modified(-1)
333            #print "11------------------"
334    
335            self.GetView().EndBatch()
336            self.GetView().FitInside()
337    
338          self.tdata = []      def GetClassification(self):
339            return self.clazz
340    
341          if clazz is None:      def SetClassification(self, clazz, group = None):
342              clazz = Classification()  
343            self.GetView().BeginBatch()
344    
345            old_len = self.GetNumberRows()
346    
347            #print "9------------------"
348            #
349            # copy the data out of the classification and into our
350            # array
351            #
352            row = -1
353    #       for g in clazz:
354    #           ng = copy.deepcopy(g)
355    #           self.__SetRow(None, ng)
356    #           if g is group:
357    #               row = self.GetNumberRows() - 1
358    #               #print "selecting row..."
359    
360            #print "10------------------"
361    
362            #self.clazz = copy.deepcopy(clazz)
363            self.clazz = clazz
364    
365            self.__NotifyRowChanges(old_len, self.GetNumberRows())
366    
367          p = clazz.GetDefaultData()          if row > -1:
368          np = ClassDataDefault(classData = p)              self.GetView().ClearSelection()
369          self.tdata.append([np, 'DEFAULT', np.GetLabel()])              self.GetView().SelectRow(row)
370                self.GetView().MakeCellVisible(row, 0)
         for p in clazz.points.values():  
             np = ClassDataPoint(p.GetValue(), classData = p)  
             self.tdata.append([np, np.GetValue(), np.GetLabel()])  
   
         for p in clazz.ranges:  
             np = ClassDataRange(p.GetMin(), p.GetMax(), classData = p)  
             self.tdata.append([np,  
                                '%s - %s' % (np.GetMin(), np.GetMax()),  
                                np.GetLabel()])  
371    
372          self.modified = 0          self.__Modified()
373    
374            self.GetView().EndBatch()
375            self.GetView().FitInside()
376    
377        def __NotifyRowChanges(self, curRows, newRows):
378          #          #
379          # silly message processing for updates to the number of          # silly message processing for updates to the number of
380          # rows and columns          # rows and columns
381          #          #
         curRows = len(old_tdata)  
         newRows = len(self.tdata)  
382          if newRows > curRows:          if newRows > curRows:
383              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
384                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
385                          newRows - curRows)    # how many                          newRows - curRows)    # how many
386              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
387                self.GetView().FitInside()
388          elif newRows < curRows:          elif newRows < curRows:
389              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
390                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,
391                          curRows - newRows,    # position                          curRows,              # position
392                          curRows - newRows)    # how many                          curRows - newRows)    # how many
393              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
394                self.GetView().FitInside()
395    
396          self.GetView().EndBatch()  
397        def __SetRow(self, row, group):
398            """Set a row's data to that of the group.
399    
400            The table is considered modified after this operation.
401    
402            row -- if row is < 0 'group' is inserted at the top of the table
403                   if row is >= GetNumberRows() or None 'group' is append to
404                        the end of the table.
405                   otherwise 'group' replaces row 'row'
406            """
407    
408            # either append or replace
409            if row is None or row >= self.GetNumberRows():
410                self.clazz.AppendGroup(group)
411            elif row < 0:
412                self.clazz.InsertGroup(0, group)
413            else:
414                if row == 0:
415                    self.clazz.SetDefaultGroup(group)
416                else:
417                    self.clazz.ReplaceGroup(row - 1, group)
418    
419            self.__Modified()
420    
421      def GetColLabelValue(self, col):      def GetColLabelValue(self, col):
422            """Return the label for the given column."""
423          return self.__col_labels[col]          return self.__col_labels[col]
424    
425      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
426          data = self.tdata[row][COL_VISUAL]          """Return the label for the given row."""
427          type = data.GetType()  
428          return self.__row_labels[type]          if row == 0:
429                return _("Default")
430            else:
431                group = self.clazz.GetGroup(row - 1)
432                if isinstance(group, ClassGroupDefault):   return _("Default")
433                if isinstance(group, ClassGroupSingleton): return _("Singleton")
434                if isinstance(group, ClassGroupRange):     return _("Range")
435                if isinstance(group, ClassGroupMap):       return _("Map")
436    
437            assert False # shouldn't get here
438            return _("")
439    
440      def GetNumberRows(self):      def GetNumberRows(self):
441          return len(self.tdata)          """Return the number of rows."""
442            if self.clazz is None:
443                return 0
444    
445            return self.clazz.GetNumGroups() + 1 # +1 for default group
446    
447      def GetNumberCols(self):      def GetNumberCols(self):
448            """Return the number of columns."""
449          return self.NUM_COLS          return self.NUM_COLS
450    
451      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
452          return 0          """Determine if a cell is empty. This is always false."""
453            return False
454    
455      def GetValue(self, row, col):      def GetValue(self, row, col):
456          return self.GetValueAsCustom(row, col, "")          """Return the object that is used to represent the given
457               cell coordinates. This may not be a string."""
458            return self.GetValueAsCustom(row, col, None)
459    
460      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
461          self.SetValueAsCustom(row, col, "", value)          """Assign 'value' to the cell specified by 'row' and 'col'.
462    
463            The table is considered modified after this operation.
464            """
465    
466            self.SetValueAsCustom(row, col, None, value)
467          self.__Modified()          self.__Modified()
468                
469      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
470          return self.tdata[row][col]          """Return the object that is used to represent the given
471               cell coordinates. This may not be a string.
472    
473            typeName -- unused, but needed to overload wxPyGridTableBase
474            """
475    
476            if row == 0:
477                group = self.clazz.GetDefaultGroup()
478            else:
479                group = self.clazz.GetGroup(row - 1)
480    
481    
482            if col == COL_SYMBOL:
483                return group.GetProperties()
484    
485            if col == COL_LABEL:
486                return group.GetLabel()
487    
488            # col must be COL_VALUE
489            assert col == COL_VALUE
490    
491            if isinstance(group, ClassGroupDefault):
492                return _("DEFAULT")
493            elif isinstance(group, ClassGroupSingleton):
494                return group.GetValue()
495            elif isinstance(group, ClassGroupRange):
496                return _("%s - %s") % (group.GetMin(), group.GetMax())
497    
498            assert(False) # shouldn't get here
499            return None
500    
501      def __ParseInput(self, value):      def __ParseInput(self, value):
502          """Try to determine what kind of input value is          """Try to determine what kind of input value is
503             (a single number or a range)             (string, number, or range)
504    
505            Returns a tuple of length one if there is a single
506            value, or of length two if it is a range.
507          """          """
508    
509          #          type = self.fieldType
510          # first try to take the input as a single number  
511          # if there's an exception try to break it into          if type == FIELDTYPE_STRING:
512          # a range seperated by a '-'. take care to ignore              return (value,)
513          # a leading '-' as that could be for a negative number.          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:
514          # then try to parse the individual parts. if there  
515          # is an exception here, let it pass up to the calling              if type == FIELDTYPE_INT:
516          # function.                  conv = lambda p: int(float(p))
517          #              else:
518          try:                  conv = lambda p: p
519              return (ClassData.POINT, Str2Num(value))  
520          except:              #
521              i = value.find('-')              # first try to take the input as a single number
522              if i == 0:              # if there's an exception try to break it into
523                  i = value.find('-', 1)              # a range seperated by a '-'. take care to ignore
524                # a leading '-' as that could be for a negative number.
525              return (ClassData.RANGE,              # then try to parse the individual parts. if there
526                      Str2Num(value[:i]),              # is an exception here, let it pass up to the calling
527                      Str2Num(value[i+1:]))              # function.
528                #
529                try:
530                    return (conv(Str2Num(value)),)
531                except ValueError:
532                    i = value.find('-')
533                    if i == 0:
534                        i = value.find('-', 1)
535    
536                    return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
537    
538            assert False  # shouldn't get here
539            return (0,)
540                            
541    
542      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
543          data = self.tdata[row][COL_VISUAL]          """Set the cell specified by 'row' and 'col' to 'value'.
544    
545          if col == COL_VISUAL:          If column represents the value column, the input is parsed
546              self.tdata[row][COL_VISUAL] = value          to determine if a string, number, or range was entered.
547          elif col == COL_VALUE:          A new ClassGroup may be created if the type of data changes.
             if row != 0: # DefaultData row  
                 type = data.GetType()  
548    
549                  if type == ClassData.MAP:          The table is considered modified after this operation.
                     # something special  
                     pass  
                 else: # POINT, RANGE  
                     try:  
                         dataInfo = self.__ParseInput(value)  
                     except: pass  
                         # bad input, ignore the request  
                     else:  
550    
551                          if dataInfo[0] == ClassData.POINT:          typeName -- unused, but needed to overload wxPyGridTableBase
552                              if type != ClassData.POINT:          """
                                 data = ClassDataPoint(classData = data)  
                             data.SetValue(dataInfo[1])  
                             self.tdata[row][COL_VALUE] = data.GetValue()  
                         elif dataInfo[0] == ClassData.RANGE:  
                             if type != ClassData.RANGE:  
                                 data = ClassDataRange(classData = data)  
                             data.SetRange(dataInfo[1], dataInfo[2])  
                             self.tdata[row][COL_VALUE] = \  
                                 "%s - %s" % (data.GetMin(), data.GetMax())  
553    
554                          self.tdata[row][COL_VISUAL] = data          assert col >= 0 and col < self.GetNumberCols()
555            assert row >= 0 and row < self.GetNumberRows()
556    
557                          self.GetView().Refresh()          if row == 0:
558                group = self.clazz.GetDefaultGroup()
559            else:
560                group = self.clazz.GetGroup(row - 1)
561    
562            mod = True # assume the data will change
563    
564            if col == COL_SYMBOL:
565                group.SetProperties(value)
566          elif col == COL_LABEL:          elif col == COL_LABEL:
567              data.SetLabel(value)              group.SetLabel(value)
568              self.tdata[row][COL_LABEL] = data.GetLabel()          elif col == COL_VALUE:
569                if isinstance(group, ClassGroupDefault):
570                    # not allowed to modify the default value
571                    pass
572                elif isinstance(group, ClassGroupMap):
573                    # something special
574                    pass
575                else: # SINGLETON, RANGE
576                    try:
577                        dataInfo = self.__ParseInput(value)
578                    except ValueError:
579                        # bad input, ignore the request
580                        mod = False
581                    else:
582    
583                        changed = False
584                        ngroup = group
585                        props = group.GetProperties()
586    
587                        #
588                        # try to update the values, which may include
589                        # changing the underlying group type if the
590                        # group was a singleton and a range was entered
591                        #
592                        if len(dataInfo) == 1:
593                            if not isinstance(group, ClassGroupSingleton):
594                                ngroup = ClassGroupSingleton(prop = props)
595                                changed = True
596                            ngroup.SetValue(dataInfo[0])
597                        elif len(dataInfo) == 2:
598                            if not isinstance(group, ClassGroupRange):
599                                ngroup = ClassGroupRange(prop = props)
600                                changed = True
601                            ngroup.SetRange(dataInfo[0], dataInfo[1])
602                        else:
603                            assert False
604                            pass
605    
606                        if changed:
607                            ngroup.SetLabel(group.GetLabel())
608                            self.SetClassGroup(row, ngroup)
609          else:          else:
610              raise ValueError(_("Invalid column request"))              assert False # shouldn't be here
611                pass
612    
613          self.__Modified()          if mod:
614                self.__Modified()
615                self.GetView().Refresh()
616    
617      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
618            """Returns the cell attributes"""
619    
620          attr = wxGridCellAttr()          attr = wxGridCellAttr()
621          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)
622    
623          if col == COL_VISUAL:          if col == COL_SYMBOL:
624                # we need to create a new renderer each time, because
625                # SetRenderer takes control of the parameter
626              attr.SetRenderer(ClassRenderer(self.shapeType))              attr.SetRenderer(ClassRenderer(self.shapeType))
627              attr.SetReadOnly()              attr.SetReadOnly()
628    
629          return attr          return attr
630    
631      def GetClassData(self, row):      def GetClassGroup(self, row):
632          return self.tdata[row][COL_VISUAL]          """Return the ClassGroup object representing row 'row'."""
633    
634      def __Modified(self):          #return self.GetValueAsCustom(row, COL_SYMBOL, None)
635          self.modified = 1          if row == 0:
636                return self.clazz.GetDefaultGroup()
637            else:
638                return self.clazz.GetGroup(row - 1)
639    
640        def SetClassGroup(self, row, group):
641            self.__SetRow(row, group)
642            self.GetView().Refresh()
643    
644        def __Modified(self, mod = True):
645            """Adjust the modified flag.
646    
647            mod -- if -1 set the modified flag to False, otherwise perform
648                   an 'or' operation with the current value of the flag and
649                   'mod'
650            """
651    
652            if mod == -1:
653                self.modified = False
654            else:
655                self.modified = mod or self.modified
656    
657      def IsModified(self):      def IsModified(self):
658            """True if this table is considered modified."""
659          return self.modified          return self.modified
660    
661      def AddNewDataRow(self):      def DeleteRows(self, pos, numRows = 1):
662          np = ClassDataPoint()          """Deletes 'numRows' beginning at row 'pos'.
663          self.tdata.append([np, np.GetValue(), np.GetLabel()])  
664          msg = wxGridTableMessage(self, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1)          The row representing the default group is not removed.
665          self.GetView().ProcessTableMessage(msg)  
666          self.GetView().Refresh()          The table is considered modified if any rows are removed.
667            """
668    
669  class Classifier(wxDialog):          assert pos >= 0
670            old_len = self.GetNumberRows()
671            for row in range(pos, pos - numRows, -1):
672                group = self.GetClassGroup(row)
673                if row != 0:
674                    self.clazz.RemoveGroup(row - 1)
675                    self.__Modified()
676            
677      def __init__(self, parent, layer):          if self.IsModified():
678          wxDialog.__init__(self, parent, -1, _("Classify"),              self.__NotifyRowChanges(old_len, self.GetNumberRows())
679                            style = wxRESIZE_BORDER)  
680        def AppendRows(self, numRows = 1):
681            """Append 'numRows' empty rows to the end of the table.
682    
683            The table is considered modified if any rows are appended.
684            """
685    
686            old_len = self.GetNumberRows()
687            for i in range(numRows):
688                np = ClassGroupSingleton()
689                self.__SetRow(None, np)
690    
691            if self.IsModified():
692                self.__NotifyRowChanges(old_len, self.GetNumberRows())
693    
694    
695    class Classifier(NonModalDialog):
696    
697        def __init__(self, parent, name, layer, group = None):
698            NonModalDialog.__init__(self, parent, name,
699                                    _("Classifier: %s") % layer.Title())
700    
701            panel = wxPanel(self, -1, size=(100, 100))
702    
703          self.layer = layer          self.layer = layer
704    
705            self.originalClass = self.layer.GetClassification()
706            field = self.originalClass.GetField()
707            fieldType = self.originalClass.GetFieldType()
708    
709          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
710            panelBox = wxBoxSizer(wxVERTICAL)
711    
712          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),          #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),
713              0, wxALIGN_LEFT | wxBOTTOM, 4)              #0, wxALIGN_LEFT | wxALL, 4)
714          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),          panelBox.Add(wxStaticText(panel, -1,
715              0, wxALIGN_LEFT | wxBOTTOM, 4)                                  _("Layer Type: %s") % layer.ShapeType()),
716                0, wxALIGN_LEFT | wxALL, 4)
717    
         propertyBox = wxBoxSizer(wxHORIZONTAL)  
         propertyBox.Add(wxStaticText(self, -1, _("Property: ")),  
             0, wxALIGN_CENTER | wxALL, 4)  
718    
719          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          #
720            # make field combo box
721            #
722            self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
723                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
724    
725          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
726          self.__cur_prop = -1          # just assume the first field in case one hasn't been
727          field = layer.GetClassification().GetField()          # specified in the file.
728            self.__cur_field = 0
729    
730            self.fields.Append("<None>")
731            self.fields.SetClientData(0, None)
732    
733          for i in range(self.num_cols):          for i in range(self.num_cols):
734              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
735                self.fields.Append(name)
736    
737              if name == field:              if name == field:
738                  self.__cur_prop = i                  self.__cur_field = i + 1
739              self.properties.Append(name)                  self.fields.SetClientData(i + 1,
740              self.properties.SetClientData(i, None)                                            copy.deepcopy(self.originalClass))
741                else:
742          self.properties.SetSelection(self.__cur_prop)                  self.fields.SetClientData(i + 1, None)
743          propertyBox.Add(self.properties, 1, wxGROW|wxALL, 0)  
744          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)  
745            ###########
746    
747            self.fieldTypeText = wxStaticText(panel, -1, "")
748            panelBox.Add(self.fieldTypeText, 0,
749                         wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
750    
751            propertyBox = wxBoxSizer(wxHORIZONTAL)
752            propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),
753                0, wxALIGN_LEFT | wxALL, 4)
754            propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
755            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
756    
757            panelBox.Add(propertyBox, 0, wxGROW, 4)
758    
         topBox.Add(propertyBox, 0, wxGROW, 4)  
759    
760          #          #
761          # Classification data table          # Control Box
762          #          #
   
763          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
         self.classGrid = ClassGrid(self, layer)  
764    
765          controlBox.Add(self.classGrid, 1, wxGROW, 0)  
766            ###########
767            #
768            # Control buttons:
769            #
770            self.controlButtons = []
771    
772          controlButtonBox = wxBoxSizer(wxVERTICAL)          controlButtonBox = wxBoxSizer(wxVERTICAL)
         controlButtonBox.Add(wxButton(self, ID_CLASSIFY_ADD,  
             _("Add")), 0, wxGROW | wxALL, 4)  
         controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,  
             _("Generate Ranges")), 0, wxGROW | wxALL, 4)  
773    
774          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))
775          topBox.Add(controlBox, 1, wxGROW, 10)          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
776            self.controlButtons.append(button)
777    
778            button = wxButton(panel, ID_CLASSIFY_EDITPROPS, _("Edit Properties"))
779            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
780            self.controlButtons.append(button)
781    
782            button = wxButton(panel, ID_CLASSIFY_GENCLASS, _("Generate Class"))
783            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
784            self.controlButtons.append(button)
785    
786            button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))
787            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
788            self.controlButtons.append(button)
789    
790            button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))
791            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
792            self.controlButtons.append(button)
793    
794            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
795    
796            button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))
797            controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
798            self.controlButtons.append(button)
799    
         EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)  
         EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)  
         EVT_GRID_CELL_LEFT_DCLICK(self.classGrid, self.OnCellDClick)  
800    
801            ###########
802          #          #
803          # Control buttons:          # Classification data table
804          #          #
805    
806            self.classGrid = ClassGrid(panel, self)
807            #self.__SetGridTable(self.__cur_field, group)
808            #self.fields.SetSelection(self.__cur_field)
809    
810            # calling __SelectField after creating the classGrid fills in the
811            # grid with the correct information
812            #print "2------------------"
813            self.fields.SetSelection(self.__cur_field)
814            self.__SelectField(self.__cur_field, group = group)
815    
816            #self.classGrid.SelectGroup(group)
817    
818            controlBox.Add(self.classGrid, 1, wxGROW, 0)
819    
820    
821    
822            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
823            panelBox.Add(controlBox, 1, wxGROW, 10)
824    
825            EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)
826            EVT_BUTTON(self, ID_CLASSIFY_EDITPROPS, self._OnEditGroupProperties)
827            EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)
828            EVT_BUTTON(self, ID_CLASSIFY_GENCLASS, self._OnGenClass)
829            EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)
830            EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)
831    
832            ###########
833    
834          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
835          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),
836                        0, wxALL, 4)                        0, wxALL, 4)
837          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(60, 20, 0, wxALL, 4)
838            buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),
839                        0, wxALL, 4)                        0, wxALL, 4)
840          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          buttonBox.Add(60, 20, 0, wxALL, 4)
841            buttonBox.Add(wxButton(panel, ID_CLASSIFY_CLOSE, _("Close")),
842                          0, wxALL, 4)
843            buttonBox.Add(60, 20, 0, wxALL, 4)
844            buttonBox.Add(wxButton(panel, ID_CLASSIFY_CANCEL, _("Cancel")),
845                          0, wxALL, 4)
846            panelBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
847    
848            EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)
849            EVT_BUTTON(self, ID_CLASSIFY_APPLY, self._OnApply)
850            EVT_BUTTON(self, ID_CLASSIFY_CLOSE, self._OnCloseBtn)
851            EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)
852    
853            ###########
854    
855    
856          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          panel.SetAutoLayout(True)
857          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          panel.SetSizer(panelBox)
858            panelBox.SetSizeHints(panel)
859    
860          self.SetAutoLayout(true)          topBox.Add(panel, 1, wxGROW, 0)
861            panelBox.SetSizeHints(self)
862            self.SetAutoLayout(True)
863          self.SetSizer(topBox)          self.SetSizer(topBox)
         topBox.Fit(self)  
         topBox.SetSizeHints(self)  
864    
865      def __BuildClassification(self, prop):          #print "1------------------"
866            #self.Fit()
867            ######################
868    
869          clazz = Classification()          self.haveApplied = False
         clazz.SetField(self.properties.GetStringSelection())  
870    
871          numRows = self.classGrid.GetNumberRows()      def EditGroupProperties(self, row):
872            table = self.classGrid.GetTable()
873            prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
874    
875            # get a new ClassGroupProperties object and copy the
876            # values over to our current object
877            propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
878            if propDlg.ShowModal() == wxID_OK:
879                new_prop = propDlg.GetClassGroupProperties()
880                table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
881            propDlg.Destroy()
882            
883    
884        def __BuildClassification(self, fieldIndex, copyClass = False):
885    
886    #       numRows = self.classGrid.GetNumberRows()
887    #       assert numRows > 0  # there should always be a default row
888    
889    #       clazz = Classification()
890            if fieldIndex == 0:
891                fieldName = None
892                fieldType = None
893            else:
894                fieldName = self.fields.GetString(fieldIndex)
895                fieldType = self.layer.GetFieldType(fieldName)
896    
897            clazz = self.classGrid.GetTable().GetClassification()
898    
899            if copyClass:
900                clazz = copy.deepcopy(clazz)
901    
902            clazz.SetField(fieldName)
903            clazz.SetFieldType(fieldType)
904    
         if numRows > 0:  
             table = self.classGrid.GetTable()  
             clazz.SetDefaultData(table.GetClassData(0))  
905    
906              for i in range(1, numRows):  #       table = self.classGrid.GetTable()
907                  clazz.AddClassData(table.GetClassData(i))  #       clazz.SetDefaultGroup(table.GetClassGroup(0))
908    
909    #       for i in range(1, numRows):
910    #           clazz.AppendGroup(table.GetClassGroup(i))
911    
912          return clazz          return clazz
913    
914      def OnPropertySelect(self, event):      def __SetGridTable(self, fieldIndex, group = None):
         self.properties.SetClientData(  
             self.__cur_prop, self.__BuildClassification(self.__cur_prop))  
915    
916          self.__cur_prop = self.properties.GetSelection()          clazz = self.fields.GetClientData(fieldIndex)
917          clazz = self.properties.GetClientData(self.__cur_prop)  
918          table = self.classGrid.GetTable()          if clazz is None:
919                clazz = Classification()
920                clazz.SetDefaultGroup(
921                    ClassGroupDefault(
922                        self.layer.GetClassification().
923                                   GetDefaultGroup().GetProperties()))
924    
925                fieldName = self.fields.GetString(fieldIndex)
926                fieldType = self.layer.GetFieldType(fieldName)
927                clazz.SetFieldType(fieldType)
928                    
929            #print "6------------------"
930            self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
931            #print "7------------------"
932    
933    
934    
935        type2string = {None:             _("None"),
936                       FIELDTYPE_STRING: _("Text"),
937                       FIELDTYPE_INT:    _("Integer"),
938                       FIELDTYPE_DOUBLE: _("Decimal")}
939    
940        def __SetFieldTypeText(self, fieldIndex):
941            fieldName = self.fields.GetString(fieldIndex)
942            fieldType = self.layer.GetFieldType(fieldName)
943    
944            assert Classifier.type2string.has_key(fieldType)
945    
946            text = Classifier.type2string[fieldType]
947    
948            self.fieldTypeText.SetLabel(_("Field Type: %s") % text)
949    
950        def __SelectField(self, newIndex, oldIndex = -1, group = None):
951            """This method assumes that the current selection for the
952            combo has already been set by a call to SetSelection().
953            """
954    
955          table.Reset(clazz, self.layer.ShapeType())          #print "3------------------"
956    
957      def OnOK(self, event):          assert oldIndex >= -1
958    
959            if oldIndex != -1:
960                clazz = self.__BuildClassification(oldIndex)
961                self.fields.SetClientData(oldIndex, clazz)
962    
963            #print "4------------------"
964            self.__SetGridTable(newIndex, group)
965            #print "5------------------"
966    
967            enabled = newIndex != 0
968    
969            for b in self.controlButtons:
970                b.Enable(enabled)
971    
972            self.__SetFieldTypeText(newIndex)
973    
974    
975        def _OnEditGroupProperties(self, event):
976            sel = self.classGrid.GetCurrentSelection()
977    
978            if len(sel) == 1:
979                self.EditGroupProperties(sel[0])
980    
981        def _OnFieldSelect(self, event):
982            index = self.fields.GetSelection()
983            self.__SelectField(index, self.__cur_field)
984            self.__cur_field = index
985    
986        def _OnApply(self, event):
987          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
988             it to the layer.             it to the layer.
989          """          """
990    
991          clazz = self.properties.GetClientData(self.__cur_prop)          clazz = self.fields.GetClientData(self.__cur_field)
992    
993          #          #
994          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
995          # to begin with or it has been modified          # to begin with or it has been modified
996          #          #
997          if clazz is None or self.classGrid.GetTable().IsModified():          if clazz is None or self.classGrid.GetTable().IsModified():
998              clazz = self.__BuildClassification(self.__cur_prop)              clazz = self.__BuildClassification(self.__cur_field, True)
   
         clazz.SetLayer(self.layer)  
999    
1000          self.layer.SetClassification(clazz)          self.layer.SetClassification(clazz)
1001    
1002          self.EndModal(wxID_OK)          self.haveApplied = True
1003    
1004      def OnCancel(self, event):      def _OnOK(self, event):
1005          """Do nothing. The layer's current classification stays the same."""          self._OnApply(event)
1006          self.EndModal(wxID_CANCEL)          self.Close()
1007    
1008        def _OnCloseBtn(self, event):
1009            """Close is similar to Cancel except that any changes that were
1010            made and applied remain applied, but the currently displayed
1011            classification is discarded.
1012            """
1013    
1014            self.Close()
1015    
1016      def OnAdd(self, event):      def _OnCancel(self, event):
1017          self.classGrid.GetTable().AddNewDataRow()          """The layer's current classification stays the same."""
1018          print "Classifier.OnAdd()"          if self.haveApplied:
1019                self.layer.SetClassification(self.originalClass)
1020    
1021            self.Close()
1022    
1023        def _OnAdd(self, event):
1024            self.classGrid.AppendRows()
1025    
1026        def _OnRemove(self, event):
1027            self.classGrid.DeleteSelectedRows()
1028    
1029        def _OnGenClass(self, event):
1030    
1031            genDlg = ClassGenDialog(self,
1032                                    self.layer.table,
1033                                    self.fields.GetString(self.__cur_field))
1034    
1035            if genDlg.ShowModal() == wxID_OK:
1036                clazz = genDlg.GetClassification()
1037                self.fields.SetClientData(self.__cur_field, clazz)
1038                self.classGrid.GetTable().SetClassification(clazz)
1039            genDlg.Destroy()
1040    
1041        def _OnMoveUp(self, event):
1042            sel = self.classGrid.GetCurrentSelection()
1043    
1044            #print "sel: ", sel
1045    
1046            if len(sel) == 1:
1047                i = sel[0]
1048                if i > 1:
1049                    table = self.classGrid.GetTable()
1050                    x = table.GetClassGroup(i - 1)
1051                    y = table.GetClassGroup(i)
1052                    table.SetClassGroup(i - 1, y)
1053                    table.SetClassGroup(i, x)
1054                    self.classGrid.ClearSelection()
1055                    self.classGrid.SelectRow(i - 1)
1056                    self.classGrid.MakeCellVisible(i - 1, 0)
1057    
1058      def OnGenRange(self, event):      def _OnMoveDown(self, event):
1059          print "Classifier.OnGenRange()"          sel = self.classGrid.GetCurrentSelection()
1060    
1061      def OnCellDClick(self, event):          if len(sel) == 1:
1062          r = event.GetRow()              i = sel[0]
1063          c = event.GetCol()              table = self.classGrid.GetTable()
1064          if c == COL_VISUAL:              if 0 < i < table.GetNumberRows() - 1:
1065              prop = self.classGrid.GetTable().GetValueAsCustom(r, c, None)                  x = table.GetClassGroup(i)
1066              propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())                  y = table.GetClassGroup(i + 1)
1067              if propDlg.ShowModal() == wxID_OK:                  table.SetClassGroup(i, y)
1068                  new_prop = propDlg.GetClassData()                  table.SetClassGroup(i + 1, x)
1069                  prop.SetStroke(new_prop.GetStroke())                  self.classGrid.ClearSelection()
1070                  prop.SetStrokeWidth(new_prop.GetStrokeWidth())                  self.classGrid.SelectRow(i + 1)
1071                  prop.SetFill(new_prop.GetFill())                  self.classGrid.MakeCellVisible(i + 1, 0)
                 self.classGrid.Refresh()  
             propDlg.Destroy()  
1072    
1073    
1074  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
# Line 404  ID_SELPROP_SPINCTRL = 4002 Line 1077  ID_SELPROP_SPINCTRL = 4002
1077  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1078  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1079  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1080    ID_SELPROP_STROKECLRTRANS = 4006
1081    ID_SELPROP_FILLCLRTRANS = 4007
1082    
1083  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1084    
1085      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1086          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1087                            style = wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1088    
1089          self.prop = ClassData(classData = prop)          self.prop = ClassGroupProperties(prop)
1090    
1091          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
1092    
# Line 421  class SelectPropertiesDialog(wxDialog): Line 1096  class SelectPropertiesDialog(wxDialog):
1096          previewBox = wxBoxSizer(wxVERTICAL)          previewBox = wxBoxSizer(wxVERTICAL)
1097          previewBox.Add(wxStaticText(self, -1, _("Preview:")),          previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1098              0, wxALIGN_LEFT | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
1099          self.previewer = ClassDataPreviewer(None, self.prop, shapeType,          self.previewWin = ClassDataPreviewWindow(None, self.prop, shapeType,
1100                                              self, ID_SELPROP_PREVIEW, (40, 40))                                              self, ID_SELPROP_PREVIEW, (40, 40))
1101          previewBox.Add(self.previewer, 1, wxGROW, 15)          previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1102    
1103          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1104    
1105          # control box          # control box
1106          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1107          ctrlBox.Add(  
1108              wxButton(self, ID_SELPROP_STROKECLR, "Change Stroke Color"),          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1109              0, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)          lineColorBox.Add(
1110          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self.OnChangeStrokeColor)              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1111                1, wxALL | wxGROW, 4)
1112            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1113    
1114            lineColorBox.Add(
1115                wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1116                1, wxALL | wxGROW, 4)
1117            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1118                       self._OnChangeLineColorTrans)
1119    
1120            ctrlBox.Add(lineColorBox, 0,
1121                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1122    
1123          if shapeType != SHAPETYPE_ARC:          if shapeType != SHAPETYPE_ARC:
1124              ctrlBox.Add(              fillColorBox = wxBoxSizer(wxHORIZONTAL)
1125                  wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),              fillColorBox.Add(
1126                  0, wxALIGN_LEFT | wxALL | wxGROW, 4)                  wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1127              EVT_BUTTON(self, ID_SELPROP_FILLCLR, self.OnChangeFillColor)                  1, wxALL | wxGROW, 4)
1128                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1129                fillColorBox.Add(
1130                    wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1131                    1, wxALL | wxGROW, 4)
1132                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1133                           self._OnChangeFillColorTrans)
1134                ctrlBox.Add(fillColorBox, 0,
1135                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1136    
1137          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1138          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1139                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1140          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1141                                     min=1, max=10,                                     min=1, max=10,
1142                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetLineWidth()),
1143                                     initial=prop.GetStrokeWidth())                                     initial=prop.GetLineWidth())
1144    
1145          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1146    
1147          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1148    
# Line 456  class SelectPropertiesDialog(wxDialog): Line 1150  class SelectPropertiesDialog(wxDialog):
1150          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1151          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1152    
   
1153          #          #
1154          # Control buttons:          # Control buttons:
1155          #          #
1156          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1157          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1158                        0, wxALL, 4)                        0, wxALL, 4)
1159          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1160                        0, wxALL, 4)                        0, wxALL, 4)
1161          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1162                                                                                                                                                                    
1163          EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1164          EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1165                                                                                                                                                                    
1166          self.SetAutoLayout(true)          self.SetAutoLayout(True)
1167          self.SetSizer(topBox)          self.SetSizer(topBox)
1168          topBox.Fit(self)          topBox.Fit(self)
1169          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1170    
1171      def OnOK(self, event):      def _OnOK(self, event):
1172          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1173    
1174      def OnCancel(self, event):      def _OnCancel(self, event):
1175          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1176    
1177      def OnSpin(self, event):      def _OnSpin(self, event):
1178          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
1179          self.previewer.Refresh()          self.previewWin.Refresh()
1180    
1181      def __GetColor(self, cur):      def __GetColor(self, cur):
1182          dialog = wxColourDialog(self)          dialog = wxColourDialog(self)
1183          dialog.GetColourData().SetColour(Color2wxColour(cur))          if cur is not Color.Transparent:
1184                dialog.GetColourData().SetColour(Color2wxColour(cur))
1185    
1186          ret = None          ret = None
1187          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1188              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = wxColour2Color(dialog.GetColourData().GetColour())
# Line 496  class SelectPropertiesDialog(wxDialog): Line 1191  class SelectPropertiesDialog(wxDialog):
1191    
1192          return ret          return ret
1193                    
1194      def OnChangeStrokeColor(self, event):      def _OnChangeLineColor(self, event):
1195          clr = self.__GetColor(self.prop.GetStroke())          clr = self.__GetColor(self.prop.GetLineColor())
1196          if clr is not None:          if clr is not None:
1197              self.prop.SetStroke(clr)              self.prop.SetLineColor(clr)
1198          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1199    
1200      def OnChangeFillColor(self, event):      def _OnChangeLineColorTrans(self, event):
1201            self.prop.SetLineColor(Color.Transparent)
1202            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1203            
1204        def _OnChangeFillColor(self, event):
1205          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1206          if clr is not None:          if clr is not None:
1207              self.prop.SetFill(clr)              self.prop.SetFill(clr)
1208          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1209    
1210      def GetClassData(self):      def _OnChangeFillColorTrans(self, event):
1211            self.prop.SetFill(Color.Transparent)
1212            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1213    
1214        def GetClassGroupProperties(self):
1215          return self.prop          return self.prop
1216    
1217    
1218  class ClassDataPreviewer(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1219    
1220      def __init__(self, rect, data, shapeType,      def __init__(self, rect, prop, shapeType,
1221                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1222          if parent is not None:          if parent is not None:
1223              wxWindow.__init__(self, parent, id, size=size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1224              EVT_PAINT(self, self.OnPaint)              EVT_PAINT(self, self._OnPaint)
1225    
1226          self.rect = rect          self.rect = rect
1227          self.data = data  
1228            self.prop = prop
1229          self.shapeType = shapeType          self.shapeType = shapeType
1230            self.previewer = ClassDataPreviewer()
1231    
1232      def OnPaint(self, event):      def _OnPaint(self, event):
1233          dc = wxPaintDC(self)          dc = wxPaintDC(self)
1234    
1235          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
1236          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1237    
1238          self.Draw(dc, None)          if self.rect is None:
1239                w, h = self.GetSize()
1240                rect = wxRect(0, 0, w, h)
1241            else:
1242                rect = self.rect
1243    
1244            self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1245    
1246      def Draw(self, dc, rect, data = None, shapeType = None):  class ClassDataPreviewer:
1247    
1248          if data is None: data = self.data      def Draw(self, dc, rect, prop, shapeType):
1249          if shapeType is None: shapeType = self.shapeType  
1250            assert dc is not None
1251            assert isinstance(prop, ClassGroupProperties)
1252    
1253          if rect is None:          if rect is None:
1254              x = y = 0              x = 0
1255              w, h = self.GetClientSizeTuple()              y = 0
1256                w, h = dc.GetSize()
1257          else:          else:
1258              x = rect.GetX()              x = rect.GetX()
1259              y = rect.GetY()              y = rect.GetY()
1260              w = rect.GetWidth()              w = rect.GetWidth()
1261              h = rect.GetHeight()              h = rect.GetHeight()
1262    
1263          stroke = data.GetStroke()          stroke = prop.GetLineColor()
1264          if stroke is Color.None:          if stroke is Color.Transparent:
1265              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1266          else:          else:
1267              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
1268                          data.GetStrokeWidth(),                          prop.GetLineWidth(),
1269                          wxSOLID)                          wxSOLID)
1270    
1271          stroke = data.GetFill()          stroke = prop.GetFill()
1272          if stroke is Color.None:          if stroke is Color.Transparent:
1273              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1274          else:          else:
1275              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 569  class ClassDataPreviewer(wxWindow): Line 1283  class ClassDataPreviewer(wxWindow):
1283                             wxPoint(x + w/2, y + h/4*3),                             wxPoint(x + w/2, y + h/4*3),
1284                             wxPoint(x + w, y)])                             wxPoint(x + w, y)])
1285    
1286          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT:
              shapeType == SHAPETYPE_POLYGON:  
1287    
1288              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2,
1289                            (min(w, h) - data.GetStrokeWidth())/2)                            (min(w, h) - prop.GetLineWidth())/2)
1290    
1291            elif shapeType == SHAPETYPE_POLYGON:
1292                dc.DrawRectangle(x, y, w, h)
1293    
1294  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1295    
1296      def __init__(self, shapeType):      def __init__(self, shapeType):
1297          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
1298          self.previewer = ClassDataPreviewer(None, None, shapeType)          self.shapeType = shapeType
1299            self.previewer = ClassDataPreviewer()
1300    
1301      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1302          data = grid.GetTable().GetValueAsCustom(row, col, "")          data = grid.GetTable().GetClassGroup(row)
   
1303    
1304          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1305                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 592  class ClassRenderer(wxPyGridCellRenderer Line 1308  class ClassRenderer(wxPyGridCellRenderer
1308          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
1309                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1310    
1311          self.previewer.Draw(dc, rect, data)          if not isinstance(data, ClassGroupMap):
1312                self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1313    
1314          if isSelected:          if isSelected:
1315              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
                       4, wxSOLID))  
1316              dc.SetBrush(wxTRANSPARENT_BRUSH)              dc.SetBrush(wxTRANSPARENT_BRUSH)
1317    
1318              dc.DrawRectangle(rect.GetX(), rect.GetY(),              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1319                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1320    

Legend:
Removed from v.430  
changed lines
  Added in v.615

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26