/[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 496 by jonathan, Mon Mar 10 10:54:50 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    
20  from Thuban import _  from Thuban import _
21  from Thuban.common import *  from Thuban.common import *
22    from Thuban.UI.common import *
23    
24  from Thuban.Model.classification import * #Classification, ClassData  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 dialogs import NonModalDialog
31    
32    # widget id's
33  ID_PROPERTY_SELECT = 4010  ID_PROPERTY_SELECT = 4010
34  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
35    
# Line 30  ID_CLASSIFY_OK = 4001 Line 37  ID_CLASSIFY_OK = 4001
37  ID_CLASSIFY_CANCEL = 4002  ID_CLASSIFY_CANCEL = 4002
38  ID_CLASSIFY_ADD = 4003  ID_CLASSIFY_ADD = 4003
39  ID_CLASSIFY_GENRANGE = 4004  ID_CLASSIFY_GENRANGE = 4004
40    ID_CLASSIFY_REMOVE = 4005
41    ID_CLASSIFY_MOVEUP = 4006
42    ID_CLASSIFY_MOVEDOWN = 4007
43    ID_CLASSIFY_APPLY = 4008
44    
45  COL_VISUAL = 0  # table columns
46    COL_SYMBOL = 0
47  COL_VALUE  = 1  COL_VALUE  = 1
48  COL_LABEL  = 2  COL_LABEL  = 2
49    
50    # indices into the client data lists in Classifier.fields
51    FIELD_CLASS = 0
52    FIELD_TYPE = 1
53    FIELD_NAME = 2
54    
55  #  #
56  # 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
57  # 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 59  COL_LABEL  = 2
59  import weakref  import weakref
60  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
61    
62      def __init__(self, parent, layer):      def __init__(self, parent):
63          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (300, 150))          """Constructor.
64          self.SetTable(  
65              ClassTable(layer.GetClassification(), layer.ShapeType(), self),          parent -- the parent window
66              true)  
67            clazz -- the working classification that this grid should
68                     use for display.
69            """
70    
71            #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
72            wxGrid.__init__(self, parent, ID_CLASS_TABLE)
73            #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
74    
75            EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
76            EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
77            EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
78    
79            self.currentSelection = []
80    
81        def CreateTable(self, clazz, shapeType):
82    
83            assert(isinstance(clazz, Classification))
84    
85            self.shapeType = shapeType
86            table = self.GetTable()
87            if table is None:
88                self.SetTable(ClassTable(clazz, self.shapeType, self), true)
89            else:
90                table.Reset(clazz, self.shapeType)
91    
92            self.SetSelectionMode(wxGrid.wxGridSelectRows)
93            self.ClearSelection()
94    
95        def GetCurrentSelection(self):
96            """Return the currently highlighted rows as an increasing list
97               of row numbers."""
98            sel = copy.copy(self.currentSelection)
99            sel.sort()
100            return sel
101    
102      def SetCellRenderer(self, row, col):      def SetCellRenderer(self, row, col):
103          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
104    
105        #
106        # [Set|Get]Table is taken from http://wiki.wxpython.org
107        # they are needed as a work around to ensure that the table
108        # that is passed to SetTable is the one that is returned
109        # by GetTable.
110        #
111      def SetTable(self, object, *attributes):      def SetTable(self, object, *attributes):
112          self.tableRef = weakref.ref(object)          self.tableRef = weakref.ref(object)
113          return wxGrid.SetTable(self, object, *attributes)          return wxGrid.SetTable(self, object, *attributes)
114    
115      def GetTable(self):      def GetTable(self):
116          return self.tableRef()          try:
117                return self.tableRef()
118            except:
119                return None
120    
121        def DeleteSelectedRows(self):
122            """Deletes all highlighted rows.
123      
124            If only one row is highlighted then after it is deleted the
125            row that was below the deleted row is highlighted."""
126    
127            sel = self.GetCurrentSelection()
128    
129            # nothing to do
130            if len(sel) == 0: return
131    
132            # if only one thing is selected check if it is the default
133            # data row, because we can't remove that
134            if len(sel) == 1:
135                #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
136                group = self.GetTable().GetClassGroup(sel[0])
137                if isinstance(group, ClassGroupDefault):
138                    wxMessageDialog(self,
139                                    "The Default group cannot be removed.",
140                                    style = wxOK | wxICON_EXCLAMATION).ShowModal()
141                    return
142            
143    
144            self.ClearSelection()
145    
146            # we need to remove things from the bottom up so we don't
147            # change the indexes of rows that will be deleted next
148            sel.reverse()
149    
150            #
151            # actually remove the rows
152            #
153            table = self.GetTable()
154            for row in sel:
155                table.DeleteRows(row)
156    
157            #
158            # if there was only one row selected highlight the row
159            # that was directly below it, or move up one if the
160            # deleted row was the last row.
161            #
162            if len(sel) == 1:
163                r = sel[0]
164                if r > self.GetNumberRows() - 1:
165                    r = self.GetNumberRows() - 1
166                self.SelectRow(r)
167            
168    #
169    # XXX: This isn't working, and there is no way to deselect rows wxPython!
170    #
171    #   def DeselectRow(self, row):
172    #       self.ProcessEvent(
173    #           wxGridRangeSelectEvent(-1,
174    #                                  wxEVT_GRID_RANGE_SELECT,
175    #                                  self,
176    #                                  (row, row), (row, row),
177    #                                  sel = False))
178    
179        def _OnCellDClick(self, event):
180            """Handle a double on a cell."""
181    
182            r = event.GetRow()
183            c = event.GetCol()
184            if c == COL_SYMBOL:
185                prop = self.GetTable().GetValueAsCustom(r, c, None)
186                #prop = group.GetProperties()
187    
188                # get a new ClassGroupProperties object and copy the
189                # values over to our current object
190                propDlg = SelectPropertiesDialog(NULL, prop, self.shapeType)
191                if propDlg.ShowModal() == wxID_OK:
192                    new_prop = propDlg.GetClassGroupProperties()
193                    #prop.SetProperties(new_prop)
194                    self.GetTable().SetValueAsCustom(r, c, None, new_prop)
195                propDlg.Destroy()
196    
197        #
198        # _OnSelectedRange() and _OnSelectedCell() were borrowed
199        # from http://wiki.wxpython.org to keep track of which
200        # cells are currently highlighted
201        #
202        def _OnSelectedRange(self, event):
203            """Internal update to the selection tracking list"""
204            if event.Selecting():
205                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
206                    if index not in self.currentSelection:
207                        self.currentSelection.append( index )
208            else:
209                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
210                    while index in self.currentSelection:
211                        self.currentSelection.remove( index )
212            #self.ConfigureForSelection()
213    
214            event.Skip()
215    
216        def _OnSelectedCell( self, event ):
217            """Internal update to the selection tracking list"""
218            self.currentSelection = [ event.GetRow() ]
219            #self.ConfigureForSelection()
220            event.Skip()
221    
222  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
223        """Represents the underlying data structure for the grid."""
224    
225      NUM_COLS = 3      NUM_COLS = 3
226    
227      __col_labels = [_("Visual"), _("Value"), _("Label")]      __col_labels = [_("Symbol"), _("Value"), _("Label")]
   
     # this is tied to the values of classification.ClassData  
     __row_labels = [_("Default"), _("Point"), _("Range"), _("Map")]  
