/[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 782 by jonathan, Tue Apr 29 16:53:55 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.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)  
         self.SetSelectionMode(wxGrid.wxGridSelectRows)  
         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                    # the float call allows the user to enter 1.0 for 1
482                    conv = lambda p: int(float(p))
483                else:
484                    conv = lambda p: p
485    
486              #              #
487              # 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 493  class ClassTable(wxPyGridTableBase):
493              # function.              # function.
494              #              #
495              try:              try:
496                  return (Str2Num(value),)                  return (conv(value),)
497              except:              except ValueError:
498                  i = value.find('-')                  i = value.find('-')
499                  if i == 0:                  if i == 0:
500                      i = value.find('-', 1)                      i = value.find('-', 1)
501    
502                  return (Str2Num(value[:i]), Str2Num(value[i+1:]))                  return (conv(value[:i]), conv(value[i+1:]))
503    
504            assert False  # shouldn't get here
505            return (0,)
506                            
507    
508      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
509          group = self.tdata[row][COL_VISUAL]          """Set the cell specified by 'row' and 'col' to 'value'.
510    
511          if col == COL_VISUAL:          If column represents the value column, the input is parsed
512              self.tdata[row][COL_VISUAL] = value          to determine if a string, number, or range was entered.
513            A new ClassGroup may be created if the type of data changes.
514    
515            The table is considered modified after this operation.
516    
517            typeName -- unused, but needed to overload wxPyGridTableBase
518            """
519    
520            assert 0 <= col < self.GetNumberCols()
521            assert 0 <= row < self.GetNumberRows()
522    
523            if row == 0:
524                group = self.clazz.GetDefaultGroup()
525            else:
526                group = self.clazz.GetGroup(row - 1)
527    
528            mod = True # assume the data will change
529    
530            if col == COL_VISIBLE:
531                group.SetVisible(value)
532            elif col == COL_SYMBOL:
533                group.SetProperties(value)
534            elif col == COL_LABEL:
535                group.SetLabel(value)
536          elif col == COL_VALUE:          elif col == COL_VALUE:
537              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
538                  # not allowed to modify the default value                  # not allowed to modify the default value
# Line 318  class ClassTable(wxPyGridTableBase): Line 543  class ClassTable(wxPyGridTableBase):
543              else: # SINGLETON, RANGE              else: # SINGLETON, RANGE
544                  try:                  try:
545                      dataInfo = self.__ParseInput(value)                      dataInfo = self.__ParseInput(value)
546                  except:                  except ValueError:
547                      # bad input, ignore the request                      # bad input, ignore the request
548                      pass                      mod = False
549                  else:                  else:
550    
551                        changed = False
552                      ngroup = group                      ngroup = group
553                      props = group.GetProperties()                      props = group.GetProperties()
554    
555                        #
556                        # try to update the values, which may include
557                        # changing the underlying group type if the
558                        # group was a singleton and a range was entered
559                        #
560                      if len(dataInfo) == 1:                      if len(dataInfo) == 1:
561                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
562                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(props = props)
563                                changed = True
564                          ngroup.SetValue(dataInfo[0])                          ngroup.SetValue(dataInfo[0])
565                      elif len(dataInfo) == 2:                      elif len(dataInfo) == 2:
566                          if not isinstance(group, ClassGroupRange):                          if not isinstance(group, ClassGroupRange):
567                              ngroup = ClassGroupRange(prop = props)                              ngroup = ClassGroupRange(props = props)
568                                changed = True
569                          ngroup.SetRange(dataInfo[0], dataInfo[1])                          ngroup.SetRange(dataInfo[0], dataInfo[1])
570                      else:                      else:
571                          assert(False)                          assert False
572                            pass
                     ngroup.SetLabel(group.GetLabel())  
                     self.__SetRow(row, ngroup)  
573    
574                      self.GetView().Refresh()                      if changed:
575                            ngroup.SetLabel(group.GetLabel())
576          elif col == COL_LABEL:                          self.SetClassGroup(row, ngroup)
             group.SetLabel(value)  
             self.tdata[row][COL_LABEL] = group.GetLabel()  
577          else:          else:
578              raise ValueError(_("Invalid column request"))              assert False # shouldn't be here
579                pass
580    
581          self.__Modified()          if mod:
582                self.__Modified()
583                self.GetView().Refresh()
584    
585      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
586          attr = wxGridCellAttr()          """Returns the cell attributes"""
         #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)  
   
         if col == COL_VISUAL:  
             attr.SetRenderer(ClassRenderer(self.shapeType))  
             attr.SetReadOnly()  
