/[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 415 by jonathan, Wed Feb 19 16:52:51 2003 UTC revision 455 by bh, Tue Mar 4 11:32:20 2003 UTC
# Line 16  from wxPython.grid import * Line 16  from wxPython.grid import *
16    
17  from Thuban import _  from Thuban import _
18  from Thuban.common import *  from Thuban.common import *
19    from Thuban.UI.common import *
20    
21  from Thuban.Model.classification import * #Classification, ClassData  from Thuban.Model.classification import *
22    
23  from Thuban.Model.color import Color  from Thuban.Model.color import Color
24    
# Line 30  ID_CLASSIFY_OK = 4001 Line 31  ID_CLASSIFY_OK = 4001
31  ID_CLASSIFY_CANCEL = 4002  ID_CLASSIFY_CANCEL = 4002
32  ID_CLASSIFY_ADD = 4003  ID_CLASSIFY_ADD = 4003
33  ID_CLASSIFY_GENRANGE = 4004  ID_CLASSIFY_GENRANGE = 4004
34    ID_CLASSIFY_REMOVE = 4005
35    
36  COL_VISUAL = 0  COL_VISUAL = 0
37  COL_VALUE  = 1  COL_VALUE  = 1
38  COL_LABEL  = 2  COL_LABEL  = 2
39    
40    FIELD_CLASS = 0
41    FIELD_TYPE = 1
42    FIELD_NAME = 2
43    
44    FIELD_TYPE_STRING = "string"
45    FIELD_TYPE_INT = "int"
46    FIELD_TYPE_DOUBLE = "double"
47    
48  #  #
49  # 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
50  # passed into SetTable is the same that is returned by GetTable  # passed into SetTable is the same that is returned by GetTable
# Line 42  COL_LABEL  = 2 Line 52  COL_LABEL  = 2
52  import weakref  import weakref
53  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
54    
55      def __init__(self, parent, layer):      def __init__(self, parent, layer, fieldData):
56          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (300, 150))          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
57          self.SetTable(          self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
58              ClassTable(layer.GetClassification(), layer.ShapeType(), self),          self.SetSelectionMode(wxGrid.wxGridSelectRows)
59              true)          EVT_GRID_CELL_LEFT_DCLICK(self, self.OnCellDClick)
60    
61            EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
62            EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
63    
64            self.layer = layer
65            self.currentSelection = []
66    
67        def GetCurrentSelection(self):
68            sel = copy.copy(self.currentSelection)
69            sel.sort()
70            return sel
71    
72      def SetCellRenderer(self, row, col):      def SetCellRenderer(self, row, col):
73          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
# Line 58  class ClassGrid(wxGrid): Line 79  class ClassGrid(wxGrid):
79      def GetTable(self):      def GetTable(self):
80          return self.tableRef()          return self.tableRef()
81    
82        def DeleteSelectedRows(self):
83            sel = self.GetCurrentSelection()
84    
85            if len(sel) == 0: return
86            if len(sel) == 1:
87                group = self.GetTable().GetValueAsCustom(sel[0], COL_VISUAL, None)
88                if isinstance(group, ClassGroupDefault):
89                    wxMessageDialog(self,
90                                    "The Default group cannot be removed.",
91                                    style = wxOK | wxICON_EXCLAMATION).ShowModal()
92                    return
93            
94            self.ClearSelection()
95    
96            sel.reverse()
97            table = self.GetTable()
98            for row in sel:
99                table.DeleteRows(row)
100    
101            if len(sel) == 1:
102                r = sel[0]
103                if r > self.GetNumberRows() - 1:
104                    r = self.GetNumberRows() - 1
105                self.SelectRow(r)
106            
107    #
108    # XXX: This isn't working, and there is no way to deselect rows wxPython!
109    #
110    #   def DeselectRow(self, row):
111    #       self.ProcessEvent(
112    #           wxGridRangeSelectEvent(-1,
113    #                                  wxEVT_GRID_RANGE_SELECT,
114    #                                  self,
115    #                                  (row, row), (row, row),
116    #                                  sel = False))
117    
118        def OnCellDClick(self, event):
119            r = event.GetRow()
120            c = event.GetCol()
121            if c == COL_VISUAL:
122                # XXX: getting the properties is only possible with non-Maps!!!
123                group = self.GetTable().GetValueAsCustom(r, c, None)
124                prop = group.GetProperties()
125                propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
126                if propDlg.ShowModal() == wxID_OK:
127                    new_prop = propDlg.GetClassGroupProperties()
128                    prop.SetStroke(new_prop.GetStroke())
129                    prop.SetStrokeWidth(new_prop.GetStrokeWidth())
130                    prop.SetFill(new_prop.GetFill())
131                    self.Refresh()
132                propDlg.Destroy()
133    
134        #
135        # _OnSelectedRange() and _OnSelectedCell() were borrowed
136        # from http://wiki.wxpython.org
137        #
138        def _OnSelectedRange(self, event):
139            """Internal update to the selection tracking list"""
140            if event.Selecting():
141                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
142                    if index not in self.currentSelection:
143                        self.currentSelection.append( index )
144            else:
145                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
146                    while index in self.currentSelection:
147                        self.currentSelection.remove( index )
148            #self.ConfigureForSelection()
149    
150            event.Skip()
151    
152        def _OnSelectedCell( self, event ):
153            """Internal update to the selection tracking list"""
154            self.currentSelection = [ event.GetRow() ]
155            #self.ConfigureForSelection()
156            event.Skip()
157    
158  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
159    
160      NUM_COLS = 3      NUM_COLS = 3
161    
162      __col_labels = [_("Visual"), _("Value"), _("Label")]      __col_labels = [_("Symbol"), _("Value"), _("Label")]
163    
164      # this is tied to the values of classification.ClassData      def __init__(self, fieldData, shapeType, view = None):
     __row_labels = [_("Default"), _("Point"), _("Range"), _("Map")]  
   
     def __init__(self, clazz, shapeType, view = None):  
