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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26