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

Legend:
Removed from v.430  
changed lines
  Added in v.549

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26