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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 451 - (show 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 # 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 import copy
13
14 from wxPython.wx import *
15 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
28 ID_CLASS_TABLE = 40011
29
30 ID_CLASSIFY_OK = 4001
31 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.SetSelectionMode(wxGrid.wxGridSelectRows)
58 self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), true)
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):
395
396 def __init__(self, parent, layer):
397 wxDialog.__init__(self, parent, -1, _("Classify"),
398 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
399
400
401 self.layer = layer
402
403 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)
411 propertyBox.Add(wxStaticText(self, -1, _("Field: ")),
412 0, wxALIGN_CENTER | wxALL, 4)
413
414 self.fields = wxComboBox(self, ID_PROPERTY_SELECT, "",
415 style = wxCB_READONLY)
416
417 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):
424 type, name, len, decc = layer.table.field_info(i)
425 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 self.fields.SetSelection(self.__cur_field)
434
435 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
442 #
443
444 controlBox = wxBoxSizer(wxHORIZONTAL)
445 self.classGrid = ClassGrid(self,
446 layer,
447 self.fields.GetClientData(self.__cur_field))
448
449 controlBox.Add(self.classGrid, 1, wxGROW, 0)
450
451 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
457 controlButtonBox.Add(wxButton(self, ID_CLASSIFY_REMOVE,
458 _("Remove")), 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
459
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:
469 #
470 buttonBox = wxBoxSizer(wxHORIZONTAL)
471 buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),
472 0, wxALL, 4)
473 buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),
474 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
481
482 self.SetAutoLayout(true)
483 self.SetSizer(topBox)
484 topBox.Fit(self)
485 topBox.SetSizeHints(self)
486
487 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):
625 self.EndModal(wxID_OK)
626
627 def OnCancel(self, event):
628 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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26