165          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
166          self.SetView(view)          self.SetView(view)
167          self.tdata = []          self.tdata = []
168    
169          self.Reset(clazz, shapeType)          self.Reset(fieldData, shapeType)
170    
171      def Reset(self, clazz, shapeType):      def Reset(self, fieldData, shapeType):
172    
173          self.GetView().BeginBatch()          self.GetView().BeginBatch()
174    
175            self.fieldData = fieldData
176          self.shapeType = shapeType          self.shapeType = shapeType
177          self.renderer = ClassRenderer(self.shapeType)          self.renderer = ClassRenderer(self.shapeType)
178    
179          old_tdata = self.tdata          old_len = len(self.tdata)
180    
181          self.tdata = []          self.tdata = []
182    
183            clazz = fieldData[FIELD_CLASS]
184          if clazz is None:          if clazz is None:
185              clazz = Classification()              clazz = Classification()
186    
187          p = clazz.GetDefaultData()  #       p = clazz.GetDefaultGroup()
188          np = ClassDataPoint(classData = p)  #       np = ClassDataDefault(classData = p)
189          self.tdata.append([np, 'DEFAULT', np.GetLabel()])  #       self.tdata.append([np, 'DEFAULT', np.GetLabel()])
190    
191          for p in clazz.points.values():  #       for p in clazz.points.values():
192              np = ClassDataPoint(p.GetValue(), classData = p)  #           np = ClassDataPoint(p.GetValue(), classData = p)
193              self.tdata.append([np, np.GetValue(), np.GetLabel()])  #           self.tdata.append([np, np.GetValue(), np.GetLabel()])
194    
195          for p in clazz.ranges:  #       for p in clazz.ranges:
196              np = ClassDataRange(p.GetMin(), p.GetMax(), classData = p)  #           np = ClassDataRange(p.GetMin(), p.GetMax(), classData = p)
197              self.tdata.append([np,  #           self.tdata.append([np,
198                                 '%s - %s' % (np.GetMin(), np.GetMax()),  #                              '%s - %s' % (np.GetMin(), np.GetMax()),
199                                 np.GetLabel()])  #                              np.GetLabel()])
200    
201            i = 0
202            for p in clazz:
203                np = copy.copy(p)
204                self.__SetRow(i, np)
205                i += 1
206    
207    
208          self.modified = 0          self.modified = 0
209    
210            self.__NotifyRowChanges(old_len, len(self.tdata))
211            self.GetView().EndBatch()
212    
213        def __NotifyRowChanges(self, curRows, newRows):
214          #          #
215          # silly message processing for updates to the number of          # silly message processing for updates to the number of
216          # rows and columns          # rows and columns
217          #          #
         curRows = len(old_tdata)  
         newRows = len(self.tdata)  
