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

Legend:
Removed from v.451  
changed lines
  Added in v.611

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26