/[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 455 by bh, Tue Mar 4 11:32:20 2003 UTC revision 476 by bh, Thu Mar 6 15:03:37 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    # widget id's
31  ID_PROPERTY_SELECT = 4010  ID_PROPERTY_SELECT = 4010
32  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
33    
# Line 32  ID_CLASSIFY_CANCEL = 4002 Line 36  ID_CLASSIFY_CANCEL = 4002
36  ID_CLASSIFY_ADD = 4003  ID_CLASSIFY_ADD = 4003
37  ID_CLASSIFY_GENRANGE = 4004  ID_CLASSIFY_GENRANGE = 4004
38  ID_CLASSIFY_REMOVE = 4005  ID_CLASSIFY_REMOVE = 4005
39    ID_CLASSIFY_MOVEUP = 4006
40    ID_CLASSIFY_MOVEDOWN = 4007
41    
42  COL_VISUAL = 0  # table columns
43    COL_SYMBOL = 0
44  COL_VALUE  = 1  COL_VALUE  = 1
45  COL_LABEL  = 2  COL_LABEL  = 2
46    
47    # indices into the client data lists in Classifier.fields
48  FIELD_CLASS = 0  FIELD_CLASS = 0
49  FIELD_TYPE = 1  FIELD_TYPE = 1
50  FIELD_NAME = 2  FIELD_NAME = 2
51    
 FIELD_TYPE_STRING = "string"  
 FIELD_TYPE_INT = "int"  
 FIELD_TYPE_DOUBLE = "double"  
   
52  #  #
53  # 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
54  # 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 56  FIELD_TYPE_DOUBLE = "double"
56  import weakref  import weakref
57  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
58    
59      def __init__(self, parent, layer, fieldData):      def __init__(self, parent):
60            """Constructor.
61    
62            parent -- the parent window
63    
64            clazz -- the working classification that this grid should
65                     use for display.
66            """
67    
68          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
69          self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)          #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
         self.SetSelectionMode(wxGrid.wxGridSelectRows)  
         EVT_GRID_CELL_LEFT_DCLICK(self, self.OnCellDClick)  
70    
71            EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
72          EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)          EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
73          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
74    
         self.layer = layer  