218          if newRows > curRows:          if newRows > curRows:
219              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
220                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
# Line 123  class ClassTable(wxPyGridTableBase): Line 227  class ClassTable(wxPyGridTableBase):
227                          curRows - newRows)    # how many                          curRows - newRows)    # how many
228              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
229    
230          self.GetView().EndBatch()  
231        def __SetRow(self, row, group):
232    
233            if isinstance(group, ClassGroupDefault):
234                data = [group, _('DEFAULT'), group.GetLabel()]
235            elif isinstance(group, ClassGroupSingleton):
236                data = [group, group.GetValue(), group.GetLabel()]
237            elif isinstance(group, ClassGroupRange):
238                data = [group,
239                        _('%s - %s') % (group.GetMin(), group.GetMax()),
240                        group.GetLabel()]
241    
242            if row >= len(self.tdata):
243                self.tdata.append(data)
244            else:
245                self.tdata[row] = data
246    
247      def GetColLabelValue(self, col):      def GetColLabelValue(self, col):
248          return self.__col_labels[col]          return self.__col_labels[col]
249    
250      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
251          data = self.tdata[row][COL_VISUAL]          data = self.tdata[row][COL_VISUAL]
252          type = data.GetType()          if isinstance(data, ClassGroupDefault):   return _("Default")
253          return self.__row_labels[type]          if isinstance(data, ClassGroupSingleton): return _("Singleton")
254            if isinstance(data, ClassGroupRange):     return _("Range")
255            if isinstance(data, ClassGroupMap):       return _("Map")
256    
257      def GetNumberRows(self):      def GetNumberRows(self):
258          return len(self.tdata)          return len(self.tdata)
# Line 157  class ClassTable(wxPyGridTableBase): Line 278  class ClassTable(wxPyGridTableBase):
278             (a single number or a range)             (a single number or a range)
279          """          """
280    
281          #          type = self.fieldData[FIELD_TYPE]
282          # first try to take the input as a single number  
283          # if there's an exception try to break it into          if type == FIELD_TYPE_STRING:
284          # a range seperated by a '-'. take care to ignore              return (value,)
285          # a leading '-' as that could be for a negative number.          elif type == FIELD_TYPE_INT or type == FIELD_TYPE_DOUBLE:
286          # then try to parse the individual parts. if there  
287          # is an exception here, let it pass up to the calling              #
288          # function.              # first try to take the input as a single number
289          #              # if there's an exception try to break it into
290          try:              # a range seperated by a '-'. take care to ignore
291              return (ClassData.POINT, Str2Num(value))              # a leading '-' as that could be for a negative number.
292          except:              # then try to parse the individual parts. if there
293              i = value.find('-')              # is an exception here, let it pass up to the calling
294              if i == 0:              # function.
295                  i = value.find('-', 1)              #
296                try:
297              return (ClassData.RANGE,                  return (Str2Num(value),)
298                      Str2Num(value[:i]),              except:
299                      Str2Num(value[i+1:]))                  i = value.find('-')
300                    if i == 0:
301                        i = value.find('-', 1)
302    
303                    return (Str2Num(value[:i]), Str2Num(value[i+1:]))
304                            
305    
306      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
307          data = self.tdata[row][COL_VISUAL]          group = self.tdata[row][COL_VISUAL]
308    
309          if col == COL_VISUAL:          if col == COL_VISUAL:
310              self.tdata[row][COL_VISUAL] = value              self.tdata[row][COL_VISUAL] = value
311          elif col == COL_VALUE:          elif col == COL_VALUE:
312              if row != 0: # DefaultData row              if isinstance(group, ClassGroupDefault):
313                  type = data.GetType()                  # not allowed to modify the default value
314                    pass
315                  if type == ClassData.MAP:              elif isinstance(group, ClassGroupMap):
316                      # something special                  # something special
317                    pass
318                else: # SINGLETON, RANGE
319                    try:
320                        dataInfo = self.__ParseInput(value)
321                    except:
322                        # bad input, ignore the request
323                      pass                      pass
324                  else: # POINT, RANGE                  else:
325                      try:  
326                          dataInfo = self.__ParseInput(value)                      ngroup = group
327                      except: pass                      props = group.GetProperties()
328                          # bad input, ignore the request                      if len(dataInfo) == 1:
329                            if not isinstance(group, ClassGroupSingleton):
330                                ngroup = ClassGroupSingleton(prop = props)
331                            ngroup.SetValue(dataInfo[0])
332                        elif len(dataInfo) == 2:
333                            if not isinstance(group, ClassGroupRange):
334                                ngroup = ClassGroupRange(prop = props)
335                            ngroup.SetRange(dataInfo[0], dataInfo[1])
336                      else:                      else:
337                            assert(False)
338    
339                        ngroup.SetLabel(group.GetLabel())
340                        self.__SetRow(row, ngroup)
341    
342                          if dataInfo[0] == ClassData.POINT:                      self.GetView().Refresh()
                             if type != ClassData.POINT:  
                                 data = ClassDataPoint(classData = data)  
                             data.SetValue(dataInfo[1])  
                             self.tdata[row][COL_VALUE] = data.GetValue()  
                         elif dataInfo[0] == ClassData.RANGE:  
                             if type != ClassData.RANGE:  
                                 data = ClassDataRange(classData = data)  
                             data.SetRange(dataInfo[1], dataInfo[2])  
                             self.tdata[row][COL_VALUE] = \  
                                 "%s - %s" % (data.GetMin(), data.GetMax())  
   
                         self.tdata[row][COL_VISUAL] = data  
   
                         # i just want it to redraw!  
                         self.GetView().BeginBatch()  
                         self.GetView().EndBatch()  
