/[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 1464 - (show annotations)
Fri Jul 18 18:20:40 2003 UTC (21 years, 7 months ago) by bh
File MIME type: text/x-python
File size: 46758 byte(s)
* Thuban/UI/messages.py (MAP_REPLACED): New message.

* Thuban/UI/viewport.py (ViewPort.SetMap): Issue MAP_REPLACED
after the new map has been assigned

* Thuban/UI/mainwindow.py (MainWindow.delegated_messages):
Delegate MAP_REPLACED to the canvas too
(MainWindow.prepare_new_session): Removed. Thanks to the new
MAP_REPLACED message it's no longer needed
(MainWindow.OpenSession, MainWindow.NewSession):
prepare_new_session has been removed.

* Thuban/UI/classifier.py (Classifier.__init__): Subscribe to
MAP_REPLACED so that we can close the dialog if a new map is set.
(Classifier.unsubscribe_messages): Unsubscribe from MAP_REPLACED
(Classifier.map_replaced): Handle MAP_REPLACED by closing the
dialog

* test/test_viewport.py (SimpleViewPortTest)
(SimpleViewPortTest.test_default_size): Add doc-strings
(ViewPortTest.setUp): Bind map to self.map so we can use it in
tests. Subscribe to MAP_REPLACED messages too.
(ViewPortTest.tearDown): No need to explicitly unsubscribe
(ViewPortTest.test_set_map): New test for the SetMap method.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26