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

Legend:
Removed from v.444  
changed lines
  Added in v.460

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26