587    
588          return attr          return self.__colAttr.get(col, wxGridCellAttr()).Clone()
589    
590      def GetClassGroup(self, row):      def GetClassGroup(self, row):
591          return self.tdata[row][COL_VISUAL]          """Return the ClassGroup object representing row 'row'."""
592    
593      def __Modified(self):          #return self.GetValueAsCustom(row, COL_SYMBOL, None)
594          self.modified = 1          if row == 0:
595                return self.clazz.GetDefaultGroup()
596            else:
597                return self.clazz.GetGroup(row - 1)
598    
599        def SetClassGroup(self, row, group):
600            self.__SetRow(row, group)
601            self.GetView().Refresh()
602    
603        def __Modified(self, mod = True):
604            """Adjust the modified flag.
605    
606            mod -- if -1 set the modified flag to False, otherwise perform
607                   an 'or' operation with the current value of the flag and
608                   'mod'
609            """
610    
611            if mod == -1:
612                self.modified = False
613            else:
614                self.modified = mod or self.modified
615    
616      def IsModified(self):      def IsModified(self):
617            """True if this table is considered modified."""
618          return self.modified          return self.modified
619    
620      def DeleteRows(self, pos, numRows = 1):      def DeleteRows(self, pos, numRows = 1):
621          assert(pos >= 0)          """Deletes 'numRows' beginning at row 'pos'.
622          old_len = len(self.tdata)  
623            The row representing the default group is not removed.
624    
625            The table is considered modified if any rows are removed.
626            """
627    
628            assert pos >= 0
629            old_len = self.GetNumberRows()
630          for row in range(pos, pos - numRows, -1):          for row in range(pos, pos - numRows, -1):
631              group = self.GetValueAsCustom(row, COL_VISUAL, None)              group = self.GetClassGroup(row)
632              if not isinstance(group, ClassGroupDefault):              if row != 0:
633                  self.tdata.pop(row)                  self.clazz.RemoveGroup(row - 1)
634                  self.__Modified()                  self.__Modified()
635            
636          if self.IsModified():          if self.IsModified():
637              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
638    
639      def AppendRows(self, numRows = 1):      def AppendRows(self, numRows = 1):
640          old_len = len(self.tdata)          """Append 'numRows' empty rows to the end of the table.
641    
642            The table is considered modified if any rows are appended.
643            """
644    
645            old_len = self.GetNumberRows()
646          for i in range(numRows):          for i in range(numRows):
647              np = ClassGroupSingleton()              np = ClassGroupSingleton()
648              self.tdata.append([np, np.GetValue(), np.GetLabel()])              self.__SetRow(None, np)
             self.__Modified()  