228    
229      def __init__(self, clazz, shapeType, view = None):      def __init__(self, clazz, shapeType, view = None):
230            """Constructor.
231    
232            shapeType -- the type of shape that the layer uses
233    
234            view -- a wxGrid object that uses this class for its table
235            """
236    
237          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
238    
239          self.SetView(view)          self.SetView(view)
240          self.tdata = []          self.tdata = []
241    
242          self.Reset(clazz, shapeType)          self.Reset(clazz, shapeType)
243    
244      def Reset(self, clazz, shapeType):      def Reset(self, clazz, shapeType):
245            """Reset the table with the given data.
246    
247            This is necessary because wxWindows does not allow a grid's
248            table to change once it has been intially set and so we
249            need a way of modifying the data.
250    
251            clazz -- the working classification that this table should
252                     use for display. This may be different from the
253                     classification in the layer.
254    
255            shapeType -- the type of shape that the layer uses
256            """
257    
258            assert(isinstance(clazz, Classification))
259    
260          self.GetView().BeginBatch()          self.GetView().BeginBatch()
261    
262            self.fieldType = clazz.GetFieldType()
263          self.shapeType = shapeType          self.shapeType = shapeType
         self.renderer = ClassRenderer(self.shapeType)  
264    
265          old_tdata = self.tdata          old_len = len(self.tdata)
266    
267          self.tdata = []          self.tdata = []
268    
269          if clazz is None:          #
270              clazz = Classification()          # copy the data out of the classification and into our
271            # array
272            #
273            for p in clazz:
274                np = copy.deepcopy(p)
275                self.__SetRow(-1, np)
276    
277    
278            self.__Modified(-1)
279    
280          p = clazz.GetDefaultData()          self.__NotifyRowChanges(old_len, len(self.tdata))
         np = ClassDataPoint(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()])  
281    
282          self.modified = 0          view = self.GetView()
283            w = view.GetDefaultColSize() * 3 + view.GetDefaultRowLabelSize()
284            h = view.GetDefaultRowSize() * 4 + view.GetDefaultColLabelSize()
285            view.SetDimensions(-1, -1, w, h)
286            view.SetSizeHints(w, h, -1, -1)
287                
288            self.GetView().EndBatch()
289    
290        def __NotifyRowChanges(self, curRows, newRows):
291          #          #
292          # silly message processing for updates to the number of          # silly message processing for updates to the number of
293          # rows and columns          # rows and columns
294          #          #
         curRows = len(old_tdata)  
         newRows = len(self.tdata)  