343    
344          elif col == COL_LABEL:          elif col == COL_LABEL:
345              data.SetLabel(value)              group.SetLabel(value)
346              self.tdata[row][COL_LABEL] = data.GetLabel()              self.tdata[row][COL_LABEL] = group.GetLabel()
347          else:          else:
348              raise ValueError(_("Invalid column request"))              raise ValueError(_("Invalid column request"))
349    
# Line 233  class ClassTable(wxPyGridTableBase): Line 359  class ClassTable(wxPyGridTableBase):
359    
360          return attr          return attr
361    
362      def GetClassData(self, row):      def GetClassGroup(self, row):
363          return self.tdata[row][COL_VISUAL]          return self.tdata[row][COL_VISUAL]
364    
365      def __Modified(self):      def __Modified(self):
# Line 242  class ClassTable(wxPyGridTableBase): Line 368  class ClassTable(wxPyGridTableBase):
368      def IsModified(self):      def IsModified(self):
369          return self.modified          return self.modified
370    
371      def AddNewDataRow(self):      def DeleteRows(self, pos, numRows = 1):
372          np = ClassDataPoint()          assert(pos >= 0)
373          self.tdata.append([np, np.GetValue(), np.GetLabel()])          old_len = len(self.tdata)
374          msg = wxGridTableMessage(self, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1)          for row in range(pos, pos - numRows, -1):
375          self.GetView().ProcessTableMessage(msg)              group = self.GetValueAsCustom(row, COL_VISUAL, None)
376          self.GetView().BeginBatch()              if not isinstance(group, ClassGroupDefault):
377          self.GetView().EndBatch()                  self.tdata.pop(row)
378                    self.__Modified()
379        
380            if self.IsModified():
381                self.__NotifyRowChanges(old_len, len(self.tdata))
382    
383        def AppendRows(self, numRows = 1):
384            old_len = len(self.tdata)
385            for i in range(numRows):
386                np = ClassGroupSingleton()
387                self.tdata.append([np, np.GetValue(), np.GetLabel()])
388                self.__Modified()
389    
390            if self.IsModified():
391                self.__NotifyRowChanges(old_len, len(self.tdata))
392    
393    
394  class Classifier(wxDialog):  class Classifier(wxDialog):
395            
396      def __init__(self, parent, layer):      def __init__(self, parent, layer):
397          wxDialog.__init__(self, parent, -1, _("Classify"),          wxDialog.__init__(self, parent, -1, _("Classify"),
398                            style = wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
399    
400    
401          self.layer = layer          self.layer = layer
402    
403          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
404    
405          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),
406              0, wxALIGN_LEFT | wxBOTTOM, 4)              0, wxALIGN_LEFT | wxALL, 4)
407          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),
408              0, wxALIGN_LEFT | wxBOTTOM, 4)              0, wxALIGN_LEFT | wxALL, 4)
409    
410          propertyBox = wxBoxSizer(wxHORIZONTAL)          propertyBox = wxBoxSizer(wxHORIZONTAL)
411          propertyBox.Add(wxStaticText(self, -1, _("Property: ")),          propertyBox.Add(wxStaticText(self, -1, _("Field: ")),
412              0, wxALIGN_CENTER | wxALL, 4)              0, wxALIGN_CENTER | wxALL, 4)
413    
414          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",
415                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
416    
417          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
418          self.__cur_prop = -1          # just assume the first field in case one hasn't been
419          field = layer.GetClassification().GetField()          # specified in the file.
420            self.__cur_field = 0
421            clazz = layer.GetClassification()
422            field = clazz.GetField()
423          for i in range(self.num_cols):          for i in range(self.num_cols):
424              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
425                self.fields.Append(name)
426    
427              if name == field:              if name == field:
428                  self.__cur_prop = i                  self.__cur_field = i
429              self.properties.Append(name)                  self.fields.SetClientData(i, [clazz, type, name, len, decc])
430              self.properties.SetClientData(i, None)              else:
431                    self.fields.SetClientData(i, [None, type, name, len, decc])
432          self.properties.SetSelection(self.__cur_prop)  
433          propertyBox.Add(self.properties, 1, wxGROW|wxALL, 0)          self.fields.SetSelection(self.__cur_field)
434          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)  
435            propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
436            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnFieldSelect)
437    
438          topBox.Add(propertyBox, 0, wxGROW, 4)          topBox.Add(propertyBox, 0, wxGROW, 4)
439    
# Line 293  class Classifier(wxDialog): Line 442  class Classifier(wxDialog):
442          #          #
443    
444          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
445          self.classGrid = ClassGrid(self, layer)          self.classGrid = ClassGrid(self,
446                                       layer,
447                                       self.fields.GetClientData(self.__cur_field))
448    
449          controlBox.Add(self.classGrid, 1, wxGROW, 0)          controlBox.Add(self.classGrid, 1, wxGROW, 0)
450    
# Line 303  class Classifier(wxDialog): Line 454  class Classifier(wxDialog):
454          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,
455              _("Generate Ranges")), 0, wxGROW | wxALL, 4)              _("Generate Ranges")), 0, wxGROW | wxALL, 4)
456    
457            controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,
458                _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
459    
460          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          controlBox.Add(controlButtonBox, 0, wxGROW, 10)
461          topBox.Add(controlBox, 1, wxGROW, 10)          topBox.Add(controlBox, 1, wxGROW, 10)
462    
463          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)
464            EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self.OnRemove)
465          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)
         EVT_GRID_CELL_LEFT_DCLICK(self.classGrid, self.OnCellDClick)  