649    
650          if self.IsModified():          if self.IsModified():
651              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
652    
653    
654  class Classifier(wxDialog):  ID_PROPERTY_OK = 4001
655        ID_PROPERTY_REVERT = 4002
656      def __init__(self, parent, layer):  ID_PROPERTY_ADD = 4003
657          wxDialog.__init__(self, parent, -1, _("Classify"),  ID_PROPERTY_GENCLASS = 4004
658                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)  ID_PROPERTY_REMOVE = 4005
659    ID_PROPERTY_MOVEUP = 4006
660    ID_PROPERTY_MOVEDOWN = 4007
661    ID_PROPERTY_TRY = 4008
662    ID_PROPERTY_EDITSYM = 4009
663    ID_PROPERTY_CLOSE = 4010
664    ID_PROPERTY_SELECT = 4011
665    ID_PROPERTY_TITLE = 4012
666    ID_PROPERTY_FIELDTEXT = 4013
667    
668    BTN_ADD = 0
669    BTN_EDIT = 1
670    BTN_GEN = 2
671    BTN_UP = 3
672    BTN_DOWN = 4
673    BTN_RM = 5
674    
675    EB_LAYER_TITLE = 0
676    EB_SELECT_FIELD = 1
677    EB_GEN_CLASS = 2
678    
679    class Classifier(NonModalDialog):
680    
681        type2string = {None:             _("None"),
682                       FIELDTYPE_STRING: _("Text"),
683                       FIELDTYPE_INT:    _("Integer"),
684                       FIELDTYPE_DOUBLE: _("Decimal")}
685    
686        def __init__(self, parent, name, layer, group = None):
687            NonModalDialog.__init__(self, parent, name, "")
688    
689            self.__SetTitle(layer.Title())
690    
691          self.layer = layer          self.layer = layer
692    
693            self.originalClass = self.layer.GetClassification()
694            field = self.originalClass.GetField()
695            fieldType = self.originalClass.GetFieldType()
696    
697            self.genDlg = None
698    
699          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
700    
701          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),          panel = wxPanel(self, -1, size=(100, 100))
702              0, wxALIGN_LEFT | wxALL, 4)  
703          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),          panelBox = wxBoxSizer(wxVERTICAL)
704    
705            sizer = wxBoxSizer(wxHORIZONTAL)
706            sizer.Add(wxStaticText(panel, -1, _("Title: ")),
707                0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
708            sizer.Add(wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title()),
709                      1, wxGROW | wxALL, 4)
710            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
711    
712            panelBox.Add(sizer, 0, wxGROW, 4)
713    
714            panelBox.Add(wxStaticText(panel, -1,
715                                    _("Type: %s") % layer.ShapeType()),
716              0, wxALIGN_LEFT | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
717    
         propertyBox = wxBoxSizer(wxHORIZONTAL)  
         propertyBox.Add(wxStaticText(self, -1, _("Field: ")),  
             0, wxALIGN_CENTER | wxALL, 4)  
718    
719          self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",          #####################
720                                       style = wxCB_READONLY)  
721            #panelBox = wxBoxSizer(wxVERTICAL)
722            classBox = wxStaticBoxSizer(
723                        wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
724    
725    
726            #
727            # make field choice box
728            #
729            self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
730            #self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
731            #                             style = wxCB_READONLY)
732    
733          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
734          # just assume the first field in case one hasn't been          # just assume the first field in case one hasn't been
735          # specified in the file.          # specified in the file.
736          self.__cur_field = 0          self.__cur_field = 0
737          clazz = layer.GetClassification()  
738          field = clazz.GetField()          self.fields.Append("<None>")
739    
740            if self.originalClass.GetFieldType() is None:
741                self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
742            else:
743                self.fields.SetClientData(0, None)
744    
745          for i in range(self.num_cols):          for i in range(self.num_cols):
746              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
747              self.fields.Append(name)              self.fields.Append(name)
748    
749              if name == field:              if name == field:
750                  self.__cur_field = i                  self.__cur_field = i + 1
751                  self.fields.SetClientData(i, [clazz, type, name, len, decc])                  self.fields.SetClientData(i + 1,
752                                              copy.deepcopy(self.originalClass))
753              else:              else:
754                  self.fields.SetClientData(i, [None, type, name, len, decc])                  self.fields.SetClientData(i + 1, None)
755    
         self.fields.SetSelection(self.__cur_field)  
756    
757          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)          ###########
758          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnFieldSelect)  
759    
760            sizer = wxBoxSizer(wxHORIZONTAL)
761            sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
762                0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
763            sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
764            EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
765            #EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
766    
767            classBox.Add(sizer, 0, wxGROW, 4)
768    
769            self.fieldTypeText = wxStaticText(panel, -1, "")
770            classBox.Add(self.fieldTypeText, 0,
771                         wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
772    
         topBox.Add(propertyBox, 0, wxGROW, 4)  
773    
774          #          #
775          # Classification data table          # Control Box
776          #          #
   
777          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
         self.classGrid = ClassGrid(self,  
                                    layer,  
                                    self.fields.GetClientData(self.__cur_field))  
778    
         controlBox.Add(self.classGrid, 1, wxGROW, 0)  
779    
780            ###########
781            #
782            # Control buttons:
783            #
784          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)  
785    
786          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,          button = wxButton(panel, ID_PROPERTY_GENCLASS, _("Generate Class"))
787              _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
788    
789          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          button = wxButton(panel, ID_PROPERTY_ADD, _("Add"))
790          topBox.Add(controlBox, 1, wxGROW, 10)          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
791    
792            button = wxButton(panel, ID_PROPERTY_MOVEUP, _("Move Up"))
793            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
794    
795            button = wxButton(panel, ID_PROPERTY_MOVEDOWN, _("Move Down"))
796            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
797    
798            button = wxButton(panel, ID_PROPERTY_EDITSYM, _("Edit Symbol"))
799            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
800    
801            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
802    
803          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)          button = wxButton(panel, ID_PROPERTY_REMOVE, _("Remove"))
804          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)  
805    
806    
807            ###########
808          #          #
809          # Control buttons:          # Classification data table
810          #          #
811    
812            self.classGrid = ClassGrid(panel, self)
813    
814            # calling __SelectField after creating the classGrid fills in the
815            # grid with the correct information
816            self.fields.SetSelection(self.__cur_field)
817            self.__SelectField(self.__cur_field, group = group)
818    
819            controlBox.Add(self.classGrid, 1, wxGROW, 0)
820            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
821    
822            classBox.Add(controlBox, 1, wxGROW, 10)
823            panelBox.Add(classBox, 1, wxGROW, 0)
824    
825            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
826            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
827            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
828            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
829            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
830            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
831    
832            ###########
833    
834    
835            panel.SetAutoLayout(True)
836            panel.SetSizer(panelBox)
837            panelBox.SetSizeHints(panel)
838    
839            topBox.Add(panel, 1, wxGROW | wxALL, 4)
840    
841            ###########
842    
843          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
844          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_PROPERTY_TRY, _("Try")),
845                        0, wxALL, 4)                        0, wxALL, 4)
846          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(60, 20, 0, wxALL, 4)
847            buttonBox.Add(wxButton(self, ID_PROPERTY_REVERT, _("Revert")),
848                        0, wxALL, 4)                        0, wxALL, 4)
849          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          buttonBox.Add(60, 20, 0, wxALL, 4)
850            buttonBox.Add(wxButton(self, ID_PROPERTY_OK, _("OK")),
851          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)                        0, wxALL, 4)
852          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          buttonBox.Add(60, 20, 0, wxALL, 4)
853            buttonBox.Add(wxButton(self, ID_PROPERTY_CLOSE, _("Close")),
854                          0, wxALL, 4)
855            topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
856    
857            EVT_BUTTON(self, ID_PROPERTY_OK, self._OnOK)
858            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
859            EVT_BUTTON(self, ID_PROPERTY_CLOSE, self._OnCloseBtn)
860            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
861    
862            ###########
863    
864          self.SetAutoLayout(true)          topBox.SetSizeHints(self)
865            self.SetAutoLayout(True)
866          self.SetSizer(topBox)          self.SetSizer(topBox)
         topBox.Fit(self)  
         topBox.SetSizeHints(self)  