295          if newRows > curRows:          if newRows > curRows:
296              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
297                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
# Line 123  class ClassTable(wxPyGridTableBase): Line 304  class ClassTable(wxPyGridTableBase):
304                          curRows - newRows)    # how many                          curRows - newRows)    # how many
305              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
306    
307          self.GetView().EndBatch()      def __SetRow(self, row, group):
308            """Set a row's data to that of the group.
309    
310            The table is considered modified after this operation.
311    
312            row -- if row is -1 or greater than the current number of rows
313                   then group is appended to the end.
314            """
315    
316            # either append or replace
317            if row == -1 or row >= self.GetNumberRows():
318                self.tdata.append(group)
319            else:
320                self.tdata[row] = group
321    
322            self.__Modified()
323    
324      def GetColLabelValue(self, col):      def GetColLabelValue(self, col):
325            """Return the label for the given column."""
326          return self.__col_labels[col]          return self.__col_labels[col]
327    
328      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
329          data = self.tdata[row][COL_VISUAL]          """Return the label for the given row."""
330          type = data.GetType()  
331          return self.__row_labels[type]          group = self.tdata[row]
332            if isinstance(group, ClassGroupDefault):   return _("Default")
333            if isinstance(group, ClassGroupSingleton): return _("Singleton")
334            if isinstance(group, ClassGroupRange):     return _("Range")
335            if isinstance(group, ClassGroupMap):       return _("Map")
336    
337            assert(False) # shouldn't get here
338            return _("")
339    
340      def GetNumberRows(self):      def GetNumberRows(self):
341            """Return the number of rows."""
342          return len(self.tdata)          return len(self.tdata)
343    
344      def GetNumberCols(self):      def GetNumberCols(self):
345            """Return the number of columns."""
346          return self.NUM_COLS          return self.NUM_COLS
347    
348      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
349          return 0          """Determine if a cell is empty. This is always false."""
350            return False
351    
352      def GetValue(self, row, col):      def GetValue(self, row, col):
353          return self.GetValueAsCustom(row, col, "")          """Return the object that is used to represent the given
354               cell coordinates. This may not be a string."""
355            return self.GetValueAsCustom(row, col, None)
356    
357      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
358          self.SetValueAsCustom(row, col, "", value)          """Assign 'value' to the cell specified by 'row' and 'col'.
359    
360            The table is considered modified after this operation.
361            """
362    
363            self.SetValueAsCustom(row, col, None, value)
364          self.__Modified()          self.__Modified()
365                
366      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
367          return self.tdata[row][col]          """Return the object that is used to represent the given
368               cell coordinates. This may not be a string.
369    
370            typeName -- unused, but needed to overload wxPyGridTableBase
371            """
372    
373            group = self.tdata[row]
374    
375            if col == COL_SYMBOL:
376                return group.GetProperties()
377    
378            if col == COL_LABEL:
379                return group.GetLabel()
380    
381            # col must be COL_VALUE
382            assert(col == COL_VALUE)
383    
384            if isinstance(group, ClassGroupDefault):
385                return _("DEFAULT")
386            elif isinstance(group, ClassGroupSingleton):
387                return group.GetValue()
388            elif isinstance(group, ClassGroupRange):
389                return _("%s - %s") % (group.GetMin(), group.GetMax())
390    
391            assert(False) # shouldn't get here
392            return None
393    
394      def __ParseInput(self, value):      def __ParseInput(self, value):
395          """Try to determine what kind of input value is          """Try to determine what kind of input value is
396             (a single number or a range)             (string, number, or range)
397    
398            Returns a tuple of length one if there is a single
399            value, or of length two if it is a range.
400          """          """
401    
402          #          type = self.fieldType
403          # first try to take the input as a single number  
404          # if there's an exception try to break it into          if type == FIELDTYPE_STRING:
405          # a range seperated by a '-'. take care to ignore              return (value,)
406          # a leading '-' as that could be for a negative number.          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:
407          # then try to parse the individual parts. if there  
408          # is an exception here, let it pass up to the calling              if type == FIELDTYPE_INT:
409          # function.                  conv = lambda p: int(float(p))
410          #              else:
411          try:                  conv = lambda p: p
412              return (ClassData.POINT, Str2Num(value))  
413          except:              #
414              i = value.find('-')              # first try to take the input as a single number
415              if i == 0:              # if there's an exception try to break it into
416                  i = value.find('-', 1)              # a range seperated by a '-'. take care to ignore
417                # a leading '-' as that could be for a negative number.
418              return (ClassData.RANGE,              # then try to parse the individual parts. if there
419                      Str2Num(value[:i]),              # is an exception here, let it pass up to the calling
420                      Str2Num(value[i+1:]))              # function.
421                #
422                try:
423                    return (conv(Str2Num(value)),)
424                except ValueError:
425                    i = value.find('-')
426                    if i == 0:
427                        i = value.find('-', 1)
428    
429                    return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
430    
431            assert(False) # shouldn't get here
432            return (0,)
433                            
434    
435      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
436          data = self.tdata[row][COL_VISUAL]          """Set the cell specified by 'row' and 'col' to 'value'.
437    
438          if col == COL_VISUAL:          If column represents the value column, the input is parsed
439              self.tdata[row][COL_VISUAL] = value          to determine if a string, number, or range was entered.
440          elif col == COL_VALUE:          A new ClassGroup may be created if the type of data changes.
             if row != 0: # DefaultData row  
                 type = data.GetType()  
441    
442                  if type == ClassData.MAP:          The table is considered modified after this operation.
443                      # something special  
444                      pass          typeName -- unused, but needed to overload wxPyGridTableBase
445                  else: # POINT, RANGE          """
446                      try:  
447                          dataInfo = self.__ParseInput(value)          assert(col >= 0 and col < self.GetNumberCols())
448                      except: pass          assert(row >= 0 and row < self.GetNumberRows())
                         # bad input, ignore the request  
                     else:  
449    
450                          if dataInfo[0] == ClassData.POINT:          group = self.tdata[row]
                             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()  
