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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26