867    
868      def __BuildClassification(self, prop):          #self.Fit()
869            ######################
870    
871          clazz = Classification()          self.haveApplied = False
         clazz.SetField(self.fields.GetString(prop))  
872    
873          numRows = self.classGrid.GetNumberRows()      def EditSymbol(self, row):
874            table = self.classGrid.GetTable()
875            prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
876    
877            # get a new ClassGroupProperties object and copy the
878            # values over to our current object
879            propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
880    
881            self.Enable(False)
882            if propDlg.ShowModal() == wxID_OK:
883                new_prop = propDlg.GetClassGroupProperties()
884                table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
885            self.Enable(True)
886            propDlg.Destroy()
887            
888        def _SetClassification(self, clazz):
889            
890            self.fields.SetClientData(self.__cur_field, clazz)
891            self.classGrid.GetTable().SetClassification(clazz)
892    
893          if numRows > 0:      def __BuildClassification(self, fieldIndex, copyClass = False):
894              table = self.classGrid.GetTable()  
895              clazz.SetDefaultGroup(table.GetClassGroup(0))  #       numRows = self.classGrid.GetNumberRows()
896    #       assert numRows > 0  # there should always be a default row
897    
898    #       clazz = Classification()
899            if fieldIndex == 0:
900                fieldName = None
901                fieldType = None
902            else:
903                fieldName = self.fields.GetString(fieldIndex)
904                fieldType = self.layer.GetFieldType(fieldName)
905    
906            clazz = self.classGrid.GetTable().GetClassification()
907    
908            if copyClass:
909                clazz = copy.deepcopy(clazz)
910    
911              for i in range(1, numRows):          clazz.SetField(fieldName)
912                  clazz.AddGroup(table.GetClassGroup(i))          clazz.SetFieldType(fieldType)
913    
914    
915    #       table = self.classGrid.GetTable()
916    #       clazz.SetDefaultGroup(table.GetClassGroup(0))
917    
918    #       for i in range(1, numRows):
919    #           clazz.AppendGroup(table.GetClassGroup(i))
920    
921          return clazz          return clazz
922    
923      def OnFieldSelect(self, event):      def __SetGridTable(self, fieldIndex, group = None):
924          data = self.fields.GetClientData(self.__cur_field)  
925          data[FIELD_CLASS] = self.__BuildClassification(self.__cur_field)          clazz = self.fields.GetClientData(fieldIndex)
926    
927            if clazz is None:
928                clazz = Classification()
929                clazz.SetDefaultGroup(
930                    ClassGroupDefault(
931                        self.layer.GetClassification().
932                                   GetDefaultGroup().GetProperties()))
933    
934                fieldName = self.fields.GetString(fieldIndex)
935                fieldType = self.layer.GetFieldType(fieldName)
936                clazz.SetFieldType(fieldType)
937                    
938            self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
939    
940        def __SetFieldTypeText(self, fieldIndex):
941            fieldName = self.fields.GetString(fieldIndex)
942            fieldType = self.layer.GetFieldType(fieldName)
943    
944            assert Classifier.type2string.has_key(fieldType)
945    
946            text = Classifier.type2string[fieldType]
947    
948            self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
949    
950        def __SelectField(self, newIndex, oldIndex = -1, group = None):
951            """This method assumes that the current selection for the
952            combo has already been set by a call to SetSelection().
953            """
954    
955            assert oldIndex >= -1
956    
957          self.fields.SetClientData(self.__cur_field, data)          if oldIndex != -1:
958                clazz = self.__BuildClassification(oldIndex)
959                self.fields.SetClientData(oldIndex, clazz)
960    
961          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())  
962    
963      def OnOK(self, event):          self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
964    
965            self.__SetFieldTypeText(newIndex)
966    
967        def __SetTitle(self, title):
968            if title != "":
969                title = ": " + title
970    
971            self.SetTitle(_("Layer Properties") + title)
972    
973        def _OnEditSymbol(self, event):
974            sel = self.classGrid.GetCurrentSelection()
975    
976            if len(sel) == 1:
977                self.EditSymbol(sel[0])
978    
979        def _OnFieldSelect(self, event):
980            index = self.fields.GetSelection()
981            self.__SelectField(index, self.__cur_field)
982            self.__cur_field = index
983    
984        def _OnTry(self, event):
985          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
986             it to the layer.             it to the layer.
987          """          """
988    
989          clazz = self.fields.GetClientData(self.__cur_field)[FIELD_CLASS]          clazz = self.fields.GetClientData(self.__cur_field)
990    
991          #          #
992          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
993          # to begin with or it has been modified          # to begin with or it has been modified
994          #          #
995          if clazz is None or self.classGrid.GetTable().IsModified():          if clazz is None or self.classGrid.GetTable().IsModified():
996              clazz = self.__BuildClassification(self.__cur_field)              clazz = self.__BuildClassification(self.__cur_field, True)
   
         clazz.SetLayer(self.layer)  