451    
452            mod = True # assume the data will change
453    
454            if col == COL_SYMBOL:
455                group.SetProperties(value)
456          elif col == COL_LABEL:          elif col == COL_LABEL:
457              data.SetLabel(value)              group.SetLabel(value)
458              self.tdata[row][COL_LABEL] = data.GetLabel()          elif col == COL_VALUE:
459                if isinstance(group, ClassGroupDefault):
460                    # not allowed to modify the default value
461                    pass
462                elif isinstance(group, ClassGroupMap):
463                    # something special
464                    pass
465                else: # SINGLETON, RANGE
466                    try:
467                        dataInfo = self.__ParseInput(value)
468                    except ValueError:
469                        # bad input, ignore the request
470                        mod = False
471                    else:
472    
473                        changed = False
474                        ngroup = group
475                        props = group.GetProperties()
476    
477                        #
478                        # try to update the values, which may include
479                        # changing the underlying group type if the
480                        # group was a singleton and a range was entered
481                        #
482                        if len(dataInfo) == 1:
483                            if not isinstance(group, ClassGroupSingleton):
484                                ngroup = ClassGroupSingleton(prop = props)
485                                changed = True
486                            ngroup.SetValue(dataInfo[0])
487                        elif len(dataInfo) == 2:
488                            if not isinstance(group, ClassGroupRange):
489                                ngroup = ClassGroupRange(prop = props)
490                                changed = True
491                            ngroup.SetRange(dataInfo[0], dataInfo[1])
492                        else:
493                            assert(False)
494                            pass
495    
496                        if changed:
497                            ngroup.SetLabel(group.GetLabel())
498                            self.SetClassGroup(row, ngroup)
499          else:          else:
500              raise ValueError(_("Invalid column request"))              assert(False) # shouldn't be here
501                pass
502    
503          self.__Modified()          if mod:
504                self.__Modified()
505                self.GetView().Refresh()
506    
507      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
508            """Returns the cell attributes"""
509    
510          attr = wxGridCellAttr()          attr = wxGridCellAttr()
511          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)
512    
513          if col == COL_VISUAL:          if col == COL_SYMBOL:
514                # we need to create a new renderer each time, because
515                # SetRenderer takes control of the parameter
516              attr.SetRenderer(ClassRenderer(self.shapeType))              attr.SetRenderer(ClassRenderer(self.shapeType))
517              attr.SetReadOnly()              attr.SetReadOnly()
518    
519          return attr          return attr
520    
521      def GetClassData(self, row):      def GetClassGroup(self, row):
522          return self.tdata[row][COL_VISUAL]          """Return the ClassGroup object representing row 'row'."""
523    
524      def __Modified(self):          return self.tdata[row] # self.GetValueAsCustom(row, COL_SYMBOL, None)
525          self.modified = 1  
526        def SetClassGroup(self, row, group):
527            self.__SetRow(row, group)
528            self.GetView().Refresh()
529    
530        def __Modified(self, mod = True):
531            """Adjust the modified flag.
532    
533            mod -- if -1 set the modified flag to False, otherwise perform
534                   an 'or' operation with the current value of the flag and
535                   'mod'
536            """
537    
538            if mod == -1:
539                self.modified = False
540            else:
541                self.modified = mod or self.modified
542    
543      def IsModified(self):      def IsModified(self):
544            """True if this table is considered modified."""
545          return self.modified          return self.modified
546    
547      def AddNewDataRow(self):      def DeleteRows(self, pos, numRows = 1):
548          np = ClassDataPoint()          """Deletes 'numRows' beginning at row 'pos'.
549          self.tdata.append([np, np.GetValue(), np.GetLabel()])  
550          msg = wxGridTableMessage(self, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1)          The row representing the default group is not removed.
         self.GetView().ProcessTableMessage(msg)  
         self.GetView().BeginBatch()  
         self.GetView().EndBatch()  
551    
552  class Classifier(wxDialog):          The table is considered modified if any rows are removed.
553            """
554    
555            assert(pos >= 0)
556            old_len = len(self.tdata)
557            for row in range(pos, pos - numRows, -1):
558                group = self.GetClassGroup(row)
559                if not isinstance(group, ClassGroupDefault):
560                    self.tdata.pop(row)
561                    self.__Modified()
562            
563      def __init__(self, parent, layer):          if self.IsModified():
564          wxDialog.__init__(self, parent, -1, _("Classify"),              self.__NotifyRowChanges(old_len, len(self.tdata))
565                            style = wxRESIZE_BORDER)  
566        def AppendRows(self, numRows = 1):
567            """Append 'numRows' empty rows to the end of the table.
568    
569            The table is considered modified if any rows are appended.
570            """
571    
572            old_len = len(self.tdata)
573            for i in range(numRows):
574                np = ClassGroupSingleton()
575                self.__SetRow(-1, np)
576    
577            if self.IsModified():
578                self.__NotifyRowChanges(old_len, len(self.tdata))
579    
580    
581    class Classifier(NonModalDialog):
582        
583        def __init__(self, parent, interactor, name, layer):
584            NonModalDialog.__init__(self, parent, interactor, name,
585                                    _("Classifier: %s") % layer.Title())
586    
587          self.layer = layer          self.layer = layer
588    
589            self.originalClass = self.layer.GetClassification()
590            field = self.originalClass.GetField()
591            fieldType = self.originalClass.GetFieldType()
592    
593          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
594    
595          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),          #topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),
596              0, wxALIGN_LEFT | wxBOTTOM, 4)              #0, wxALIGN_LEFT | wxALL, 4)
597          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),          topBox.Add(wxStaticText(self, -1,
598              0, wxALIGN_LEFT | wxBOTTOM, 4)                                  _("Layer Type: %s") % layer.ShapeType()),
599                0, wxALIGN_LEFT | wxALL, 4)
600    
         propertyBox = wxBoxSizer(wxHORIZONTAL)  
         propertyBox.Add(wxStaticText(self, -1, _("Property: ")),  
             0, wxALIGN_CENTER | wxALL, 4)  
