/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Annotation of /trunk/thuban/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 451 - (hide annotations)
Tue Mar 4 10:33:56 2003 UTC (22 years ago) by jonathan
File MIME type: text/x-python
File size: 24703 byte(s)
(ClassGrid): Moved OnCellDClick() from
        Classifier to ClassGrid. Added support for removing selected rows,
        which including code for keeping track of when cells are selected,
        and deselected.
(ClassTable): Support for added/removing rows. Fixed a problem
        with __ParseInput whereby it would not allow strings (only numbers)
        to be entered.
(Classifier): Added button and supporting code for removing selected rows.

1 jonathan 372 # Copyright (c) 2001 by Intevation GmbH
2     # Authors:
3     # Jonathan Coles <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with Thuban for details.
7    
8     """Dialog for classifying how layers are displayed"""
9    
10     __version__ = "$Revision$"
11    
12 jonathan 376 import copy
13    
14 jonathan 372 from wxPython.wx import *
15     from wxPython.grid import *
16    
17 jan 374 from Thuban import _
18 jonathan 415 from Thuban.common import *
19 jonathan 441 from Thuban.UI.common import *
20 jan 374
21 jonathan 451 from Thuban.Model.classification import *
22 jonathan 392
23 jonathan 415 from Thuban.Model.color import Color
24    
25 jonathan 392 from Thuban.Model.layer import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
26    
27 jonathan 372 ID_PROPERTY_SELECT = 4010
28     ID_CLASS_TABLE = 40011
29    
30     ID_CLASSIFY_OK = 4001
31     ID_CLASSIFY_CANCEL = 4002
32 jonathan 415 ID_CLASSIFY_ADD = 4003
33     ID_CLASSIFY_GENRANGE = 4004
34 jonathan 451 ID_CLASSIFY_REMOVE = 4005
35 jonathan 372
36 jonathan 415 COL_VISUAL = 0
37     COL_VALUE = 1
38     COL_LABEL = 2
39    
40 jonathan 451 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 jonathan 415 #
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 jonathan 451 def __init__(self, parent, layer, fieldData):
56     wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
57     self.SetSelectionMode(wxGrid.wxGridSelectRows)
58     self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
59     EVT_GRID_CELL_LEFT_DCLICK(self, self.OnCellDClick)
60 jonathan 415
61 jonathan 451 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 jonathan 415 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 jonathan 451 def DeleteSelectedRows(self):
83     sel = self.GetCurrentSelection()
84 jonathan 415
85 jonathan 451 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 jonathan 376 class ClassTable(wxPyGridTableBase):
159    
160 jonathan 415 NUM_COLS = 3
161    
162 jonathan 451 __col_labels = [_("Symbol"), _("Value"), _("Label")]
163 jonathan 415
164 jonathan 451 def __init__(self, fieldData, shapeType, view = None):
165 jonathan 376 wxPyGridTableBase.__init__(self)
166 jonathan 415 self.SetView(view)
167     self.tdata = []
168 jonathan 376
169 jonathan 451 self.Reset(fieldData, shapeType)
170 jonathan 415
171 jonathan 451 def Reset(self, fieldData, shapeType):
172 jonathan 415
173     self.GetView().BeginBatch()
174    
175 jonathan 451 self.fieldData = fieldData
176 jonathan 415 self.shapeType = shapeType
177     self.renderer = ClassRenderer(self.shapeType)
178    
179 jonathan 451 old_len = len(self.tdata)
180 jonathan 415
181 jonathan 376 self.tdata = []
182    
183 jonathan 451 clazz = fieldData[FIELD_CLASS]
184 jonathan 415 if clazz is None:
185     clazz = Classification()
186 jonathan 392
187 jonathan 441 # p = clazz.GetDefaultGroup()
188     # np = ClassDataDefault(classData = p)
189     # self.tdata.append([np, 'DEFAULT', np.GetLabel()])
190 jonathan 376
191 jonathan 441 # for p in clazz.points.values():
192     # np = ClassDataPoint(p.GetValue(), classData = p)
193     # self.tdata.append([np, np.GetValue(), np.GetLabel()])
194 jonathan 376
195 jonathan 441 # 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 jonathan 415
201 jonathan 441 i = 0
202     for p in clazz:
203     np = copy.copy(p)
204     self.__SetRow(i, np)
205     i += 1
206    
207    
208 jonathan 415 self.modified = 0
209    
210 jonathan 451 self.__NotifyRowChanges(old_len, len(self.tdata))
211     self.GetView().EndBatch()
212    
213     def __NotifyRowChanges(self, curRows, newRows):
214 jonathan 415 #
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 jonathan 441 def __SetRow(self, row, group):
232    
233     if isinstance(group, ClassGroupDefault):
234 jonathan 451 data = [group, _('DEFAULT'), group.GetLabel()]
235 jonathan 441 elif isinstance(group, ClassGroupSingleton):
236     data = [group, group.GetValue(), group.GetLabel()]
237     elif isinstance(group, ClassGroupRange):
238     data = [group,
239 jonathan 451 _('%s - %s') % (group.GetMin(), group.GetMax()),
240 jonathan 441 group.GetLabel()]
241    
242     if row >= len(self.tdata):
243     self.tdata.append(data)
244     else:
245     self.tdata[row] = data
246    
247 jonathan 415 def GetColLabelValue(self, col):
248     return self.__col_labels[col]
249    
250     def GetRowLabelValue(self, row):
251     data = self.tdata[row][COL_VISUAL]
252 jonathan 451 if isinstance(data, ClassGroupDefault): return _("Default")
253 jonathan 441 if isinstance(data, ClassGroupSingleton): return _("Singleton")
254 jonathan 451 if isinstance(data, ClassGroupRange): return _("Range")
255     if isinstance(data, ClassGroupMap): return _("Map")
256 jonathan 415
257 jonathan 376 def GetNumberRows(self):
258     return len(self.tdata)
259    
260     def GetNumberCols(self):
261 jonathan 415 return self.NUM_COLS
262 jonathan 376
263     def IsEmptyCell(self, row, col):
264 jonathan 415 return 0
265 jonathan 376
266     def GetValue(self, row, col):
267 jonathan 415 return self.GetValueAsCustom(row, col, "")
268 jonathan 376
269     def SetValue(self, row, col, value):
270 jonathan 415 self.SetValueAsCustom(row, col, "", value)
271     self.__Modified()
272    
273 jonathan 392 def GetValueAsCustom(self, row, col, typeName):
274     return self.tdata[row][col]
275 jonathan 376
276 jonathan 415 def __ParseInput(self, value):
277     """Try to determine what kind of input value is
278     (a single number or a range)
279     """
280 jonathan 392
281 jonathan 451 type = self.fieldData[FIELD_TYPE]
282 jonathan 415
283 jonathan 451 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 jonathan 415
305    
306     def SetValueAsCustom(self, row, col, typeName, value):
307 jonathan 451 group = self.tdata[row][COL_VISUAL]
308 jonathan 415
309     if col == COL_VISUAL:
310     self.tdata[row][COL_VISUAL] = value
311     elif col == COL_VALUE:
312 jonathan 451 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 jonathan 415
326 jonathan 451 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 jonathan 415 else:
337 jonathan 451 assert(False)
338 jonathan 415
339 jonathan 451 ngroup.SetLabel(group.GetLabel())
340     self.__SetRow(row, ngroup)
341 jonathan 415
342 jonathan 451 self.GetView().Refresh()
343 jonathan 415
344     elif col == COL_LABEL:
345 jonathan 451 group.SetLabel(value)
346     self.tdata[row][COL_LABEL] = group.GetLabel()
347 jonathan 415 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 jonathan 441 def GetClassGroup(self, row):
363 jonathan 415 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 jonathan 451 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 jonathan 415
383 jonathan 451 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 jonathan 372 class Classifier(wxDialog):
395    
396     def __init__(self, parent, layer):
397 jan 374 wxDialog.__init__(self, parent, -1, _("Classify"),
398 jonathan 451 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
399 jonathan 372
400 jonathan 451
401 jonathan 415 self.layer = layer
402    
403 jonathan 372 topBox = wxBoxSizer(wxVERTICAL)
404    
405 jonathan 415 topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),
406 jonathan 451 0, wxALIGN_LEFT | wxALL, 4)
407 jonathan 415 topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),
408 jonathan 451 0, wxALIGN_LEFT | wxALL, 4)
409 jonathan 415
410 jonathan 372 propertyBox = wxBoxSizer(wxHORIZONTAL)
411 jonathan 451 propertyBox.Add(wxStaticText(self, -1, _("Field: ")),
412 jonathan 372 0, wxALIGN_CENTER | wxALL, 4)
413    
414 jonathan 451 self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",
415 jonathan 372 style = wxCB_READONLY)
416    
417     self.num_cols = layer.table.field_count()
418 jonathan 441 # just assume the first field in case one hasn't been
419     # specified in the file.
420 jonathan 451 self.__cur_field = 0
421     clazz = layer.GetClassification()
422     field = clazz.GetField()
423 jonathan 372 for i in range(self.num_cols):
424     type, name, len, decc = layer.table.field_info(i)
425 jonathan 451 self.fields.Append(name)
426    
427 jonathan 415 if name == field:
428 jonathan 451 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 jonathan 372
433 jonathan 451 self.fields.SetSelection(self.__cur_field)
434 jonathan 372
435 jonathan 451 propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
436     EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnFieldSelect)
437    
438 jonathan 376 topBox.Add(propertyBox, 0, wxGROW, 4)
439 jonathan 372
440     #
441     # Classification data table
442     #
443    
444 jonathan 415 controlBox = wxBoxSizer(wxHORIZONTAL)
445 jonathan 451 self.classGrid = ClassGrid(self,
446     layer,
447     self.fields.GetClientData(self.__cur_field))
448 jonathan 376
449 jonathan 415 controlBox.Add(self.classGrid, 1, wxGROW, 0)
450 jonathan 376
451 jonathan 415 controlButtonBox = wxBoxSizer(wxVERTICAL)
452     controlButtonBox.Add(wxButton(self, ID_CLASSIFY_ADD,
453     _("Add")), 0, wxGROW | wxALL, 4)
454     controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,
455     _("Generate Ranges")), 0, wxGROW | wxALL, 4)
456 jonathan 372
457 jonathan 451 controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,
458     _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
459    
460 jonathan 415 controlBox.Add(controlButtonBox, 0, wxGROW, 10)
461     topBox.Add(controlBox, 1, wxGROW, 10)
462    
463     EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)
464 jonathan 451 EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self.OnRemove)
465 jonathan 415 EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)
466    
467 jonathan 372 #
468     # Control buttons:
469     #
470     buttonBox = wxBoxSizer(wxHORIZONTAL)
471 jan 374 buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),
472 jonathan 372 0, wxALL, 4)
473 jan 374 buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),
474 jonathan 372 0, wxALL, 4)
475     topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
476    
477     EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)
478     EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)
479    
480 jonathan 451
481    
482 jonathan 372 self.SetAutoLayout(true)
483     self.SetSizer(topBox)
484     topBox.Fit(self)
485     topBox.SetSizeHints(self)
486    
487 jonathan 415 def __BuildClassification(self, prop):
488    
489     clazz = Classification()
490 jonathan 451 clazz.SetField(self.fields.GetString(prop))
491 jonathan 415
492     numRows = self.classGrid.GetNumberRows()
493    
494     if numRows > 0:
495     table = self.classGrid.GetTable()
496 jonathan 441 clazz.SetDefaultGroup(table.GetClassGroup(0))
497 jonathan 415
498     for i in range(1, numRows):
499 jonathan 441 clazz.AddGroup(table.GetClassGroup(i))
500 jonathan 415
501     return clazz
502    
503 jonathan 451 def OnFieldSelect(self, event):
504     data = self.fields.GetClientData(self.__cur_field)
505     data[FIELD_CLASS] = self.__BuildClassification(self.__cur_field)
506 jonathan 415
507 jonathan 451 self.fields.SetClientData(self.__cur_field, data)
508 jonathan 415
509 jonathan 451 self.__cur_field = self.fields.GetSelection()
510     fieldData = self.fields.GetClientData(self.__cur_field)
511     self.classGrid.GetTable().Reset(fieldData, self.layer.ShapeType())
512 jonathan 415
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 jonathan 451 clazz = self.fields.GetClientData(self.__cur_field)[FIELD_CLASS]
519 jonathan 415
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 jonathan 451 clazz = self.__BuildClassification(self.__cur_field)
526 jonathan 415
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 jonathan 451 self.classGrid.AppendRows()
539 jonathan 415
540 jonathan 451 def OnRemove(self, event):
541     self.classGrid.DeleteSelectedRows()
542    
543 jonathan 415 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 jonathan 430 ID_SELPROP_PREVIEW = 4003
551     ID_SELPROP_STROKECLR = 4004
552     ID_SELPROP_FILLCLR = 4005
553 jonathan 415
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 jonathan 441 self.prop = ClassGroupProperties(prop)
561 jonathan 415
562 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
563 jonathan 415
564 jonathan 430 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 jonathan 451 1, wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
581 jonathan 430 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 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
590     spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),
591 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
592 jonathan 415 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 jonathan 430 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 jonathan 415
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 jonathan 372 def OnOK(self, event):
625     self.EndModal(wxID_OK)
626    
627     def OnCancel(self, event):
628     self.EndModal(wxID_CANCEL)
629    
630 jonathan 415 def OnSpin(self, event):
631     self.prop.SetStrokeWidth(self.spinCtrl.GetValue())
632 jonathan 430 self.previewer.Refresh()
633 jonathan 392
634 jonathan 430 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 jonathan 441 def GetClassGroupProperties(self):
658 jonathan 415 return self.prop
659 jonathan 392
660    
661 jonathan 430 class ClassDataPreviewer(wxWindow):
662 jonathan 415
663 jonathan 441 def __init__(self, rect, prop, shapeType,
664 jonathan 430 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 jonathan 415
669 jonathan 430 self.rect = rect
670 jonathan 441 self.prop = prop
671 jonathan 430 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 jonathan 441 def Draw(self, dc, rect, prop = None, shapeType = None):
682 jonathan 430
683 jonathan 441 if prop is None: prop = self.prop
684 jonathan 430 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 jonathan 441 stroke = prop.GetStroke()
696 jonathan 415 if stroke is Color.None:
697 jonathan 392 pen = wxTRANSPARENT_PEN
698     else:
699 jonathan 430 pen = wxPen(Color2wxColour(stroke),
700 jonathan 441 prop.GetStrokeWidth(),
701 jonathan 392 wxSOLID)
702    
703 jonathan 441 stroke = prop.GetFill()
704 jonathan 415 if stroke is Color.None:
705 jonathan 392 brush = wxTRANSPARENT_BRUSH
706     else:
707 jonathan 430 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
708 jonathan 392
709     dc.SetPen(pen)
710     dc.SetBrush(brush)
711    
712 jonathan 415 if shapeType == SHAPETYPE_ARC:
713 jonathan 430 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 jonathan 392
718 jonathan 415 elif shapeType == SHAPETYPE_POINT or \
719     shapeType == SHAPETYPE_POLYGON:
720    
721 jonathan 430 dc.DrawCircle(x + w/2, y + h/2,
722 jonathan 441 (min(w, h) - prop.GetStrokeWidth())/2)
723 jonathan 392
724 jonathan 415 class ClassRenderer(wxPyGridCellRenderer):
725    
726     def __init__(self, shapeType):
727     wxPyGridCellRenderer.__init__(self)
728 jonathan 430 self.previewer = ClassDataPreviewer(None, None, shapeType)
729 jonathan 415
730     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
731 jonathan 451 data = grid.GetTable().GetValueAsCustom(row, col, None)
732 jonathan 415
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 jonathan 441 if not isinstance(data, ClassGroupMap):
741     self.previewer.Draw(dc, rect, data.GetProperties())
742 jonathan 415
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 jonathan 392 dc.DestroyClippingRegion()
751    

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26