997    
998          self.layer.SetClassification(clazz)          self.layer.SetClassification(clazz)
999    
1000          self.EndModal(wxID_OK)          self.haveApplied = True
1001    
1002      def OnCancel(self, event):      def _OnOK(self, event):
1003          """Do nothing. The layer's current classification stays the same."""          self._OnTry(event)
1004          self.EndModal(wxID_CANCEL)          self.Close()
1005    
1006        def _OnCloseBtn(self, event):
1007            """Close is similar to Cancel except that any changes that were
1008            made and applied remain applied, but the currently displayed
1009            classification is discarded.
1010            """
1011    
1012            self.Close()
1013    
1014        def _OnRevert(self, event):
1015            """The layer's current classification stays the same."""
1016            if self.haveApplied:
1017                self.layer.SetClassification(self.originalClass)
1018    
1019      def OnAdd(self, event):          #self.Close()
1020    
1021        def _OnAdd(self, event):
1022          self.classGrid.AppendRows()          self.classGrid.AppendRows()
1023    
1024      def OnRemove(self, event):      def _OnRemove(self, event):
1025          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1026    
1027      def OnGenRange(self, event):      def _OnGenClass(self, event):
1028          print "Classifier.OnGenRange()"  
1029            self.genDlg = ClassGenDialog(self, self.layer,
1030                              self.fields.GetString(self.__cur_field))
1031    
1032            EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1033    
1034            self.__EnableButtons(EB_GEN_CLASS, False)
1035    
1036            self.genDlg.Show()
1037    
1038        def _OnGenDialogClose(self, event):
1039            self.genDlg.Destroy()
1040            self.__EnableButtons(EB_GEN_CLASS, True)
1041    
1042        def _OnMoveUp(self, event):
1043            sel = self.classGrid.GetCurrentSelection()
1044    
1045            if len(sel) == 1:
1046                i = sel[0]
1047                if i > 1:
1048                    table = self.classGrid.GetTable()
1049                    x = table.GetClassGroup(i - 1)
1050                    y = table.GetClassGroup(i)
1051                    table.SetClassGroup(i - 1, y)
1052                    table.SetClassGroup(i, x)
1053                    self.classGrid.ClearSelection()
1054                    self.classGrid.SelectRow(i - 1)
1055                    self.classGrid.MakeCellVisible(i - 1, 0)
1056    
1057        def _OnMoveDown(self, event):
1058            sel = self.classGrid.GetCurrentSelection()
1059    
1060            if len(sel) == 1:
1061                i = sel[0]
1062                table = self.classGrid.GetTable()
1063                if 0 < i < table.GetNumberRows() - 1:
1064                    x = table.GetClassGroup(i)
1065                    y = table.GetClassGroup(i + 1)
1066                    table.SetClassGroup(i, y)
1067                    table.SetClassGroup(i + 1, x)
1068                    self.classGrid.ClearSelection()
1069                    self.classGrid.SelectRow(i + 1)
1070                    self.classGrid.MakeCellVisible(i + 1, 0)
1071    
1072        def _OnTitleChanged(self, event):
1073            obj = event.GetEventObject()
1074    
1075            self.layer.SetTitle(obj.GetValue())
1076            self.__SetTitle(self.layer.Title())
1077    
1078            self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1079    
1080        def __EnableButtons(self, case, enable):
1081    
1082            if case == EB_LAYER_TITLE:  
1083                list = (ID_PROPERTY_OK,
1084                        ID_PROPERTY_CLOSE)
1085    
1086            elif case == EB_SELECT_FIELD:
1087                list = (ID_PROPERTY_GENCLASS,
1088                        ID_PROPERTY_ADD,
1089                        ID_PROPERTY_MOVEUP,
1090                        ID_PROPERTY_MOVEDOWN,
1091                        ID_PROPERTY_EDITSYM,
1092                        ID_PROPERTY_REMOVE)
1093    
1094            elif case == EB_GEN_CLASS:
1095                list = (ID_PROPERTY_SELECT,
1096                        ID_PROPERTY_FIELDTEXT,
1097                        ID_PROPERTY_GENCLASS,
1098                        ID_PROPERTY_EDITSYM)
1099    
1100            for id in list:
1101                self.FindWindowById(id).Enable(enable)
1102    
1103  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
1104  ID_SELPROP_CANCEL = 4002  ID_SELPROP_CANCEL = 4002
# Line 550  ID_SELPROP_SPINCTRL = 4002 Line 1106  ID_SELPROP_SPINCTRL = 4002
1106  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1107  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1108  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1109    ID_SELPROP_STROKECLRTRANS = 4006
1110    ID_SELPROP_FILLCLRTRANS = 4007
1111    
1112  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1113    
1114      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1115          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1116                            style = wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1117    
1118          self.prop = ClassGroupProperties(prop)          self.prop = ClassGroupProperties(prop)
1119    
# Line 567  class SelectPropertiesDialog(wxDialog): Line 1125  class SelectPropertiesDialog(wxDialog):
1125          previewBox = wxBoxSizer(wxVERTICAL)          previewBox = wxBoxSizer(wxVERTICAL)
1126          previewBox.Add(wxStaticText(self, -1, _("Preview:")),          previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1127              0, wxALIGN_LEFT | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
1128          self.previewer = ClassDataPreviewer(None, self.prop, shapeType,  
1129                                              self, ID_SELPROP_PREVIEW, (40, 40))          self.previewWin = ClassGroupPropertiesCtrl(
1130          previewBox.Add(self.previewer, 1, wxGROW, 15)              self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1131                (40, 40), wxSIMPLE_BORDER)
1132    
1133            self.previewWin.AllowEdit(False)
1134    
1135            previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1136    
1137          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1138    
1139          # control box          # control box
1140          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1141          ctrlBox.Add(  
1142              wxButton(self, ID_SELPROP_STROKECLR, "Change Stroke Color"),          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1143              1, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)          lineColorBox.Add(
1144          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self.OnChangeStrokeColor)              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1145                1, wxALL | wxGROW, 4)
1146            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1147    
1148            lineColorBox.Add(
1149                wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1150                1, wxALL | wxGROW, 4)
1151            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1152                       self._OnChangeLineColorTrans)
1153    
1154            ctrlBox.Add(lineColorBox, 0,
1155                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1156    
1157          if shapeType != SHAPETYPE_ARC:          if shapeType != SHAPETYPE_ARC:
1158              ctrlBox.Add(              fillColorBox = wxBoxSizer(wxHORIZONTAL)
1159                  wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),              fillColorBox.Add(
1160                  0, wxALIGN_LEFT | wxALL | wxGROW, 4)                  wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1161              EVT_BUTTON(self, ID_SELPROP_FILLCLR, self.OnChangeFillColor)                  1, wxALL | wxGROW, 4)
1162                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1163                fillColorBox.Add(
1164                    wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1165                    1, wxALL | wxGROW, 4)
1166                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1167                           self._OnChangeFillColorTrans)
1168                ctrlBox.Add(fillColorBox, 0,
1169                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1170    
1171          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1172          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1173                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1174          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1175                                     min=1, max=10,                                     min=1, max=10,
1176                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetLineWidth()),
1177                                     initial=prop.GetStrokeWidth())                                     initial=prop.GetLineWidth())
1178    
1179          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1180    
1181          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1182    
# Line 602  class SelectPropertiesDialog(wxDialog): Line 1184  class SelectPropertiesDialog(wxDialog):
1184          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1185          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1186    
   