601    
602          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          #
603            # make field combo box
604            #
605            self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",
606                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
607    
608          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
609          self.__cur_prop = -1          # just assume the first field in case one hasn't been
610          field = layer.GetClassification().GetField()          # specified in the file.
611            self.__cur_field = 0
612    
613            self.fields.Append("<None>")
614            self.fields.SetClientData(0, None)
615    
616          for i in range(self.num_cols):          for i in range(self.num_cols):
617              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
618                self.fields.Append(name)
619    
620              if name == field:              if name == field:
621                  self.__cur_prop = i                  self.__cur_field = i + 1
622              self.properties.Append(name)                  self.fields.SetClientData(i + 1, self.originalClass)
623              self.properties.SetClientData(i, None)              else:
624                    self.fields.SetClientData(i + 1, None)
625          self.properties.SetSelection(self.__cur_prop)  
626          propertyBox.Add(self.properties, 1, wxGROW|wxALL, 0)  
627          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)          #
628            #
629            #
630    
631            self.fieldTypeText = wxStaticText(self, -1, "")
632            self.__SetFieldTypeText(self.__cur_field)
633    
634            topBox.Add(self.fieldTypeText, 0, wxALIGN_LEFT | wxALL, 4)
635            #self.fieldTypeText.SetLabel("asdfadsfs")
636    
637            propertyBox = wxBoxSizer(wxHORIZONTAL)
638            propertyBox.Add(wxStaticText(self, -1, _("Field: ")),
639                0, wxALIGN_CENTER | wxALL, 4)
640            propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
641            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
642    
643          topBox.Add(propertyBox, 0, wxGROW, 4)          topBox.Add(propertyBox, 0, wxGROW, 4)
644    
# Line 293  class Classifier(wxDialog): Line 647  class Classifier(wxDialog):
647          #          #
648    
649          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
650          self.classGrid = ClassGrid(self, layer)  
651            self.classGrid = ClassGrid(self)
652            self.__SetGridTable(self.__cur_field)
653    
654          controlBox.Add(self.classGrid, 1, wxGROW, 0)          controlBox.Add(self.classGrid, 1, wxGROW, 0)
655    
656          controlButtonBox = wxBoxSizer(wxVERTICAL)          controlButtonBox = wxBoxSizer(wxVERTICAL)
657          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_ADD,  
658              _("Add")), 0, wxGROW | wxALL, 4)          #
659          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,          # Control buttons:
660              _("Generate Ranges")), 0, wxGROW | wxALL, 4)          #
661            self.controlButtons = []
662    
663            button = wxButton(self, ID_CLASSIFY_ADD, _("Add"))
664            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
665            self.controlButtons.append(button)
666    
667            #button = wxButton(self, ID_CLASSIFY_GENRANGE, _("Generate Ranges"))
668            #controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
669            #self.controlButtons.append(button)
670    
671            button = wxButton(self, ID_CLASSIFY_MOVEUP, _("Move Up"))
672            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
673            self.controlButtons.append(button)
674    
675            button = wxButton(self, ID_CLASSIFY_MOVEDOWN, _("Move Down"))
676            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
677            self.controlButtons.append(button)
678    
679            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
680    
681            button = wxButton(self, ID_CLASSIFY_REMOVE, _("Remove"))
682            controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
683            self.controlButtons.append(button)
684    
685          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          controlBox.Add(controlButtonBox, 0, wxGROW, 10)
686          topBox.Add(controlBox, 1, wxGROW, 10)          topBox.Add(controlBox, 1, wxGROW, 10)
687    
688          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)          EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)
689          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)          EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)
690          EVT_GRID_CELL_LEFT_DCLICK(self.classGrid, self.OnCellDClick)          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self._OnGenRange)
691            EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)
692            EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)
693    
         #  
         # Control buttons:  
         #  
694          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
695          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),
696                        0, wxALL, 4)                        0, wxALL, 4)
697            buttonBox.Add(60, 20, 0, wxALL, 4)
698            buttonBox.Add(wxButton(self, ID_CLASSIFY_APPLY, _("Apply")),
699                          0, wxALL, 4)
700            buttonBox.Add(60, 20, 0, wxALL, 4)
701          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),
702                        0, wxALL, 4)                        0, wxALL, 4)
703          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
704    
705          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)
706          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_CLASSIFY_APPLY, self._OnApply)
707            EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)
708    
709            self.fields.SetSelection(self.__cur_field)
710            self.__SelectField(self.__cur_field)
711    
712          self.SetAutoLayout(true)          self.SetAutoLayout(true)
713          self.SetSizer(topBox)          self.SetSizer(topBox)
714          topBox.Fit(self)          topBox.Fit(self)
715          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
716    
     def __BuildClassification(self, prop):  
