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

Legend:
Removed from v.398  
changed lines
  Added in v.1058

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26