1187          #          #
1188          # Control buttons:          # Control buttons:
1189          #          #
1190          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1191          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1192                        0, wxALL, 4)                        0, wxALL, 4)
1193          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1194                        0, wxALL, 4)                        0, wxALL, 4)
1195          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1196                                                                                                                                                                    
1197          EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1198          EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1199                                                                                                                                                                    
1200          self.SetAutoLayout(true)          self.SetAutoLayout(True)
1201          self.SetSizer(topBox)          self.SetSizer(topBox)
1202          topBox.Fit(self)          topBox.Fit(self)
1203          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1204    
1205      def OnOK(self, event):      def _OnOK(self, event):
1206          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1207    
1208      def OnCancel(self, event):      def _OnCancel(self, event):
1209          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1210    
1211      def OnSpin(self, event):      def _OnSpin(self, event):
1212          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
1213          self.previewer.Refresh()          self.previewWin.Refresh()
1214    
1215      def __GetColor(self, cur):      def __GetColor(self, cur):
1216          dialog = wxColourDialog(self)          dialog = wxColourDialog(self)
1217          dialog.GetColourData().SetColour(Color2wxColour(cur))          if cur is not Color.Transparent:
1218                dialog.GetColourData().SetColour(Color2wxColour(cur))
1219    
1220          ret = None          ret = None
1221          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1222              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = wxColour2Color(dialog.GetColourData().GetColour())
# Line 642  class SelectPropertiesDialog(wxDialog): Line 1225  class SelectPropertiesDialog(wxDialog):
1225    
1226          return ret          return ret
1227                    
1228      def OnChangeStrokeColor(self, event):      def _OnChangeLineColor(self, event):
1229          clr = self.__GetColor(self.prop.GetStroke())          clr = self.__GetColor(self.prop.GetLineColor())
1230          if clr is not None:          if clr is not None:
1231              self.prop.SetStroke(clr)              self.prop.SetLineColor(clr)
1232          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1233    
1234      def OnChangeFillColor(self, event):      def _OnChangeLineColorTrans(self, event):
1235            self.prop.SetLineColor(Color.Transparent)
1236            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1237            
1238        def _OnChangeFillColor(self, event):
1239          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1240          if clr is not None:          if clr is not None:
1241              self.prop.SetFill(clr)              self.prop.SetFill(clr)
1242          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1243    
1244        def _OnChangeFillColorTrans(self, event):
1245            self.prop.SetFill(Color.Transparent)
1246            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1247    
1248      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
1249          return self.prop          return self.prop
1250    
1251    
1252  class ClassDataPreviewer(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1253    
1254      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1255                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1256          if parent is not None:          if parent is not None:
1257              wxWindow.__init__(self, parent, id, size=size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1258              EVT_PAINT(self, self.OnPaint)              EVT_PAINT(self, self._OnPaint)
1259    
1260          self.rect = rect          self.rect = rect
1261    
1262          self.prop = prop          self.prop = prop
1263          self.shapeType = shapeType          self.shapeType = shapeType
1264            self.previewer = ClassDataPreviewer()
1265    
1266      def OnPaint(self, event):      def GetProperties():
1267            return self.prop
1268    
1269        def _OnPaint(self, event):
1270          dc = wxPaintDC(self)          dc = wxPaintDC(self)
1271    
1272          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
1273          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1274    
1275          self.Draw(dc, None)          if self.rect is None:
1276                w, h = self.GetSize()
1277                rect = wxRect(0, 0, w, h)
1278            else:
1279                rect = self.rect
1280    
1281      def Draw(self, dc, rect, prop = None, shapeType = None):          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1282    
1283          if prop is None: prop = self.prop  class ClassDataPreviewer:
1284          if shapeType is None: shapeType = self.shapeType  
1285        def Draw(self, dc, rect, prop, shapeType):
1286    
1287            assert dc is not None
1288            assert isinstance(prop, ClassGroupProperties)
1289    
1290          if rect is None:          if rect is None:
1291              x = y = 0              x = 0
1292              w, h = self.GetClientSizeTuple()              y = 0
1293                w, h = dc.GetSize()
1294          else:          else:
1295              x = rect.GetX()              x = rect.GetX()
1296              y = rect.GetY()              y = rect.GetY()
1297              w = rect.GetWidth()              w = rect.GetWidth()
1298              h = rect.GetHeight()              h = rect.GetHeight()
1299    
1300          stroke = prop.GetStroke()          stroke = prop.GetLineColor()
1301          if stroke is Color.None:          if stroke is Color.Transparent:
1302              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1303          else:          else:
1304              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
1305                          prop.GetStrokeWidth(),                          prop.GetLineWidth(),
1306                          wxSOLID)                          wxSOLID)
1307    
1308          stroke = prop.GetFill()          stroke = prop.GetFill()
1309          if stroke is Color.None:          if stroke is Color.Transparent:
1310              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1311          else:          else:
1312              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 715  class ClassDataPreviewer(wxWindow): Line 1320  class ClassDataPreviewer(wxWindow):
1320                             wxPoint(x + w/2, y + h/4*3),                             wxPoint(x + w/2, y + h/4*3),
1321                             wxPoint(x + w, y)])                             wxPoint(x + w, y)])
1322    
1323          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT:
              shapeType == SHAPETYPE_POLYGON:  