466    
467          #          #
468          # Control buttons:          # Control buttons:
# Line 323  class Classifier(wxDialog): Line 477  class Classifier(wxDialog):
477          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)
478          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)
479    
480    
481    
482          self.SetAutoLayout(true)          self.SetAutoLayout(true)
483          self.SetSizer(topBox)          self.SetSizer(topBox)
484          topBox.Fit(self)          topBox.Fit(self)
# Line 331  class Classifier(wxDialog): Line 487  class Classifier(wxDialog):
487      def __BuildClassification(self, prop):      def __BuildClassification(self, prop):
488    
489          clazz = Classification()          clazz = Classification()
490          clazz.SetField(self.properties.GetStringSelection())          clazz.SetField(self.fields.GetString(prop))
491    
492          numRows = self.classGrid.GetNumberRows()          numRows = self.classGrid.GetNumberRows()
493    
494          if numRows > 0:          if numRows > 0:
495              table = self.classGrid.GetTable()              table = self.classGrid.GetTable()
496              clazz.SetDefaultData(table.GetClassData(0))              clazz.SetDefaultGroup(table.GetClassGroup(0))
497    
498              for i in range(1, numRows):              for i in range(1, numRows):
499                  clazz.AddClassData(table.GetClassData(i))                  clazz.AddGroup(table.GetClassGroup(i))
500    
501          return clazz          return clazz
502    
503      def OnPropertySelect(self, event):      def OnFieldSelect(self, event):
504          self.properties.SetClientData(          data = self.fields.GetClientData(self.__cur_field)
505              self.__cur_prop, self.__BuildClassification(self.__cur_prop))          data[FIELD_CLASS] = self.__BuildClassification(self.__cur_field)
506    
507          self.__cur_prop = self.properties.GetSelection()          self.fields.SetClientData(self.__cur_field, data)
508          clazz = self.properties.GetClientData(self.__cur_prop)  
509          table = self.classGrid.GetTable()          self.__cur_field = self.fields.GetSelection()
510            fieldData = self.fields.GetClientData(self.__cur_field)
511          table.Reset(clazz, self.layer.ShapeType())          self.classGrid.GetTable().Reset(fieldData, self.layer.ShapeType())
512    
513      def OnOK(self, event):      def OnOK(self, event):
514          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
515             it to the layer.             it to the layer.
516          """          """
517    
518          clazz = self.properties.GetClientData(self.__cur_prop)          clazz = self.fields.GetClientData(self.__cur_field)[FIELD_CLASS]
519    
520          #          #
521          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
522          # to begin with or it has been modified          # to begin with or it has been modified
523          #          #
524          if clazz is None or self.classGrid.GetTable().IsModified():          if clazz is None or self.classGrid.GetTable().IsModified():
525              clazz = self.__BuildClassification(self.__cur_prop)              clazz = self.__BuildClassification(self.__cur_field)
526    
527          clazz.SetLayer(self.layer)          clazz.SetLayer(self.layer)
528    
# Line 378  class Classifier(wxDialog): Line 534  class Classifier(wxDialog):
534          """Do nothing. The layer's current classification stays the same."""          """Do nothing. The layer's current classification stays the same."""
535          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
536    
   