75          self.currentSelection = []          self.currentSelection = []
76    
77        def CreateTable(self, clazz, shapeType):
78    
79            assert(isinstance(clazz, Classification))
80    
81            self.shapeType = shapeType
82            table = self.GetTable()
83            if table is None:
84                self.SetTable(ClassTable(clazz, self.shapeType, self), true)
85            else:
86                table.Reset(clazz, self.shapeType)
87    
88            self.SetSelectionMode(wxGrid.wxGridSelectRows)
89            self.ClearSelection()
90    
91      def GetCurrentSelection(self):      def GetCurrentSelection(self):
92            """Return the currently highlighted rows as an increasing list
93               of row numbers."""
94          sel = copy.copy(self.currentSelection)          sel = copy.copy(self.currentSelection)
95          sel.sort()          sel.sort()
96          return sel          return sel
# Line 72  class ClassGrid(wxGrid): Line 98  class ClassGrid(wxGrid):
98      def SetCellRenderer(self, row, col):      def SetCellRenderer(self, row, col):
99          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
100    
101        #
102        # [Set|Get]Table is taken from http://wiki.wxpython.org
103        # they are needed as a work around to ensure that the table
104        # that is passed to SetTable is the one that is returned
105        # by GetTable.
106        #
107      def SetTable(self, object, *attributes):      def SetTable(self, object, *attributes):
108          self.tableRef = weakref.ref(object)          self.tableRef = weakref.ref(object)
109          return wxGrid.SetTable(self, object, *attributes)          return wxGrid.SetTable(self, object, *attributes)
110    
111      def GetTable(self):      def GetTable(self):
112          return self.tableRef()          try:
113                return self.tableRef()
114            except:
115                return None
116    
117      def DeleteSelectedRows(self):      def DeleteSelectedRows(self):
118            """Deletes all highlighted rows.
119      
120            If only one row is highlighted then after it is deleted the
121            row that was below the deleted row is highlighted."""
122    
123          sel = self.GetCurrentSelection()          sel = self.GetCurrentSelection()
124    
125          if len(sel) == 0: return          # nothing to do
126            if len(sel) == 0: return
127    
128            # if only one thing is selected check if it is the default
129            # data row, because we can't remove that
130          if len(sel) == 1:          if len(sel) == 1:
131              group = self.GetTable().GetValueAsCustom(sel[0], COL_VISUAL, None)              group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
132              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
133                  wxMessageDialog(self,                  wxMessageDialog(self,
134                                  "The Default group cannot be removed.",                                  "The Default group cannot be removed.",
135                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()
136                  return                  return
137                    
138    
139          self.ClearSelection()          self.ClearSelection()
140    
141            # we need to remove things from the bottom up so we don't
142            # change the indexes of rows that will be deleted next
143          sel.reverse()          sel.reverse()
144    
145            #
146            # actually remove the rows
147            #
148          table = self.GetTable()          table = self.GetTable()
149          for row in sel:          for row in sel:
150              table.DeleteRows(row)              table.DeleteRows(row)
151    
152            #
153            # if there was only one row selected highlight the row
154            # that was directly below it, or move up one if the
155            # deleted row was the last row.
156            #
157          if len(sel) == 1:          if len(sel) == 1:
158              r = sel[0]              r = sel[0]
159              if r > self.GetNumberRows() - 1:              if r > self.GetNumberRows() - 1:
# Line 115  class ClassGrid(wxGrid): Line 171  class ClassGrid(wxGrid):
171  #                                  (row, row), (row, row),  #                                  (row, row), (row, row),
172  #                                  sel = False))  #                                  sel = False))
173    
174      def OnCellDClick(self, event):      def _OnCellDClick(self, event):
175            """Handle a double on a cell."""
176    
177          r = event.GetRow()          r = event.GetRow()
178          c = event.GetCol()          c = event.GetCol()
179          if c == COL_VISUAL:          if c == COL_SYMBOL:
             # XXX: getting the properties is only possible with non-Maps!!!  
180              group = self.GetTable().GetValueAsCustom(r, c, None)              group = self.GetTable().GetValueAsCustom(r, c, None)
181              prop = group.GetProperties()              prop = group.GetProperties()
182              propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())  
183                # get a new ClassGroupProperties object and copy the
184                # values over to our current object
185                propDlg = SelectPropertiesDialog(NULL, prop, self.shapeType)
186              if propDlg.ShowModal() == wxID_OK:              if propDlg.ShowModal() == wxID_OK:
187                  new_prop = propDlg.GetClassGroupProperties()                  new_prop = propDlg.GetClassGroupProperties()
188                  prop.SetStroke(new_prop.GetStroke())                  prop.SetProperties(new_prop)
                 prop.SetStrokeWidth(new_prop.GetStrokeWidth())  
                 prop.SetFill(new_prop.GetFill())  
189                  self.Refresh()                  self.Refresh()
190              propDlg.Destroy()              propDlg.Destroy()
191    
192      #      #
193      # _OnSelectedRange() and _OnSelectedCell() were borrowed      # _OnSelectedRange() and _OnSelectedCell() were borrowed
194      # from http://wiki.wxpython.org      # from http://wiki.wxpython.org to keep track of which
195        # cells are currently highlighted
196      #      #
197      def _OnSelectedRange(self, event):      def _OnSelectedRange(self, event):
198          """Internal update to the selection tracking list"""          """Internal update to the selection tracking list"""
# Line 156  class ClassGrid(wxGrid): Line 215  class ClassGrid(wxGrid):
215          event.Skip()          event.Skip()
216    
217  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
218        """Represents the underlying data structure for the grid."""
219    
220      NUM_COLS = 3      NUM_COLS = 3
221    
222      __col_labels = [_("Symbol"), _("Value"), _("Label")]      __col_labels = [_("Symbol"), _("Value"), _("Label")]
223    
224      def __init__(self, fieldData, shapeType, view = None):      def __init__(self, clazz, shapeType, view = None):
225            """Constructor.
226    
227            shapeType -- the type of shape that the layer uses
228    
229            view -- a wxGrid object that uses this class for its table
230            """
231    
232          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
233          self.SetView(view)          self.SetView(view)
234          self.tdata = []          self.tdata = []
235    
236          self.Reset(fieldData, shapeType)          self.Reset(clazz, shapeType)
237    
238        def Reset(self, clazz, shapeType):
239            """Reset the table with the given data.
240    
241      def Reset(self, fieldData, shapeType):          This is necessary because wxWindows does not allow a grid's
242            table to change once it has been intially set and so we
243            need a way of modifying the data.
244    
245            clazz -- the working classification that this table should
246                     use for display. This may be different from the
247                     classification in the layer.
248    
249            shapeType -- the type of shape that the layer uses
250            """
251    
252            assert(isinstance(clazz, Classification))
253    
254          self.GetView().BeginBatch()          self.GetView().BeginBatch()
255    
256          self.fieldData = fieldData          self.clazz = clazz
257          self.shapeType = shapeType          self.shapeType = shapeType
258          self.renderer = ClassRenderer(self.shapeType)          self.renderer = ClassRenderer(self.shapeType)
259    
# Line 180  class ClassTable(wxPyGridTableBase): Line 261  class ClassTable(wxPyGridTableBase):
261    
262          self.tdata = []          self.tdata = []
263    
264          clazz = fieldData[FIELD_CLASS]          #
265          if clazz is None:          # copy the data out of the classification and into our
266              clazz = Classification()          # array
267            #
268  #       p = clazz.GetDefaultGroup()          for p in self.clazz:
 #       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:  
269              np = copy.copy(p)              np = copy.copy(p)
270              self.__SetRow(i, np)              self.__SetRow(-1, np)
             i += 1  
271    
272    
273          self.modified = 0          self.__Modified(False)
274    
275          self.__NotifyRowChanges(old_len, len(self.tdata))          self.__NotifyRowChanges(old_len, len(self.tdata))
276          self.GetView().EndBatch()          self.GetView().EndBatch()
# Line 227  class ClassTable(wxPyGridTableBase): Line 292  class ClassTable(wxPyGridTableBase):
292                          curRows - newRows)    # how many                          curRows - newRows)    # how many
293              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
294    
   