717    
718          clazz = Classification()      def __BuildClassification(self, fieldIndex):
         clazz.SetField(self.properties.GetStringSelection())  
719    
720          numRows = self.classGrid.GetNumberRows()          numRows = self.classGrid.GetNumberRows()
721            assert(numRows > 0) # there should always be a default row
722    
723          if numRows > 0:          clazz = Classification()
724              table = self.classGrid.GetTable()          if fieldIndex == 0:
725              clazz.SetDefaultData(table.GetClassData(0))              fieldName = None
726                fieldType = None
727            else:
728                fieldName = self.fields.GetString(fieldIndex)
729                fieldType = self.layer.GetFieldType(fieldName)
730    
731            clazz.SetField(fieldName)
732            clazz.SetFieldType(fieldType)
733    
734    
735            table = self.classGrid.GetTable()
736            clazz.SetDefaultGroup(table.GetClassGroup(0))
737    
738              for i in range(1, numRows):          for i in range(1, numRows):
739                  clazz.AddClassData(table.GetClassData(i))              clazz.AddGroup(table.GetClassGroup(i))
740    
741          return clazz          return clazz
742    
743      def OnPropertySelect(self, event):      def __SetGridTable(self, fieldIndex):
         self.properties.SetClientData(  
             self.__cur_prop, self.__BuildClassification(self.__cur_prop))  
744    
745          self.__cur_prop = self.properties.GetSelection()          clazz = self.fields.GetClientData(fieldIndex)
746          clazz = self.properties.GetClientData(self.__cur_prop)  
747          table = self.classGrid.GetTable()          if clazz is None:
748                clazz = Classification()
749                clazz.SetDefaultGroup(
750                    ClassGroupDefault(
751                        self.layer.GetClassification().
752                                   GetDefaultGroup().GetProperties()))
753    
754                fieldName = self.fields.GetString(fieldIndex)
755                fieldType = self.layer.GetFieldType(fieldName)
756                clazz.SetFieldType(fieldType)
757                    
758            self.classGrid.CreateTable(clazz, self.layer.ShapeType())
759    
760    
761    
762        type2string = {None:             "None",
763                       FIELDTYPE_STRING: "Text",
764                       FIELDTYPE_INT:    "Integer",
765                       FIELDTYPE_DOUBLE: "Decimal"}
766    
767        def __SetFieldTypeText(self, fieldIndex):
768            fieldName = self.fields.GetString(fieldIndex)
769            fieldType = self.layer.GetFieldType(fieldName)
770    
771            assert(Classifier.type2string.has_key(fieldType))
772    
773            text = Classifier.type2string[fieldType]
774    
775            self.fieldTypeText.SetLabel(_("Field Type: %s") % text)
776    
777        def __SelectField(self, fieldIndex):
778            clazz = self.__BuildClassification(fieldIndex)
779            self.fields.SetClientData(fieldIndex, clazz)
780    
781          table.Reset(clazz, self.layer.ShapeType())          self.__cur_field = self.fields.GetSelection()
782            self.__SetGridTable(fieldIndex)
783    
784      def OnOK(self, event):          enabled = fieldIndex != 0
785    
786            for b in self.controlButtons:
787                b.Enable(enabled)
788    
789            self.__SetFieldTypeText(fieldIndex)
790    
791    
792        def _OnFieldSelect(self, event):
793            self.__SelectField(self.__cur_field)
794    
795        def _OnApply(self, event):
796          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
797             it to the layer.             it to the layer.
798          """          """
799    
800          clazz = self.properties.GetClientData(self.__cur_prop)          clazz = self.fields.GetClientData(self.__cur_field)
801    
802          #          #
803          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
804          # to begin with or it has been modified          # to begin with or it has been modified
805          #          #
806          if clazz is None or self.classGrid.GetTable().IsModified():          if clazz is None or self.classGrid.GetTable().IsModified():
807              clazz = self.__BuildClassification(self.__cur_prop)              clazz = self.__BuildClassification(self.__cur_field)
   
         clazz.SetLayer(self.layer)  
808    
809          self.layer.SetClassification(clazz)          self.layer.SetClassification(clazz)
810    
811          self.EndModal(wxID_OK)      def _OnOK(self, event):
812            self._OnApply(event)
813            self.OnClose(event)
814    
815        def _OnCancel(self, event):
816            """The layer's current classification stays the same."""
817            self.layer.SetClassification(self.originalClass)
818            self.OnClose(event)
819    
820        def _OnAdd(self, event):
821            self.classGrid.AppendRows()
822    
823        def _OnRemove(self, event):
824            self.classGrid.DeleteSelectedRows()
825    
826        def _OnGenRange(self, event):
827            print "Classifier._OnGenRange()"
828    
829        def _OnMoveUp(self, event):
830            sel = self.classGrid.GetCurrentSelection()
831    
832            if len(sel) == 1:
833                i = sel[0]
834                if i > 1:
835                    table = self.classGrid.GetTable()
836                    x = table.GetClassGroup(i - 1)
837                    y = table.GetClassGroup(i)
838                    table.SetClassGroup(i - 1, y)
839                    table.SetClassGroup(i, x)
840                    self.classGrid.ClearSelection()
841                    self.classGrid.SelectRow(i - 1)
842    
843      def OnCancel(self, event):      def _OnMoveDown(self, event):
844          """Do nothing. The layer's current classification stays the same."""          sel = self.classGrid.GetCurrentSelection()
         self.EndModal(wxID_CANCEL)  
   
   
     def OnAdd(self, event):  
         self.classGrid.GetTable().AddNewDataRow()  
         print "Classifier.OnAdd()"  
