/[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 372 by jonathan, Mon Jan 27 13:50:58 2003 UTC revision 455 by bh, Tue Mar 4 11:32:20 2003 UTC
# Line 9  Line 9 
9    
10  __version__ = "$Revision$"  __version__ = "$Revision$"
11    
12    import copy
13    
14  from wxPython.wx import *  from wxPython.wx import *
15  from wxPython.grid import *  from wxPython.grid import *
16    
17    from Thuban import _
18    from Thuban.common import *
19    from Thuban.UI.common import *
20    
21    from Thuban.Model.classification import *
22    
23    from Thuban.Model.color import Color
24    
25    from Thuban.Model.layer import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
26    
27  ID_PROPERTY_SELECT = 4010  ID_PROPERTY_SELECT = 4010
28  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
29    
30  ID_CLASSIFY_OK = 4001  ID_CLASSIFY_OK = 4001
31  ID_CLASSIFY_CANCEL = 4002  ID_CLASSIFY_CANCEL = 4002
32    ID_CLASSIFY_ADD = 4003
33    ID_CLASSIFY_GENRANGE = 4004
34    ID_CLASSIFY_REMOVE = 4005
35    
36    COL_VISUAL = 0
37    COL_VALUE  = 1
38    COL_LABEL  = 2
39    
40    FIELD_CLASS = 0
41    FIELD_TYPE = 1
42    FIELD_NAME = 2
43    
44    FIELD_TYPE_STRING = "string"
45    FIELD_TYPE_INT = "int"
46    FIELD_TYPE_DOUBLE = "double"
47    
48    #
49    # this is a silly work around to ensure that the table that is
50    # passed into SetTable is the same that is returned by GetTable
51    #
52    import weakref
53    class ClassGrid(wxGrid):
54    
55        def __init__(self, parent, layer, fieldData):
56            wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
57            self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
58            self.SetSelectionMode(wxGrid.wxGridSelectRows)
59            EVT_GRID_CELL_LEFT_DCLICK(self, self.OnCellDClick)
60    
61            EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
62            EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
63    
64            self.layer = layer
65            self.currentSelection = []
66    
67        def GetCurrentSelection(self):
68            sel = copy.copy(self.currentSelection)
69            sel.sort()
70            return sel
71    
72        def SetCellRenderer(self, row, col):
73            raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
74    
75        def SetTable(self, object, *attributes):
76            self.tableRef = weakref.ref(object)
77            return wxGrid.SetTable(self, object, *attributes)
78    
79        def GetTable(self):
80            return self.tableRef()
81    
82        def DeleteSelectedRows(self):
83            sel = self.GetCurrentSelection()
84    
85            if len(sel) == 0: return
86            if len(sel) == 1:
87                group = self.GetTable().GetValueAsCustom(sel[0], COL_VISUAL, None)
88                if isinstance(group, ClassGroupDefault):
89                    wxMessageDialog(self,
90                                    "The Default group cannot be removed.",
91                                    style = wxOK | wxICON_EXCLAMATION).ShowModal()
92                    return
93            
94            self.ClearSelection()
95    
96            sel.reverse()
97            table = self.GetTable()
98            for row in sel:
99                table.DeleteRows(row)
100    
101            if len(sel) == 1:
102                r = sel[0]
103                if r > self.GetNumberRows() - 1:
104                    r = self.GetNumberRows() - 1
105                self.SelectRow(r)
106            
107    #
108    # XXX: This isn't working, and there is no way to deselect rows wxPython!
109    #
110    #   def DeselectRow(self, row):
111    #       self.ProcessEvent(
112    #           wxGridRangeSelectEvent(-1,
113    #                                  wxEVT_GRID_RANGE_SELECT,
114    #                                  self,
115    #                                  (row, row), (row, row),
116    #                                  sel = False))
117    
118        def OnCellDClick(self, event):
119            r = event.GetRow()
120            c = event.GetCol()
121            if c == COL_VISUAL:
122                # XXX: getting the properties is only possible with non-Maps!!!
123                group = self.GetTable().GetValueAsCustom(r, c, None)
124                prop = group.GetProperties()
125                propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
126                if propDlg.ShowModal() == wxID_OK:
127                    new_prop = propDlg.GetClassGroupProperties()
128                    prop.SetStroke(new_prop.GetStroke())
129                    prop.SetStrokeWidth(new_prop.GetStrokeWidth())
130                    prop.SetFill(new_prop.GetFill())
131                    self.Refresh()
132                propDlg.Destroy()
133    
134        #
135        # _OnSelectedRange() and _OnSelectedCell() were borrowed
136        # from http://wiki.wxpython.org
137        #
138        def _OnSelectedRange(self, event):
139            """Internal update to the selection tracking list"""
140            if event.Selecting():
141                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
142                    if index not in self.currentSelection:
143                        self.currentSelection.append( index )
144            else:
145                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
146                    while index in self.currentSelection:
147                        self.currentSelection.remove( index )
148            #self.ConfigureForSelection()
149    
150            event.Skip()
151    
152        def _OnSelectedCell( self, event ):
153            """Internal update to the selection tracking list"""
154            self.currentSelection = [ event.GetRow() ]
155            #self.ConfigureForSelection()
156            event.Skip()
157    
158    class ClassTable(wxPyGridTableBase):
159    
160        NUM_COLS = 3
161    
162        __col_labels = [_("Symbol"), _("Value"), _("Label")]
163    
164        def __init__(self, fieldData, shapeType, view = None):
165            wxPyGridTableBase.__init__(self)
166            self.SetView(view)
167            self.tdata = []
168    
169            self.Reset(fieldData, shapeType)
170    
171        def Reset(self, fieldData, shapeType):
172    
173            self.GetView().BeginBatch()
174    
175            self.fieldData = fieldData
176            self.shapeType = shapeType
177            self.renderer = ClassRenderer(self.shapeType)
178    
179            old_len = len(self.tdata)
180    
181            self.tdata = []
182    
183            clazz = fieldData[FIELD_CLASS]
184            if clazz is None:
185                clazz = Classification()
186    
187    #       p = clazz.GetDefaultGroup()
188    #       np = ClassDataDefault(classData = p)
189    #       self.tdata.append([np, 'DEFAULT', np.GetLabel()])
190    
191    #       for p in clazz.points.values():
192    #           np = ClassDataPoint(p.GetValue(), classData = p)
193    #           self.tdata.append([np, np.GetValue(), np.GetLabel()])
194    
195    #       for p in clazz.ranges:
196    #           np = ClassDataRange(p.GetMin(), p.GetMax(), classData = p)
197    #           self.tdata.append([np,
198    #                              '%s - %s' % (np.GetMin(), np.GetMax()),
199    #                              np.GetLabel()])
200    
201            i = 0
202            for p in clazz:
203                np = copy.copy(p)
204                self.__SetRow(i, np)
205                i += 1
206    
207    
208            self.modified = 0
209    
210            self.__NotifyRowChanges(old_len, len(self.tdata))
211            self.GetView().EndBatch()
212    
213        def __NotifyRowChanges(self, curRows, newRows):
214            #
215            # silly message processing for updates to the number of
216            # rows and columns
217            #
218            if newRows > curRows:
219                msg = wxGridTableMessage(self,
220                            wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
221                            newRows - curRows)    # how many
222                self.GetView().ProcessTableMessage(msg)
223            elif newRows < curRows:
224                msg = wxGridTableMessage(self,
225                            wxGRIDTABLE_NOTIFY_ROWS_DELETED,
226                            curRows - newRows,    # position
227                            curRows - newRows)    # how many
228                self.GetView().ProcessTableMessage(msg)
229    
230    
231        def __SetRow(self, row, group):
232    
233            if isinstance(group, ClassGroupDefault):
234                data = [group, _('DEFAULT'), group.GetLabel()]
235            elif isinstance(group, ClassGroupSingleton):
236                data = [group, group.GetValue(), group.GetLabel()]
237            elif isinstance(group, ClassGroupRange):
238                data = [group,
239                        _('%s - %s') % (group.GetMin(), group.GetMax()),
240                        group.GetLabel()]
241    
242            if row >= len(self.tdata):
243                self.tdata.append(data)
244            else:
245                self.tdata[row] = data
246    
247        def GetColLabelValue(self, col):
248            return self.__col_labels[col]
249    
250        def GetRowLabelValue(self, row):
251            data = self.tdata[row][COL_VISUAL]
252            if isinstance(data, ClassGroupDefault):   return _("Default")
253            if isinstance(data, ClassGroupSingleton): return _("Singleton")
254            if isinstance(data, ClassGroupRange):     return _("Range")
255            if isinstance(data, ClassGroupMap):       return _("Map")
256    
257        def GetNumberRows(self):
258            return len(self.tdata)
259    
260        def GetNumberCols(self):
261            return self.NUM_COLS
262    
263        def IsEmptyCell(self, row, col):
264            return 0
265    
266        def GetValue(self, row, col):
267            return self.GetValueAsCustom(row, col, "")
268    
269        def SetValue(self, row, col, value):
270            self.SetValueAsCustom(row, col, "", value)
271            self.__Modified()
272          
273        def GetValueAsCustom(self, row, col, typeName):
274            return self.tdata[row][col]
275    
276        def __ParseInput(self, value):
277            """Try to determine what kind of input value is
278               (a single number or a range)
279            """
280    
281            type = self.fieldData[FIELD_TYPE]
282    
283            if type == FIELD_TYPE_STRING:
284                return (value,)
285            elif type == FIELD_TYPE_INT or type == FIELD_TYPE_DOUBLE:
286    
287                #
288                # first try to take the input as a single number
289                # if there's an exception try to break it into
290                # a range seperated by a '-'. take care to ignore
291                # a leading '-' as that could be for a negative number.
292                # then try to parse the individual parts. if there
293                # is an exception here, let it pass up to the calling
294                # function.
295                #
296                try:
297                    return (Str2Num(value),)
298                except:
299                    i = value.find('-')
300                    if i == 0:
301                        i = value.find('-', 1)
302    
303                    return (Str2Num(value[:i]), Str2Num(value[i+1:]))
304                
305    
306        def SetValueAsCustom(self, row, col, typeName, value):
307            group = self.tdata[row][COL_VISUAL]
308    
309            if col == COL_VISUAL:
310                self.tdata[row][COL_VISUAL] = value
311            elif col == COL_VALUE:
312                if isinstance(group, ClassGroupDefault):
313                    # not allowed to modify the default value
314                    pass
315                elif isinstance(group, ClassGroupMap):
316                    # something special
317                    pass
318                else: # SINGLETON, RANGE
319                    try:
320                        dataInfo = self.__ParseInput(value)
321                    except:
322                        # bad input, ignore the request
323                        pass
324                    else:
325    
326                        ngroup = group
327                        props = group.GetProperties()
328                        if len(dataInfo) == 1:
329                            if not isinstance(group, ClassGroupSingleton):
330                                ngroup = ClassGroupSingleton(prop = props)
331                            ngroup.SetValue(dataInfo[0])
332                        elif len(dataInfo) == 2:
333                            if not isinstance(group, ClassGroupRange):
334                                ngroup = ClassGroupRange(prop = props)
335                            ngroup.SetRange(dataInfo[0], dataInfo[1])
336                        else:
337                            assert(False)
338    
339                        ngroup.SetLabel(group.GetLabel())
340                        self.__SetRow(row, ngroup)
341    
342                        self.GetView().Refresh()
343    
344            elif col == COL_LABEL:
345                group.SetLabel(value)
346                self.tdata[row][COL_LABEL] = group.GetLabel()
347            else:
348                raise ValueError(_("Invalid column request"))
349    
350            self.__Modified()
351    
352        def GetAttr(self, row, col, someExtraParameter):
353            attr = wxGridCellAttr()
354            #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)
355    
356            if col == COL_VISUAL:
357                attr.SetRenderer(ClassRenderer(self.shapeType))
358                attr.SetReadOnly()
359    
360            return attr
361    
362        def GetClassGroup(self, row):
363            return self.tdata[row][COL_VISUAL]
364    
365        def __Modified(self):
366            self.modified = 1
367    
368        def IsModified(self):
369            return self.modified
370    
371        def DeleteRows(self, pos, numRows = 1):
372            assert(pos >= 0)
373            old_len = len(self.tdata)
374            for row in range(pos, pos - numRows, -1):
375                group = self.GetValueAsCustom(row, COL_VISUAL, None)
376                if not isinstance(group, ClassGroupDefault):
377                    self.tdata.pop(row)
378                    self.__Modified()
379        
380            if self.IsModified():
381                self.__NotifyRowChanges(old_len, len(self.tdata))
382    
383        def AppendRows(self, numRows = 1):
384            old_len = len(self.tdata)
385            for i in range(numRows):
386                np = ClassGroupSingleton()
387                self.tdata.append([np, np.GetValue(), np.GetLabel()])
388                self.__Modified()
389    
390            if self.IsModified():
391                self.__NotifyRowChanges(old_len, len(self.tdata))
392    
393    
394  class Classifier(wxDialog):  class Classifier(wxDialog):
395            
396      def __init__(self, parent, layer):      def __init__(self, parent, layer):
397          wxDialog.__init__(self, parent, -1, "Classify",          wxDialog.__init__(self, parent, -1, _("Classify"),
398                            style = wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
399    
400    
401            self.layer = layer
402    
403          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
404    
405            topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),
406                0, wxALIGN_LEFT | wxALL, 4)
407            topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),
408                0, wxALIGN_LEFT | wxALL, 4)
409    
410          propertyBox = wxBoxSizer(wxHORIZONTAL)          propertyBox = wxBoxSizer(wxHORIZONTAL)
411          propertyBox.Add(wxStaticText(self, -1, "Property"),          propertyBox.Add(wxStaticText(self, -1, _("Field: ")),
412              0, wxALIGN_CENTER | wxALL, 4)              0, wxALIGN_CENTER | wxALL, 4)
413    
414          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",
415                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
416    
417          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
418            # just assume the first field in case one hasn't been
419            # specified in the file.
420            self.__cur_field = 0
421            clazz = layer.GetClassification()
422            field = clazz.GetField()
423          for i in range(self.num_cols):          for i in range(self.num_cols):
424              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
425              self.properties.Append(name)              self.fields.Append(name)
426    
427                if name == field:
428                    self.__cur_field = i
429                    self.fields.SetClientData(i, [clazz, type, name, len, decc])
430                else:
431                    self.fields.SetClientData(i, [None, type, name, len, decc])
432    
433          propertyBox.Add(self.properties, 0, wxALL, 4)          self.fields.SetSelection(self.__cur_field)
         EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)  