295      def __SetRow(self, row, group):      def __SetRow(self, row, group):
296            """Set a row's data to that of the group.
297    
298          if isinstance(group, ClassGroupDefault):          row -- if row is -1 or greater than the current number of rows
299              data = [group, _('DEFAULT'), group.GetLabel()]                 then group is appended to the end.
300          elif isinstance(group, ClassGroupSingleton):          """
             data = [group, group.GetValue(), group.GetLabel()]  
         elif isinstance(group, ClassGroupRange):  
             data = [group,  
                     _('%s - %s') % (group.GetMin(), group.GetMax()),  
                     group.GetLabel()]  
301    
302          if row >= len(self.tdata):          # either append or replace
303              self.tdata.append(data)          if row == -1 or row >= self.GetNumberRows():
304                self.tdata.append(group)
305          else:          else:
306              self.tdata[row] = data              self.tdata[row] = group
307    
308            self.__Modified()
309    
310      def GetColLabelValue(self, col):      def GetColLabelValue(self, col):
311            """Return the label for the given column."""
312          return self.__col_labels[col]          return self.__col_labels[col]
313    
314      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
315          data = self.tdata[row][COL_VISUAL]          """Return the label for the given row."""
316          if isinstance(data, ClassGroupDefault):   return _("Default")  
317          if isinstance(data, ClassGroupSingleton): return _("Singleton")          group = self.tdata[row]
318          if isinstance(data, ClassGroupRange):     return _("Range")          if isinstance(group, ClassGroupDefault):   return _("Default")
319          if isinstance(data, ClassGroupMap):       return _("Map")          if isinstance(group, ClassGroupSingleton): return _("Singleton")
320            if isinstance(group, ClassGroupRange):     return _("Range")
321            if isinstance(group, ClassGroupMap):       return _("Map")
322    
323            assert(False) # shouldn't get here
324            return _("")
325    
326      def GetNumberRows(self):      def GetNumberRows(self):
327            """Return the number of rows."""
328          return len(self.tdata)          return len(self.tdata)
329    
330      def GetNumberCols(self):      def GetNumberCols(self):
331            """Return the number of columns."""
332          return self.NUM_COLS          return self.NUM_COLS
333    
334      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
335          return 0          """Determine if a cell is empty. This is always false."""
336            return False
337    
338      def GetValue(self, row, col):      def GetValue(self, row, col):
339          return self.GetValueAsCustom(row, col, "")          """Return the object that is used to represent the given
340               cell coordinates. This may not be a string."""
341            return self.GetValueAsCustom(row, col, None)
342    
343      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
344          self.SetValueAsCustom(row, col, "", value)          """Assign 'value' to the cell specified by 'row' and 'col'.
345    
346            The table is considered modified after this operation.
347            """
348    
349            self.SetValueAsCustom(row, col, None, value)
350          self.__Modified()          self.__Modified()
351                
352      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
353          return self.tdata[row][col]          """Return the object that is used to represent the given
354               cell coordinates. This may not be a string.
355    
356            typeName -- unused, but needed to overload wxPyGridTableBase
357            """
358    
359            group = self.tdata[row]
360    
361            if col == COL_SYMBOL:
362                return group
363    
364            if col == COL_LABEL:
365                return group.GetLabel()
366    
367            # col must be COL_VALUE
368            assert(col == COL_VALUE)
369    
370            if isinstance(group, ClassGroupDefault):
371                return _("DEFAULT")
372            elif isinstance(group, ClassGroupSingleton):
373                return group.GetValue()
374            elif isinstance(group, ClassGroupRange):
375                return _("%s - %s") % (group.GetMin(), group.GetMax())
376    
377            assert(False) # shouldn't get here
378            return None
379    
380      def __ParseInput(self, value):      def __ParseInput(self, value):
381          """Try to determine what kind of input value is          """Try to determine what kind of input value is
382             (a single number or a range)             (string, number, or range)
383    
384            Returns a tuple of length one if there is a single
385            value, or of length two if it is a range.
386          """          """
387    
388          type = self.fieldData[FIELD_TYPE]          type = self.clazz.GetFieldType()
389    
390          if type == FIELD_TYPE_STRING:          if type == FIELDTYPE_STRING:
391              return (value,)              return (value,)
392          elif type == FIELD_TYPE_INT or type == FIELD_TYPE_DOUBLE:          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:
393    
394                if type == FIELDTYPE_INT:
395                    conv = lambda p: int(float(p))
396                else:
397                    conv = lambda p: p
398    
399              #              #
400              # 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 406  class ClassTable(wxPyGridTableBase):
406              # function.              # function.
407              #              #
408              try:              try:
409                  return (Str2Num(value),)                  return (conv(Str2Num(value)),)
410              except:              except ValueError:
411                  i = value.find('-')                  i = value.find('-')
412                  if i == 0:                  if i == 0:
413                      i = value.find('-', 1)                      i = value.find('-', 1)
414    
415                  return (Str2Num(value[:i]), Str2Num(value[i+1:]))                  return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
416    
417            assert(False) # shouldn't get here
418                            
419    
420      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
421          group = self.tdata[row][COL_VISUAL]          """Set the cell specified by 'row' and 'col' to 'value'.
422    
423            If column represents the value column, the input is parsed
424            to determine if a string, number, or range was entered.
425            A new ClassGroup may be created if the type of data changes.
426    
427            The table is considered modified after this operation.
428    
429            typeName -- unused, but needed to overload wxPyGridTableBase
430            """
431    
432          if col == COL_VISUAL:          assert(col >= 0 and col < self.GetNumberCols())
433              self.tdata[row][COL_VISUAL] = value          assert(row >= 0 and row < self.GetNumberRows())
434    
435            group = self.tdata[row]
436    
437            mod = False
438    
439            if col == COL_SYMBOL:
440                self.__SetRow(row, value)
441                mod = True
442          elif col == COL_VALUE:          elif col == COL_VALUE:
443              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
444                  # not allowed to modify the default value                  # not allowed to modify the default value
# Line 318  class ClassTable(wxPyGridTableBase): Line 449  class ClassTable(wxPyGridTableBase):
449              else: # SINGLETON, RANGE              else: # SINGLETON, RANGE
450                  try:                  try:
451                      dataInfo = self.__ParseInput(value)                      dataInfo = self.__ParseInput(value)
452                  except:                  except ValueError:
453                      # bad input, ignore the request                      # bad input, ignore the request
454                      pass                      pass
455                  else:                  else:
456    
457                      ngroup = group                      ngroup = group
458                      props = group.GetProperties()                      props = group.GetProperties()
459    
460                        #
461                        # try to update the values, which may include
462                        # changing the underlying group type if the
463                        # group was a singleton and a range was entered
464                        #
465                      if len(dataInfo) == 1:                      if len(dataInfo) == 1:
466                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
467                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(prop = props)
# Line 337  class ClassTable(wxPyGridTableBase): Line 474  class ClassTable(wxPyGridTableBase):
474                          assert(False)                          assert(False)
475    
476                      ngroup.SetLabel(group.GetLabel())                      ngroup.SetLabel(group.GetLabel())
477    
478                      self.__SetRow(row, ngroup)                      self.__SetRow(row, ngroup)
479    
480                      self.GetView().Refresh()                      mod = True
481    
482    
483          elif col == COL_LABEL:          elif col == COL_LABEL:
484              group.SetLabel(value)              group.SetLabel(value)
485              self.tdata[row][COL_LABEL] = group.GetLabel()              mod = True
         else:  
             raise ValueError(_("Invalid column request"))  
