/[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 1433 - (show annotations)
Wed Jul 16 13:24:25 2003 UTC (21 years, 7 months ago) by jonathan
File MIME type: text/x-python
File size: 46292 byte(s)
(ClassGrid.CreateTable): Add fieldType parameter.
(ClassTable.Reset): Add fieldType parameter and use it, rather
        than asking the classification.
(Classifier.__init__): Remember the original class's field
        and ask the layer for the field type, rather than the classification.
(Classifier.__SetGridTable): Retrieve the field and field type
        for the table because they are not in the classification.
(Classifier._OnTry, Classifier._OnRevert): Set the classification
        field on the layer in addition to the classification itself.

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