845    
846      def OnGenRange(self, event):          if len(sel) == 1:
847          print "Classifier.OnGenRange()"              i = sel[0]
848                table = self.classGrid.GetTable()
849      def OnCellDClick(self, event):              if 0 < i < table.GetNumberRows() - 1:
850          r = event.GetRow()                  x = table.GetClassGroup(i)
851          c = event.GetCol()                  y = table.GetClassGroup(i + 1)
852          if c == COL_VISUAL:                  table.SetClassGroup(i, y)
853              prop = self.classGrid.GetTable().GetValueAsCustom(r, c, None)                  table.SetClassGroup(i + 1, x)
854              propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())                  self.classGrid.ClearSelection()
855              if propDlg.ShowModal() == wxID_OK:                  self.classGrid.SelectRow(i + 1)
                 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()  
856    
857    
858  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
859  ID_SELPROP_CANCEL = 4002  ID_SELPROP_CANCEL = 4002
860  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL = 4002
861    ID_SELPROP_PREVIEW = 4003
862    ID_SELPROP_STROKECLR = 4004
863    ID_SELPROP_FILLCLR = 4005
864    ID_SELPROP_STROKECLRTRANS = 4006
865    ID_SELPROP_FILLCLRTRANS = 4007
866    
867  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
868    
# Line 412  class SelectPropertiesDialog(wxDialog): Line 870  class SelectPropertiesDialog(wxDialog):
870          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
871                            style = wxRESIZE_BORDER)                            style = wxRESIZE_BORDER)
872    
873            self.prop = ClassGroupProperties(prop)
874    
875          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
   
         self.prop = ClassData(classData = prop)  
876    
877          topBox.Add(wxStaticText(self, -1, _("Stroke Color: ")),          itemBox = wxBoxSizer(wxHORIZONTAL)
878              0, wxALIGN_LEFT | wxALL, 4)  
879            # preview box
880            previewBox = wxBoxSizer(wxVERTICAL)
881            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
882                0, wxALIGN_LEFT | wxALL, 4)
883            self.previewer = ClassDataPreviewer(None, self.prop, shapeType,
884                                                self, ID_SELPROP_PREVIEW, (40, 40))
885            previewBox.Add(self.previewer, 1, wxGROW, 15)
886    
887            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
888    
889            # control box
890            ctrlBox = wxBoxSizer(wxVERTICAL)
891    
892            lineColorBox = wxBoxSizer(wxHORIZONTAL)
893            lineColorBox.Add(
894                wxButton(self, ID_SELPROP_STROKECLR, "Change Line Color"),
895                1, wxALL | wxGROW, 4)
896            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
897    
898            lineColorBox.Add(
899                wxButton(self, ID_SELPROP_STROKECLRTRANS, "Transparent"),
900                1, wxALL | wxGROW, 4)
901            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
902                       self._OnChangeLineColorTrans)
903    
904            ctrlBox.Add(lineColorBox, 0,
905                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
906    
907            if shapeType != SHAPETYPE_ARC:
908                fillColorBox = wxBoxSizer(wxHORIZONTAL)
909                fillColorBox.Add(
910                    wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),
911                    1, wxALL | wxGROW, 4)
912                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
913                fillColorBox.Add(
914                    wxButton(self, ID_SELPROP_FILLCLRTRANS, "Transparent"),
915                    1, wxALL | wxGROW, 4)
916                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
917                           self._OnChangeFillColorTrans)
918                ctrlBox.Add(fillColorBox, 0,
919                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
920    
921          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
922          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
923                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxBOTTOM, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
924          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
925                                     min=1, max=10,                                     min=1, max=10,
926                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetLineWidth()),
927                                     initial=prop.GetStrokeWidth())                                     initial=prop.GetLineWidth())
928    
929          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
930    
931          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
932    
933          topBox.Add(spinBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
934            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
935          if shapeType != SHAPETYPE_ARC:          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
             topBox.Add(wxStaticText(self, -1, _("Fill Color: ")),  
                 0, wxALIGN_LEFT | wxBOTTOM, 4)  
936    
937          #          #
938          # Control buttons:          # Control buttons:
939          #          #
940          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
941          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
942                        0, wxALL, 4)                        0, wxALL, 4)
943          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
944                        0, wxALL, 4)                        0, wxALL, 4)
945          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
946                                                                                                                                                                    
947          EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
948          EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
949                                                                                                                                                                    
950          self.SetAutoLayout(true)          self.SetAutoLayout(true)
951          self.SetSizer(topBox)          self.SetSizer(topBox)
952          topBox.Fit(self)          topBox.Fit(self)
953          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
954    
955      def OnOK(self, event):      def _OnOK(self, event):
956          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
957    
958      def OnCancel(self, event):      def _OnCancel(self, event):
959          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
960    
961      def OnSpin(self, event):      def _OnSpin(self, event):
962          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
963            self.previewer.Refresh()
964    
965        def __GetColor(self, cur):
966            dialog = wxColourDialog(self)
967            dialog.GetColourData().SetColour(Color2wxColour(cur))
968            ret = None
969            if dialog.ShowModal() == wxID_OK:
970                ret = wxColour2Color(dialog.GetColourData().GetColour())
971    
972            dialog.Destroy()
973    
974            return ret
975            
976        def _OnChangeLineColor(self, event):
977            clr = self.__GetColor(self.prop.GetLineColor())
978            if clr is not None:
979                self.prop.SetLineColor(clr)
980            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
981    
982        def _OnChangeLineColorTrans(self, event):
983            self.prop.SetLineColor(Color.None)
984            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
985            
986        def _OnChangeFillColor(self, event):
987            clr = self.__GetColor(self.prop.GetFill())
988            if clr is not None:
989                self.prop.SetFill(clr)
990            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
991    
992        def _OnChangeFillColorTrans(self, event):
993            self.prop.SetFill(Color.None)
994            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
995    
996      def GetClassData(self):      def GetClassGroupProperties(self):
997          return self.prop          return self.prop
998    
999    
1000  class ClassDataPreviewer:  class ClassDataPreviewer(wxWindow):
1001    
1002        def __init__(self, rect, prop, shapeType,
1003                           parent = None, id = -1, size = wxDefaultSize):
1004            if parent is not None:
1005                wxWindow.__init__(self, parent, id, size=size)
1006                EVT_PAINT(self, self._OnPaint)
1007    
1008            self.rect = rect
1009            self.prop = prop
1010            self.shapeType = shapeType
1011    
1012        def _OnPaint(self, event):
1013            dc = wxPaintDC(self)
1014    
1015            # XXX: this doesn't seem to be having an effect:
1016            dc.DestroyClippingRegion()
1017    
1018      def Draw(self, dc, rect, data, shapeType):          self.Draw(dc, None)
1019    
1020          stroke = data.GetStroke()      def Draw(self, dc, rect, prop = None, shapeType = None):
1021    
1022            if prop is None: prop = self.prop
1023            if shapeType is None: shapeType = self.shapeType
1024    
1025            if rect is None:
1026                x = y = 0
1027                w, h = self.GetClientSizeTuple()
1028            else:
1029                x = rect.GetX()
1030                y = rect.GetY()
1031                w = rect.GetWidth()
1032                h = rect.GetHeight()
1033    
1034            stroke = prop.GetLineColor()
1035          if stroke is Color.None:          if stroke is Color.None:
1036              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1037          else:          else:
1038              pen = wxPen(wxColour(stroke.red * 255,              pen = wxPen(Color2wxColour(stroke),
1039                                   stroke.green * 255,                          prop.GetLineWidth(),
                                  stroke.blue * 255),  
                         data.GetStrokeWidth(),  
1040                          wxSOLID)                          wxSOLID)
1041    
1042          stroke = data.GetFill()          stroke = prop.GetFill()
1043          if stroke is Color.None:          if stroke is Color.None:
1044              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1045          else:          else:
1046              brush = wxBrush(wxColour(stroke.red * 255,              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
                                      stroke.green * 255,  
                                      stroke.blue * 255), wxSOLID)  