486    
487          self.__Modified()          if mod:
488                self.__Modified()
489                self.GetView().Refresh()
490    
491      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
492            """Returns the cell attributes"""
493    
494          attr = wxGridCellAttr()          attr = wxGridCellAttr()
495          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)
496    
497          if col == COL_VISUAL:          if col == COL_SYMBOL:
498              attr.SetRenderer(ClassRenderer(self.shapeType))              attr.SetRenderer(ClassRenderer(self.shapeType))
499              attr.SetReadOnly()              attr.SetReadOnly()
500    
501          return attr          return attr
502    
503      def GetClassGroup(self, row):      def GetClassGroup(self, row):
504          return self.tdata[row][COL_VISUAL]          """Return the ClassGroup object representing row 'row'."""
505    
506      def __Modified(self):          return self.GetValueAsCustom(row, COL_SYMBOL, None)
507          self.modified = 1  
508        def SetClassGroup(self, row, group):
509            self.SetValueAsCustom(row, COL_SYMBOL, None, group)
510    
511        def __Modified(self, mod = True):
512            """Set the modified flag."""
513            self.modified = mod
514    
515      def IsModified(self):      def IsModified(self):
516            """True if this table is considered modified."""
517          return self.modified          return self.modified
518    
519      def DeleteRows(self, pos, numRows = 1):      def DeleteRows(self, pos, numRows = 1):
520            """Deletes 'numRows' beginning at row 'pos'.
521    
522            The table is considered modified after this operation.
523            """
524    
525          assert(pos >= 0)          assert(pos >= 0)
526          old_len = len(self.tdata)          old_len = len(self.tdata)
527          for row in range(pos, pos - numRows, -1):          for row in range(pos, pos - numRows, -1):
528              group = self.GetValueAsCustom(row, COL_VISUAL, None)              group = self.GetValueAsCustom(row, COL_SYMBOL, None)
529              if not isinstance(group, ClassGroupDefault):              if not isinstance(group, ClassGroupDefault):
530                  self.tdata.pop(row)                  self.tdata.pop(row)
531                  self.__Modified()                  self.__Modified()
# Line 381  class ClassTable(wxPyGridTableBase): Line 534  class ClassTable(wxPyGridTableBase):
534              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, len(self.tdata))
535    
536      def AppendRows(self, numRows = 1):      def AppendRows(self, numRows = 1):
537            """Append 'numRows' empty rows to the end of the table."""
538    
539          old_len = len(self.tdata)          old_len = len(self.tdata)
540          for i in range(numRows):          for i in range(numRows):
541              np = ClassGroupSingleton()              np = ClassGroupSingleton()
542              self.tdata.append([np, np.GetValue(), np.GetLabel()])              self.__SetRow(-1, np)
543                #self.tdata.append([np, np.GetValue(), np.GetLabel()])
544              self.__Modified()              self.__Modified()
545    
546          if self.IsModified():          if self.IsModified():
# Line 420  class Classifier(wxDialog): Line 576  class Classifier(wxDialog):
576          self.__cur_field = 0          self.__cur_field = 0
577          clazz = layer.GetClassification()          clazz = layer.GetClassification()
578          field = clazz.GetField()          field = clazz.GetField()
579    
580            self.fields.Append("<None>")
581            self.fields.SetClientData(0, None)
582    
583          for i in range(self.num_cols):          for i in range(self.num_cols):
584              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
585              self.fields.Append(name)              self.fields.Append(name)
586    
587              if name == field:              if name == field:
588                  self.__cur_field = i                  self.__cur_field = i + 1
589                  self.fields.SetClientData(i, [clazz, type, name, len, decc])                  self.fields.SetClientData(i + 1, clazz)
590              else:              else:
591                  self.fields.SetClientData(i, [None, type, name, len, decc])                  self.fields.SetClientData(i + 1, None)
592    
593          self.fields.SetSelection(self.__cur_field)          self.fields.SetSelection(self.__cur_field)
594    
595          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
596          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnFieldSelect)          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
597    
598          topBox.Add(propertyBox, 0, wxGROW, 4)          topBox.Add(propertyBox, 0, wxGROW, 4)
599    
# Line 442  class Classifier(wxDialog): Line 602  class Classifier(wxDialog):
602          #          #
603    
604          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
605          self.classGrid = ClassGrid(self,          self.classGrid = ClassGrid(self)
606                                     layer,  
607                                     self.fields.GetClientData(self.__cur_field))          self.__SetGridTable(self.__cur_field)
608    
609          controlBox.Add(self.classGrid, 1, wxGROW, 0)          controlBox.Add(self.classGrid, 1, wxGROW, 0)
610    
# Line 454  class Classifier(wxDialog): Line 614  class Classifier(wxDialog):
614          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,
615              _("Generate Ranges")), 0, wxGROW | wxALL, 4)              _("Generate Ranges")), 0, wxGROW | wxALL, 4)
616    
617            controlButtonBox.Add(wxButton(self, ID_CLASSIFY_MOVEUP,
618                _("Move Up")), 0, wxGROW | wxALL, 4)
619            controlButtonBox.Add(wxButton(self, ID_CLASSIFY_MOVEDOWN,
620                _("Move Down")), 0, wxGROW | wxALL, 4)
621    
622          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,
623              _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)              _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
624    
625          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          controlBox.Add(controlButtonBox, 0, wxGROW, 10)
626          topBox.Add(controlBox, 1, wxGROW, 10)          topBox.Add(controlBox, 1, wxGROW, 10)
627    
628          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)          EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)
629          EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self.OnRemove)          EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)
630          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self._OnGenRange)
631            EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)
632            EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)
633    
634          #          #
635          # Control buttons:          # Control buttons:
# Line 474  class Classifier(wxDialog): Line 641  class Classifier(wxDialog):
641                        0, wxALL, 4)                        0, wxALL, 4)
642          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
643    
644          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)
645          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)
646    
647    
648    
# Line 484  class Classifier(wxDialog): Line 651  class Classifier(wxDialog):
651          topBox.Fit(self)          topBox.Fit(self)
652          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
653    
654      def __BuildClassification(self, prop):      def __BuildClassification(self, fieldIndex):
655    
656          clazz = Classification()          clazz = Classification()
657          clazz.SetField(self.fields.GetString(prop))          fieldName = self.fields.GetString(fieldIndex)
658            fieldType = self.layer.GetFieldType(fieldName)
659    
660            clazz.SetField(fieldName)
661            clazz.SetFieldType(fieldType)
662    
663          numRows = self.classGrid.GetNumberRows()          numRows = self.classGrid.GetNumberRows()
664    
665          if numRows > 0:          assert(numRows > 0) # there should always be a default row
             table = self.classGrid.GetTable()  
             clazz.SetDefaultGroup(table.GetClassGroup(0))  