1324    
1325              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2,
1326                            (min(w, h) - prop.GetStrokeWidth())/2)                            (min(w, h) - prop.GetLineWidth())/2)
1327    
1328            elif shapeType == SHAPETYPE_POLYGON:
1329                dc.DrawRectangle(x, y, w, h)
1330    
1331  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1332    
1333      def __init__(self, shapeType):      def __init__(self, shapeType):
1334          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
1335          self.previewer = ClassDataPreviewer(None, None, shapeType)          self.shapeType = shapeType
1336            self.previewer = ClassDataPreviewer()
1337    
1338      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1339          data = grid.GetTable().GetValueAsCustom(row, col, None)          data = grid.GetTable().GetClassGroup(row)
1340    
1341          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1342                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 738  class ClassRenderer(wxPyGridCellRenderer Line 1346  class ClassRenderer(wxPyGridCellRenderer
1346                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1347    
1348          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1349              self.previewer.Draw(dc, rect, data.GetProperties())              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1350    
1351          if isSelected:          if isSelected:
1352              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
                       4, wxSOLID))  
1353              dc.SetBrush(wxTRANSPARENT_BRUSH)              dc.SetBrush(wxTRANSPARENT_BRUSH)
1354    
1355              dc.DrawRectangle(rect.GetX(), rect.GetY(),              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1356                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1357    
1358          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1359    
1360    
1361    class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1362    
1363        def __init__(self, parent, id, props, shapeType,
1364                     size = wxDefaultSize, style = 0):
1365    
1366            wxWindow.__init__(self, parent, id, size = size, style = style)
1367    
1368            self.SetProperties(props)
1369            self.SetShapeType(shapeType)
1370            self.AllowEdit(True)
1371    
1372            EVT_PAINT(self, self._OnPaint)
1373            EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1374    
1375            self.previewer = ClassDataPreviewer()
1376    
1377        def _OnPaint(self, event):
1378            dc = wxPaintDC(self)
1379    
1380            # XXX: this doesn't seem to be having an effect:
1381            dc.DestroyClippingRegion()
1382    
1383            w, h = self.GetClientSize()
1384    
1385            self.previewer.Draw(dc,
1386                                wxRect(0, 0, w, h),
1387                                self.GetProperties(),
1388                                self.GetShapeType())
1389    
1390    
1391        def GetProperties(self):
1392            return self.props
1393    
1394        def SetProperties(self, props):
1395            self.props = props
1396            self.Refresh()
1397    
1398        def GetShapeType(self):
1399            return self.shapeType
1400    
1401        def SetShapeType(self, shapeType):
1402            self.shapeType = shapeType
1403            self.Refresh()
1404    
1405        def AllowEdit(self, allow):
1406            self.allowEdit = allow
1407    
1408        def DoEdit(self):
1409            if not self.allowEdit: return
1410    
1411            propDlg = SelectPropertiesDialog(NULL,
1412                                             self.GetProperties(),
1413                                             self.GetShapeType())
1414    
1415            if propDlg.ShowModal() == wxID_OK:
1416                new_prop = propDlg.GetClassGroupProperties()
1417                self.SetProperties(new_prop)
1418                self.Refresh()
1419    
1420            propDlg.Destroy()
1421    
1422        def _OnLeftDClick(self, event):
1423            self.DoEdit()

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26