1047    
1048          dc.SetPen(pen)          dc.SetPen(pen)
1049          dc.SetBrush(brush)          dc.SetBrush(brush)
1050    
1051          if shapeType == SHAPETYPE_ARC:          if shapeType == SHAPETYPE_ARC:
1052              dc.DrawSpline([wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight()),              dc.DrawSpline([wxPoint(x, y + h),
1053                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w/2, y + h/4),
1054                                     rect.GetY() + rect.GetHeight()/4),                             wxPoint(x + w/2, y + h/4*3),
1055                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w, y)])
                                    rect.GetY() + rect.GetHeight()/4*3),  
                            wxPoint(rect.GetX() + rect.GetWidth(), rect.GetY())])  
1056    
1057          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT or \
1058               shapeType == SHAPETYPE_POLYGON:               shapeType == SHAPETYPE_POLYGON:
1059    
1060              dc.DrawCircle(rect.GetX() + rect.GetWidth()/2,              dc.DrawCircle(x + w/2, y + h/2,
1061                            rect.GetY() + rect.GetHeight()/2,                            (min(w, h) - prop.GetLineWidth())/2)
                           (min(rect.GetWidth(), rect.GetHeight())  
                            - data.GetStrokeWidth())/2)  
1062    
1063  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1064    
1065      def __init__(self, shapeType):      def __init__(self, shapeType):
1066          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
1067          self.shapeType = shapeType          self.previewer = ClassDataPreviewer(None, None, shapeType)
1068    
1069      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1070          data = grid.GetTable().GetValueAsCustom(row, col, "")          data = grid.GetTable().GetClassGroup(row)
   
1071    
1072          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1073                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 526  class ClassRenderer(wxPyGridCellRenderer Line 1076  class ClassRenderer(wxPyGridCellRenderer
1076          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
1077                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1078    
1079          ClassDataPreviewer().Draw(dc, rect, data, self.shapeType)          if not isinstance(data, ClassGroupMap):
1080                self.previewer.Draw(dc, rect, data.GetProperties())
1081    
1082          if isSelected:          if isSelected:
1083              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.496

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26