666    
667              for i in range(1, numRows):          table = self.classGrid.GetTable()
668                  clazz.AddGroup(table.GetClassGroup(i))          clazz.SetDefaultGroup(table.GetClassGroup(0))
669    
670            for i in range(1, numRows):
671                clazz.AddGroup(table.GetClassGroup(i))
672    
673          return clazz          return clazz
674    
675      def OnFieldSelect(self, event):      def __SetGridTable(self, fieldIndex):
         data = self.fields.GetClientData(self.__cur_field)  
         data[FIELD_CLASS] = self.__BuildClassification(self.__cur_field)  
676    
677          self.fields.SetClientData(self.__cur_field, data)          clazz = self.fields.GetClientData(fieldIndex)
678    
679            if clazz is None:
680                clazz = Classification()
681                clazz.SetDefaultGroup(
682                    ClassGroupDefault(
683                        self.layer.GetClassification().GetDefaultGroup().GetProperties()))
684    
685                fieldName = self.fields.GetString(fieldIndex)
686                fieldType = self.layer.GetFieldType(fieldName)
687                clazz.SetFieldType(fieldType)
688                    
689            self.classGrid.CreateTable(clazz, self.layer.ShapeType())
690    
691        def _OnFieldSelect(self, event):
692            clazz = self.__BuildClassification(self.__cur_field)
693            self.fields.SetClientData(self.__cur_field, clazz)
694    
695          self.__cur_field = self.fields.GetSelection()          self.__cur_field = self.fields.GetSelection()
696          fieldData = self.fields.GetClientData(self.__cur_field)          self.__SetGridTable(self.__cur_field)
         self.classGrid.GetTable().Reset(fieldData, self.layer.ShapeType())  