537      def OnAdd(self, event):      def OnAdd(self, event):
538          self.classGrid.GetTable().AddNewDataRow()          self.classGrid.AppendRows()
539          print "Classifier.OnAdd()"  
540        def OnRemove(self, event):
541            self.classGrid.DeleteSelectedRows()
542    
543      def OnGenRange(self, event):      def OnGenRange(self, event):
544          print "Classifier.OnGenRange()"          print "Classifier.OnGenRange()"
545    
     def OnCellDClick(self, event):  
         r = event.GetRow()  
         c = event.GetCol()  
         if c == COL_VISUAL:  
             prop = self.classGrid.GetTable().GetValueAsCustom(r, c, None)  
             propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())  
             if propDlg.ShowModal() == wxID_OK:  
                 new_prop = propDlg.GetClassData()  
                 prop.SetStroke(new_prop.GetStroke())  
                 prop.SetStrokeWidth(new_prop.GetStrokeWidth())  
                 prop.SetFill(new_prop.GetFill())  
                 self.classGrid.BeginBatch()  
                 self.classGrid.EndBatch()  
             propDlg.Destroy()  
   
546    
547  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
548  ID_SELPROP_CANCEL = 4002  ID_SELPROP_CANCEL = 4002
549  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL = 4002
550    ID_SELPROP_PREVIEW = 4003
551    ID_SELPROP_STROKECLR = 4004
552    ID_SELPROP_FILLCLR = 4005
553    
554  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
555    
# Line 412  class SelectPropertiesDialog(wxDialog): Line 557  class SelectPropertiesDialog(wxDialog):
557          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
558                            style = wxRESIZE_BORDER)                            style = wxRESIZE_BORDER)
559    
560            self.prop = ClassGroupProperties(prop)
561    
562          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
   
         self.prop = ClassData(classData = prop)  