434    
435          topBox.Add(propertyBox, 0, 0)          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
436            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnFieldSelect)
437    
438            topBox.Add(propertyBox, 0, wxGROW, 4)
439    
440          #          #
441          # Classification data table          # Classification data table
442          #          #
443    
444          table = wxPyGridTableBase()          controlBox = wxBoxSizer(wxHORIZONTAL)
445          tableBox = wxGridSizer(25)          self.classGrid = ClassGrid(self,
446          self.classTable = wxGrid(self, ID_CLASS_TABLE)                                     layer,
447          self.classTable.CreateGrid(10, 2)                                     self.fields.GetClientData(self.__cur_field))
448          self.classTable.SetTable(table, true)  
449          #table.SetNumberRows(10)          controlBox.Add(self.classGrid, 1, wxGROW, 0)
450          #table.SetNumberCols(2)  
451          table.SetColLabelValue(0, "Class")          controlButtonBox = wxBoxSizer(wxVERTICAL)
452          table.SetColLabelValue(1, "Value")          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_ADD,
453          #self.classTable.SetColLabelValue(0, "Class")              _("Add")), 0, wxGROW | wxALL, 4)
454          #self.classTable.SetColLabelValue(1, "Value")          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,
455          #self.classTable.SetCellValue(1, 1, "Value")              _("Generate Ranges")), 0, wxGROW | wxALL, 4)
456    
457          tableBox.Add(self.classTable, 0, wxALL, 4)          controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,
458                        _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
459          topBox.Add(self.classTable, 0, 0)  
460            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
461            topBox.Add(controlBox, 1, wxGROW, 10)
462    
463            EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)
464            EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self.OnRemove)
465            EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)
466    
467          #          #
468          # Control buttons:          # Control buttons:
469          #          #
470          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
471          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, "OK"),          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),
472                        0, wxALL, 4)                        0, wxALL, 4)
473          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, "Cancel"),          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),
474                        0, wxALL, 4)                        0, wxALL, 4)
475          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
476    
477          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)
478          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)
479    
480    
481    
482          self.SetAutoLayout(true)          self.SetAutoLayout(true)
483          self.SetSizer(topBox)          self.SetSizer(topBox)
484          topBox.Fit(self)          topBox.Fit(self)
485          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
486    
487      def OnPropertySelect(self, event): pass      def __BuildClassification(self, prop):
488    
489            clazz = Classification()
490            clazz.SetField(self.fields.GetString(prop))
491    
492            numRows = self.classGrid.GetNumberRows()
493    
494            if numRows > 0:
495                table = self.classGrid.GetTable()
496                clazz.SetDefaultGroup(table.GetClassGroup(0))
497    
498                for i in range(1, numRows):
499                    clazz.AddGroup(table.GetClassGroup(i))
500    
501            return clazz
502    
503        def OnFieldSelect(self, event):
504            data = self.fields.GetClientData(self.__cur_field)
505            data[FIELD_CLASS] = self.__BuildClassification(self.__cur_field)
506    
507            self.fields.SetClientData(self.__cur_field, data)
508    
509            self.__cur_field = self.fields.GetSelection()
510            fieldData = self.fields.GetClientData(self.__cur_field)
511            self.classGrid.GetTable().Reset(fieldData, self.layer.ShapeType())
512    
513        def OnOK(self, event):
514            """Put the data from the table into a new Classification and hand
515               it to the layer.
516            """
517    
518            clazz = self.fields.GetClientData(self.__cur_field)[FIELD_CLASS]
519    
520            #
521            # only build the classification if there wasn't one to
522            # to begin with or it has been modified
523            #
524            if clazz is None or self.classGrid.GetTable().IsModified():
525                clazz = self.__BuildClassification(self.__cur_field)
526    
527            clazz.SetLayer(self.layer)
528    
529            self.layer.SetClassification(clazz)
530    
531            self.EndModal(wxID_OK)
532    
533        def OnCancel(self, event):
534            """Do nothing. The layer's current classification stays the same."""
535            self.EndModal(wxID_CANCEL)
536    
537        def OnAdd(self, event):
538            self.classGrid.AppendRows()
539    
540        def OnRemove(self, event):
541            self.classGrid.DeleteSelectedRows()
542    
543        def OnGenRange(self, event):
544            print "Classifier.OnGenRange()"
545    
546    
547    ID_SELPROP_OK = 4001
548    ID_SELPROP_CANCEL = 4002
549    ID_SELPROP_SPINCTRL = 4002
550    ID_SELPROP_PREVIEW = 4003
551    ID_SELPROP_STROKECLR = 4004
552    ID_SELPROP_FILLCLR = 4005
553    
554    class SelectPropertiesDialog(wxDialog):
555    
556        def __init__(self, parent, prop, shapeType):
557            wxDialog.__init__(self, parent, -1, _("Select Properties"),
558                              style = wxRESIZE_BORDER)
559    
560            self.prop = ClassGroupProperties(prop)
561    
562            topBox = wxBoxSizer(wxVERTICAL)
563    
564            itemBox = wxBoxSizer(wxHORIZONTAL)
565    
566            # preview box
567            previewBox = wxBoxSizer(wxVERTICAL)
568            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
569                0, wxALIGN_LEFT | wxALL, 4)
570            self.previewer = ClassDataPreviewer(None, self.prop, shapeType,
571                                                self, ID_SELPROP_PREVIEW, (40, 40))
572            previewBox.Add(self.previewer, 1, wxGROW, 15)
573    
574            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
575    
576            # control box
577            ctrlBox = wxBoxSizer(wxVERTICAL)
578            ctrlBox.Add(
579                wxButton(self, ID_SELPROP_STROKECLR, "Change Stroke Color"),
580                1, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
581            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self.OnChangeStrokeColor)
582    
583            if shapeType != SHAPETYPE_ARC:
584                ctrlBox.Add(
585                    wxButton(self, ID_SELPROP_FILLCLR, "Change Fill Color"),
586                    0, wxALIGN_LEFT | wxALL | wxGROW, 4)
587                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self.OnChangeFillColor)
588    
589            spinBox = wxBoxSizer(wxHORIZONTAL)
590            spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),
591                    0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
592            self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
593                                       min=1, max=10,
594                                       value=str(prop.GetStrokeWidth()),
595                                       initial=prop.GetStrokeWidth())
596    
597            EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)
598    
599            spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
600    
601            ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
602            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
603            topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
604    
605    
606            #
607            # Control buttons:
608            #
609            buttonBox = wxBoxSizer(wxHORIZONTAL)
610            buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),
611                          0, wxALL, 4)
612            buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),
613                          0, wxALL, 4)
614            topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
615                                                                                    
616            EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)
617            EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)
618                                                                                    
619            self.SetAutoLayout(true)
620            self.SetSizer(topBox)
621            topBox.Fit(self)
622            topBox.SetSizeHints(self)
623    
624      def OnOK(self, event):      def OnOK(self, event):
625          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
626    
627      def OnCancel(self, event):      def OnCancel(self, event):
628          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
629    
630        def OnSpin(self, event):
631            self.prop.SetStrokeWidth(self.spinCtrl.GetValue())
632            self.previewer.Refresh()
633    
634        def __GetColor(self, cur):
635            dialog = wxColourDialog(self)
636            dialog.GetColourData().SetColour(Color2wxColour(cur))
637            ret = None
638            if dialog.ShowModal() == wxID_OK:
639                ret = wxColour2Color(dialog.GetColourData().GetColour())
640    
641            dialog.Destroy()
642    
643            return ret
644            
645        def OnChangeStrokeColor(self, event):
646            clr = self.__GetColor(self.prop.GetStroke())
647            if clr is not None:
648                self.prop.SetStroke(clr)
649            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
650    
651        def OnChangeFillColor(self, event):
652            clr = self.__GetColor(self.prop.GetFill())
653            if clr is not None:
654                self.prop.SetFill(clr)
655            self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer
656    
657        def GetClassGroupProperties(self):
658            return self.prop
659    
660    
661    class ClassDataPreviewer(wxWindow):
662    
663        def __init__(self, rect, prop, shapeType,
664                           parent = None, id = -1, size = wxDefaultSize):
665            if parent is not None:
666                wxWindow.__init__(self, parent, id, size=size)
667                EVT_PAINT(self, self.OnPaint)
668    
669            self.rect = rect
670            self.prop = prop
671            self.shapeType = shapeType
672    
673        def OnPaint(self, event):
674            dc = wxPaintDC(self)
675    
676            # XXX: this doesn't seem to be having an effect:
677            dc.DestroyClippingRegion()
678    
679            self.Draw(dc, None)
680    
681        def Draw(self, dc, rect, prop = None, shapeType = None):
682    
683            if prop is None: prop = self.prop
684            if shapeType is None: shapeType = self.shapeType
685    
686            if rect is None:
687                x = y = 0
688                w, h = self.GetClientSizeTuple()
689            else:
690                x = rect.GetX()
691                y = rect.GetY()
692                w = rect.GetWidth()
693                h = rect.GetHeight()
694    
695            stroke = prop.GetStroke()
696            if stroke is Color.None:
697                pen = wxTRANSPARENT_PEN
698            else:
699                pen = wxPen(Color2wxColour(stroke),
700                            prop.GetStrokeWidth(),
701                            wxSOLID)
702    
703            stroke = prop.GetFill()
704            if stroke is Color.None:
705                brush = wxTRANSPARENT_BRUSH
706            else:
707                brush = wxBrush(Color2wxColour(stroke), wxSOLID)
708    
709            dc.SetPen(pen)
710            dc.SetBrush(brush)
711    
712            if shapeType == SHAPETYPE_ARC:
713                dc.DrawSpline([wxPoint(x, y + h),
714                               wxPoint(x + w/2, y + h/4),
715                               wxPoint(x + w/2, y + h/4*3),
716                               wxPoint(x + w, y)])
717    
718            elif shapeType == SHAPETYPE_POINT or \
719                 shapeType == SHAPETYPE_POLYGON:
720    
721                dc.DrawCircle(x + w/2, y + h/2,
722                              (min(w, h) - prop.GetStrokeWidth())/2)
723    
724    class ClassRenderer(wxPyGridCellRenderer):
725    
726        def __init__(self, shapeType):
727            wxPyGridCellRenderer.__init__(self)
728            self.previewer = ClassDataPreviewer(None, None, shapeType)
729    
730        def Draw(self, grid, attr, dc, rect, row, col, isSelected):
731            data = grid.GetTable().GetValueAsCustom(row, col, None)
732    
733            dc.SetClippingRegion(rect.GetX(), rect.GetY(),
734                                 rect.GetWidth(), rect.GetHeight())
735            dc.SetPen(wxPen(wxLIGHT_GREY))
736            dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
737            dc.DrawRectangle(rect.GetX(), rect.GetY(),
738                             rect.GetWidth(), rect.GetHeight())
739    
740            if not isinstance(data, ClassGroupMap):
741                self.previewer.Draw(dc, rect, data.GetProperties())
742    
743            if isSelected:
744                dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),
745                          4, wxSOLID))
746                dc.SetBrush(wxTRANSPARENT_BRUSH)
747                dc.DrawRectangle(rect.GetX(), rect.GetY(),
748                                 rect.GetWidth(), rect.GetHeight())
749    
750            dc.DestroyClippingRegion()
751    

Legend:
Removed from v.372  
changed lines
  Added in v.455

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26