697    
698      def OnOK(self, event):      def _OnOK(self, event):
699          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
700             it to the layer.             it to the layer.
701          """          """
702    
703          clazz = self.fields.GetClientData(self.__cur_field)[FIELD_CLASS]          clazz = self.fields.GetClientData(self.__cur_field)
704    
705          #          #
706          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
# Line 530  class Classifier(wxDialog): Line 715  class Classifier(wxDialog):
715    
716          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
717    
718      def OnCancel(self, event):      def _OnCancel(self, event):
719          """Do nothing. The layer's current classification stays the same."""          """Do nothing. The layer's current classification stays the same."""
720          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
721    
722      def OnAdd(self, event):      def _OnAdd(self, event):
723          self.classGrid.AppendRows()          self.classGrid.AppendRows()
724    
725      def OnRemove(self, event):      def _OnRemove(self, event):
726          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
727    
728      def OnGenRange(self, event):      def _OnGenRange(self, event):
729          print "Classifier.OnGenRange()"          print "Classifier._OnGenRange()"
730    
731        def _OnMoveUp(self, event):
732            sel = self.classGrid.GetCurrentSelection()
733    
734            if len(sel) == 1:
735                i = sel[0]
736                if i > 1:
737                    table = self.classGrid.GetTable()
738                    x = table.GetClassGroup(i - 1)
739                    y = table.GetClassGroup(i)
740                    table.SetClassGroup(i - 1, y)
741                    table.SetClassGroup(i, x)
742                    self.classGrid.ClearSelection()
743                    self.classGrid.SelectRow(i - 1)
744    
745        def _OnMoveDown(self, event):
746            sel = self.classGrid.GetCurrentSelection()
747    
748            if len(sel) == 1:
749                i = sel[0]
750                table = self.classGrid.GetTable()
751                if 0 < i < table.GetNumberRows() - 1:
752                    x = table.GetClassGroup(i)
753                    y = table.GetClassGroup(i + 1)
754                    table.SetClassGroup(i, y)
755                    table.SetClassGroup(i + 1, x)
756                    self.classGrid.ClearSelection()
757                    self.classGrid.SelectRow(i + 1)
758    
759    
760  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
# Line 576  class SelectPropertiesDialog(wxDialog): Line 789  class SelectPropertiesDialog(wxDialog):
789          # control box          # control box
790          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
791          ctrlBox.Add(          ctrlBox.Add(
792              wxButton(self, ID_SELPROP_STROKECLR, "Change Stroke Color"),              wxButton(self, ID_SELPROP_STROKECLR, "Change Line Color"),
793              1, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)              0, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
794          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self.OnChangeStrokeColor)          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
795    
796          if shapeType != SHAPETYPE_ARC:          if shapeType != SHAPETYPE_ARC:
797              ctrlBox.Add(              ctrlBox.Add(
798                  wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),                  wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),
799                  0, wxALIGN_LEFT | wxALL | wxGROW, 4)                  0, wxALIGN_LEFT | wxALL | wxGROW, 4)
800              EVT_BUTTON(self, ID_SELPROP_FILLCLR, self.OnChangeFillColor)              EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
801    
802          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
803          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
804                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
805          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
806                                     min=1, max=10,                                     min=1, max=10,
807                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetLineWidth()),
808                                     initial=prop.GetStrokeWidth())                                     initial=prop.GetLineWidth())
809    
810          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
811    
812          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
813    
# Line 613  class SelectPropertiesDialog(wxDialog): Line 826  class SelectPropertiesDialog(wxDialog):
826                        0, wxALL, 4)                        0, wxALL, 4)
827          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
828                                                                                                                                                                    
829          EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
830          EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
831                                                                                                                                                                    
832          self.SetAutoLayout(true)          self.SetAutoLayout(true)
833          self.SetSizer(topBox)          self.SetSizer(topBox)
834          topBox.Fit(self)          topBox.Fit(self)
835          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
836    
837      def OnOK(self, event):      def _OnOK(self, event):
838          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
839    
840      def OnCancel(self, event):      def _OnCancel(self, event):
841          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
842    
843      def OnSpin(self, event):      def _OnSpin(self, event):
844          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
845          self.previewer.Refresh()          self.previewer.Refresh()
846    
847      def __GetColor(self, cur):      def __GetColor(self, cur):
# Line 642  class SelectPropertiesDialog(wxDialog): Line 855  class SelectPropertiesDialog(wxDialog):
855    
856          return ret          return ret
857                    
858      def OnChangeStrokeColor(self, event):      def _OnChangeLineColor(self, event):
859          clr = self.__GetColor(self.prop.GetStroke())          clr = self.__GetColor(self.prop.GetLineColor())
860          if clr is not None:          if clr is not None:
861              self.prop.SetStroke(clr)              self.prop.SetLineColor(clr)
862          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
863    
864      def OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
865          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
866          if clr is not None:          if clr is not None:
867              self.prop.SetFill(clr)              self.prop.SetFill(clr)
# Line 664  class ClassDataPreviewer(wxWindow): Line 877  class ClassDataPreviewer(wxWindow):
877                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
878          if parent is not None:          if parent is not None:
879              wxWindow.__init__(self, parent, id, size=size)              wxWindow.__init__(self, parent, id, size=size)
880              EVT_PAINT(self, self.OnPaint)              EVT_PAINT(self, self._OnPaint)
881    
882          self.rect = rect          self.rect = rect
883          self.prop = prop          self.prop = prop
884          self.shapeType = shapeType          self.shapeType = shapeType
885    
886      def OnPaint(self, event):      def _OnPaint(self, event):
887          dc = wxPaintDC(self)          dc = wxPaintDC(self)
888    
889          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
# Line 692  class ClassDataPreviewer(wxWindow): Line 905  class ClassDataPreviewer(wxWindow):
905              w = rect.GetWidth()              w = rect.GetWidth()
906              h = rect.GetHeight()              h = rect.GetHeight()
907    
908          stroke = prop.GetStroke()          stroke = prop.GetLineColor()
909          if stroke is Color.None:          if stroke is Color.None:
910              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
911          else:          else:
912              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
913                          prop.GetStrokeWidth(),                          prop.GetLineWidth(),
914                          wxSOLID)                          wxSOLID)
915    
916          stroke = prop.GetFill()          stroke = prop.GetFill()
# Line 719  class ClassDataPreviewer(wxWindow): Line 932  class ClassDataPreviewer(wxWindow):
932               shapeType == SHAPETYPE_POLYGON:               shapeType == SHAPETYPE_POLYGON:
933    
934              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2,
935                            (min(w, h) - prop.GetStrokeWidth())/2)                            (min(w, h) - prop.GetLineWidth())/2)
936    
937  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
938    

Legend:
Removed from v.455  
changed lines
  Added in v.476

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26