563    
564          topBox.Add(wxStaticText(self, -1, _("Stroke Color: ")),          itemBox = wxBoxSizer(wxHORIZONTAL)
565              0, wxALIGN_LEFT | wxALL, 4)  
566            # preview box
567            previewBox = wxBoxSizer(wxVERTICAL)
568            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
569                0, wxALIGN_LEFT | wxALL, 4)
570            self.previewer = ClassDataPreviewer(None, self.prop, shapeType,
571                                                self, ID_SELPROP_PREVIEW, (40, 40))
572            previewBox.Add(self.previewer, 1, wxGROW, 15)
573    
574            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
575    
576            # control box
577            ctrlBox = wxBoxSizer(wxVERTICAL)
578            ctrlBox.Add(
579                wxButton(self, ID_SELPROP_STROKECLR, "Change Stroke Color"),
580                1, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
581            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self.OnChangeStrokeColor)
582    
583            if shapeType != SHAPETYPE_ARC:
584                ctrlBox.Add(
585                    wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),
586                    0, wxALIGN_LEFT | wxALL | wxGROW, 4)
587                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self.OnChangeFillColor)
588    
589          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
590          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),
591                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxBOTTOM, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
592          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
593                                     min=1, max=10,                                     min=1, max=10,
594                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetStrokeWidth()),
# Line 431  class SelectPropertiesDialog(wxDialog): Line 598  class SelectPropertiesDialog(wxDialog):
598    
599          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
600    
601          topBox.Add(spinBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
602            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
603            topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
604    
         if shapeType != SHAPETYPE_ARC:  
             topBox.Add(wxStaticText(self, -1, _("Fill Color: ")),  
                 0, wxALIGN_LEFT | wxBOTTOM, 4)  
605    
606          #          #
607          # Control buttons:          # Control buttons:
# Line 463  class SelectPropertiesDialog(wxDialog): Line 629  class SelectPropertiesDialog(wxDialog):
629    
630      def OnSpin(self, event):      def OnSpin(self, event):
631          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())
632            self.previewer.Refresh()
633    
634        def __GetColor(self, cur):
635            dialog = wxColourDialog(self)
636            dialog.GetColourData().SetColour(Color2wxColour(cur))
637            ret = None
638            if dialog.ShowModal() == wxID_OK:
639                ret = wxColour2Color(dialog.GetColourData().GetColour())
640    
641            dialog.Destroy()
642    
643            return ret
644            
645        def OnChangeStrokeColor(self, event):
646            clr = self.__GetColor(self.prop.GetStroke())
647            if clr is not None:
648                self.prop.SetStroke(clr)
649            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
650    
651        def OnChangeFillColor(self, event):
652            clr = self.__GetColor(self.prop.GetFill())
653            if clr is not None:
654                self.prop.SetFill(clr)
655            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
656    
657      def GetClassData(self):      def GetClassGroupProperties(self):
658          return self.prop          return self.prop
659    
660    
661  class ClassDataPreviewer:  class ClassDataPreviewer(wxWindow):
662    
663      def Draw(self, dc, rect, data, shapeType):      def __init__(self, rect, prop, shapeType,
664                           parent = None, id = -1, size = wxDefaultSize):
665            if parent is not None:
666                wxWindow.__init__(self, parent, id, size=size)
667                EVT_PAINT(self, self.OnPaint)
668    
669          stroke = data.GetStroke()          self.rect = rect
670            self.prop = prop
671            self.shapeType = shapeType
672    
673        def OnPaint(self, event):
674            dc = wxPaintDC(self)
675    
676            # XXX: this doesn't seem to be having an effect:
677            dc.DestroyClippingRegion()
678    
679            self.Draw(dc, None)
680    
681        def Draw(self, dc, rect, prop = None, shapeType = None):
682    
683            if prop is None: prop = self.prop
684            if shapeType is None: shapeType = self.shapeType
685    
686            if rect is None:
687                x = y = 0
688                w, h = self.GetClientSizeTuple()
689            else:
690                x = rect.GetX()
691                y = rect.GetY()
692                w = rect.GetWidth()
693                h = rect.GetHeight()
694    
695            stroke = prop.GetStroke()
696          if stroke is Color.None:          if stroke is Color.None:
697              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
698          else:          else:
699              pen = wxPen(wxColour(stroke.red * 255,              pen = wxPen(Color2wxColour(stroke),
700                                   stroke.green * 255,                          prop.GetStrokeWidth(),
                                  stroke.blue * 255),  
                         data.GetStrokeWidth(),  
701                          wxSOLID)                          wxSOLID)
702    
703          stroke = data.GetFill()          stroke = prop.GetFill()
704          if stroke is Color.None:          if stroke is Color.None:
705              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
706          else:          else:
707              brush = wxBrush(wxColour(stroke.red * 255,              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
                                      stroke.green * 255,  
                                      stroke.blue * 255), wxSOLID)  
708    
709          dc.SetPen(pen)          dc.SetPen(pen)
710          dc.SetBrush(brush)          dc.SetBrush(brush)
711    
712          if shapeType == SHAPETYPE_ARC:          if shapeType == SHAPETYPE_ARC:
713              dc.DrawSpline([wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight()),              dc.DrawSpline([wxPoint(x, y + h),
714                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w/2, y + h/4),
715                                     rect.GetY() + rect.GetHeight()/4),                             wxPoint(x + w/2, y + h/4*3),
716                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w, y)])
                                    rect.GetY() + rect.GetHeight()/4*3),  
                            wxPoint(rect.GetX() + rect.GetWidth(), rect.GetY())])  
717    
718          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT or \
719               shapeType == SHAPETYPE_POLYGON:               shapeType == SHAPETYPE_POLYGON:
720    
721              dc.DrawCircle(rect.GetX() + rect.GetWidth()/2,              dc.DrawCircle(x + w/2, y + h/2,
722                            rect.GetY() + rect.GetHeight()/2,                            (min(w, h) - prop.GetStrokeWidth())/2)
                           (min(rect.GetWidth(), rect.GetHeight())  
                            - data.GetStrokeWidth())/2)  
723    
724  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
725    
726      def __init__(self, shapeType):      def __init__(self, shapeType):
727          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
728          self.shapeType = shapeType          self.previewer = ClassDataPreviewer(None, None, shapeType)
729    
730      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
731          data = grid.GetTable().GetValueAsCustom(row, col, "")          data = grid.GetTable().GetValueAsCustom(row, col, None)
   
732    
733          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
734                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 526  class ClassRenderer(wxPyGridCellRenderer Line 737  class ClassRenderer(wxPyGridCellRenderer
737          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
738                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
739    
740          ClassDataPreviewer().Draw(dc, rect, data, self.shapeType)          if not isinstance(data, ClassGroupMap):
741                self.previewer.Draw(dc, rect, data.GetProperties())
742    
743          if isSelected:          if isSelected:
744              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26