/[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 1342 - (show annotations)
Tue Jul 1 16:10:54 2003 UTC (21 years, 8 months ago) by jonathan
File MIME type: text/x-python
File size: 46394 byte(s)
Fixes RTbug #1971.
(Classifier.__BuildClassification, Classifier.__SetGridTable):
        Call Classification.SetFieldInfo() instead of SetFieldType.

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, 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, 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 #def __init__(self, clazz, shapeType, 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, 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 = clazz.GetFieldType()
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.layer = layer
687 self.map = map
688
689 self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
690 self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
691 self.layer_shapestore_replaced)
692
693 self.genDlg = None
694
695 ############################
696 # Create the controls
697 #
698
699 panel = wxPanel(self, -1)
700
701 text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
702 self.fieldTypeText = wxStaticText(panel, -1, "")
703
704 if layer.HasClassification():
705 self.originalClass = self.layer.GetClassification()
706 field = self.originalClass.GetField()
707 fieldType = self.originalClass.GetFieldType()
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 self.originalClass.GetFieldType() 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 # clazz = Classification()
900 if fieldIndex == 0:
901 fieldName = None
902 fieldType = None
903 else:
904 fieldName = self.fields.GetString(fieldIndex)
905 fieldType = self.layer.GetFieldType(fieldName)
906
907 clazz = self.classGrid.GetTable().GetClassification()
908
909 if copyClass:
910 clazz = copy.deepcopy(clazz)
911
912 clazz.SetFieldInfo(fieldName, fieldType)
913
914
915 # table = self.classGrid.GetTable()
916 # clazz.SetDefaultGroup(table.GetClassGroup(0))
917
918 # for i in range(1, numRows):
919 # clazz.AppendGroup(table.GetClassGroup(i))
920
921 return clazz
922
923 def __SetGridTable(self, fieldIndex, group = None):
924
925 clazz = self.fields.GetClientData(fieldIndex)
926
927 if clazz is None:
928 clazz = Classification()
929 clazz.SetDefaultGroup(
930 ClassGroupDefault(
931 self.layer.GetClassification().
932 GetDefaultGroup().GetProperties()))
933
934 fieldName = self.fields.GetString(fieldIndex)
935 fieldType = self.layer.GetFieldType(fieldName)
936 clazz.SetFieldInfo(fieldName, fieldType)
937
938 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
939
940 def __SetFieldTypeText(self, fieldIndex):
941 fieldName = self.fields.GetString(fieldIndex)
942 fieldType = self.layer.GetFieldType(fieldName)
943
944 assert Classifier.type2string.has_key(fieldType)
945
946 text = Classifier.type2string[fieldType]
947
948 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
949
950 def __SelectField(self, newIndex, oldIndex = -1, group = None):
951 """This method assumes that the current selection for the
952 combo has already been set by a call to SetSelection().
953 """
954
955 assert oldIndex >= -1
956
957 if oldIndex != -1:
958 clazz = self.__BuildClassification(oldIndex)
959 self.fields.SetClientData(oldIndex, clazz)
960
961 self.__SetGridTable(newIndex, group)
962
963 self.__EnableButtons(EB_SELECT_FIELD)
964
965 self.__SetFieldTypeText(newIndex)
966
967 def __SetTitle(self, title):
968 if title != "":
969 title = ": " + title
970
971 self.SetTitle(_("Layer Properties") + title)
972
973 def _OnEditSymbol(self, event):
974 sel = self.classGrid.GetCurrentSelection()
975
976 if len(sel) == 1:
977 self.EditSymbol(sel[0])
978
979 def _OnFieldSelect(self, event):
980 index = self.fields.GetSelection()
981 self.__SelectField(index, self.__cur_field)
982 self.__cur_field = index
983
984 def _OnTry(self, event):
985 """Put the data from the table into a new Classification and hand
986 it to the layer.
987 """
988
989 if self.layer.HasClassification():
990 clazz = self.fields.GetClientData(self.__cur_field)
991
992 #
993 # only build the classification if there wasn't one to
994 # to begin with or it has been modified
995 #
996 self.classGrid.SaveEditControlValue()
997 if clazz is None or self.classGrid.GetTable().IsModified():
998 clazz = self.__BuildClassification(self.__cur_field, True)
999
1000 self.layer.SetClassification(clazz)
1001
1002 self.haveApplied = True
1003
1004 def _OnOK(self, event):
1005 self._OnTry(event)
1006 self.Close()
1007
1008 def OnClose(self, event):
1009 self.unsubscribe_messages()
1010 NonModalNonParentDialog.OnClose(self, event)
1011
1012 def _OnCloseBtn(self, event):
1013 """Close is similar to Cancel except that any changes that were
1014 made and applied remain applied, but the currently displayed
1015 classification is discarded.
1016 """
1017
1018 self.Close()
1019
1020 def _OnRevert(self, event):
1021 """The layer's current classification stays the same."""
1022 if self.haveApplied:
1023 self.layer.SetClassification(self.originalClass)
1024
1025 #self.Close()
1026
1027 def _OnAdd(self, event):
1028 self.classGrid.AppendRows()
1029
1030 def _OnRemove(self, event):
1031 self.classGrid.DeleteSelectedRows()
1032
1033 def _OnGenClass(self, event):
1034
1035 self.genDlg = ClassGenDialog(self, self.layer,
1036 self.fields.GetString(self.__cur_field))
1037
1038 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1039
1040 self.__EnableButtons(EB_GEN_CLASS)
1041
1042 self.genDlg.Show()
1043
1044 def _OnGenDialogClose(self, event):
1045 self.genDlg.Destroy()
1046 self.genDlg = None
1047 self.__EnableButtons(EB_GEN_CLASS)
1048
1049 def _OnMoveUp(self, event):
1050 sel = self.classGrid.GetCurrentSelection()
1051
1052 if len(sel) == 1:
1053 i = sel[0]
1054 if i > 1:
1055 table = self.classGrid.GetTable()
1056 x = table.GetClassGroup(i - 1)
1057 y = table.GetClassGroup(i)
1058 table.SetClassGroup(i - 1, y)
1059 table.SetClassGroup(i, x)
1060 self.classGrid.ClearSelection()
1061 self.classGrid.SelectRow(i - 1)
1062 self.classGrid.MakeCellVisible(i - 1, 0)
1063
1064 def _OnMoveDown(self, event):
1065 sel = self.classGrid.GetCurrentSelection()
1066
1067 if len(sel) == 1:
1068 i = sel[0]
1069 table = self.classGrid.GetTable()
1070 if 0 < i < table.GetNumberRows() - 1:
1071 x = table.GetClassGroup(i)
1072 y = table.GetClassGroup(i + 1)
1073 table.SetClassGroup(i, y)
1074 table.SetClassGroup(i + 1, x)
1075 self.classGrid.ClearSelection()
1076 self.classGrid.SelectRow(i + 1)
1077 self.classGrid.MakeCellVisible(i + 1, 0)
1078
1079 def _OnTitleChanged(self, event):
1080 obj = event.GetEventObject()
1081
1082 self.layer.SetTitle(obj.GetValue())
1083 self.__SetTitle(self.layer.Title())
1084
1085 self.__EnableButtons(EB_LAYER_TITLE)
1086
1087 def __EnableButtons(self, case):
1088
1089 list = {wxID_OK : True,
1090 wxID_CANCEL : True,
1091 ID_PROPERTY_ADD : True,
1092 ID_PROPERTY_MOVEUP : True,
1093 ID_PROPERTY_MOVEDOWN : True,
1094 ID_PROPERTY_REMOVE : True,
1095 ID_PROPERTY_SELECT : True,
1096 ID_PROPERTY_FIELDTEXT : True,
1097 ID_PROPERTY_GENCLASS : True,
1098 ID_PROPERTY_EDITSYM : True}
1099
1100 if case == EB_LAYER_TITLE:
1101 if self.layer.Title() == "":
1102 list[wxID_OK] = False
1103 list[wxID_CANCEL] = False
1104
1105 elif case == EB_SELECT_FIELD:
1106 if self.fields.GetSelection() == 0:
1107 list[ID_PROPERTY_GENCLASS] = False
1108 list[ID_PROPERTY_ADD] = False
1109 list[ID_PROPERTY_MOVEUP] = False
1110 list[ID_PROPERTY_MOVEDOWN] = False
1111 list[ID_PROPERTY_REMOVE] = False
1112
1113 elif case == EB_GEN_CLASS:
1114 if self.genDlg is not None:
1115 list[ID_PROPERTY_SELECT] = False
1116 list[ID_PROPERTY_FIELDTEXT] = False
1117 list[ID_PROPERTY_GENCLASS] = False
1118
1119 for id, enable in list.items():
1120 win = self.FindWindowById(id)
1121 if win:
1122 win.Enable(enable)
1123
1124 ID_SELPROP_SPINCTRL = 4002
1125 ID_SELPROP_PREVIEW = 4003
1126 ID_SELPROP_STROKECLR = 4004
1127 ID_SELPROP_FILLCLR = 4005
1128 ID_SELPROP_STROKECLRTRANS = 4006
1129 ID_SELPROP_FILLCLRTRANS = 4007
1130
1131 class SelectPropertiesDialog(wxDialog):
1132
1133 def __init__(self, parent, prop, shapeType):
1134 wxDialog.__init__(self, parent, -1, _("Select Properties"),
1135 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1136
1137 self.prop = ClassGroupProperties(prop)
1138
1139 topBox = wxBoxSizer(wxVERTICAL)
1140
1141 itemBox = wxBoxSizer(wxHORIZONTAL)
1142
1143 # preview box
1144 previewBox = wxBoxSizer(wxVERTICAL)
1145 previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1146 0, wxALIGN_LEFT | wxALL, 4)
1147
1148 self.previewWin = ClassGroupPropertiesCtrl(
1149 self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1150 (40, 40), wxSIMPLE_BORDER)
1151
1152 self.previewWin.AllowEdit(False)
1153
1154 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1155
1156 itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1157
1158 # control box
1159 ctrlBox = wxBoxSizer(wxVERTICAL)
1160
1161 lineColorBox = wxBoxSizer(wxHORIZONTAL)
1162 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1163 button.SetFocus()
1164 lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1165 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1166
1167 lineColorBox.Add(
1168 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1169 1, wxALL | wxGROW, 4)
1170 EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1171 self._OnChangeLineColorTrans)
1172
1173 ctrlBox.Add(lineColorBox, 0,
1174 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1175
1176 if shapeType != SHAPETYPE_ARC:
1177 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1178 fillColorBox.Add(
1179 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1180 1, wxALL | wxGROW, 4)
1181 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1182 fillColorBox.Add(
1183 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1184 1, wxALL | wxGROW, 4)
1185 EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1186 self._OnChangeFillColorTrans)
1187 ctrlBox.Add(fillColorBox, 0,
1188 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1189
1190 spinBox = wxBoxSizer(wxHORIZONTAL)
1191 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1192 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1193 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1194 min=1, max=10,
1195 value=str(prop.GetLineWidth()),
1196 initial=prop.GetLineWidth())
1197
1198 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1199
1200 spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1201
1202 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1203 itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1204 topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1205
1206 #
1207 # Control buttons:
1208 #
1209 buttonBox = wxBoxSizer(wxHORIZONTAL)
1210 button_ok = wxButton(self, wxID_OK, _("OK"))
1211 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1212 buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1213 0, wxRIGHT|wxEXPAND, 10)
1214 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1215
1216 button_ok.SetDefault()
1217
1218 #EVT_BUTTON(self, wxID_OK, self._OnOK)
1219 #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1220
1221 self.SetAutoLayout(True)
1222 self.SetSizer(topBox)
1223 topBox.Fit(self)
1224 topBox.SetSizeHints(self)
1225
1226 def OnOK(self, event):
1227 self.EndModal(wxID_OK)
1228
1229 def OnCancel(self, event):
1230 self.EndModal(wxID_CANCEL)
1231
1232 def _OnSpin(self, event):
1233 self.prop.SetLineWidth(self.spinCtrl.GetValue())
1234 self.previewWin.Refresh()
1235
1236 def __GetColor(self, cur):
1237 dialog = wxColourDialog(self)
1238 if cur is not Transparent:
1239 dialog.GetColourData().SetColour(Color2wxColour(cur))
1240
1241 ret = None
1242 if dialog.ShowModal() == wxID_OK:
1243 ret = wxColour2Color(dialog.GetColourData().GetColour())
1244
1245 dialog.Destroy()
1246
1247 return ret
1248
1249 def _OnChangeLineColor(self, event):
1250 clr = self.__GetColor(self.prop.GetLineColor())
1251 if clr is not None:
1252 self.prop.SetLineColor(clr)
1253 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1254
1255 def _OnChangeLineColorTrans(self, event):
1256 self.prop.SetLineColor(Transparent)
1257 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1258
1259 def _OnChangeFillColor(self, event):
1260 clr = self.__GetColor(self.prop.GetFill())
1261 if clr is not None:
1262 self.prop.SetFill(clr)
1263 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1264
1265 def _OnChangeFillColorTrans(self, event):
1266 self.prop.SetFill(Transparent)
1267 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1268
1269 def GetClassGroupProperties(self):
1270 return self.prop
1271
1272
1273 class ClassDataPreviewWindow(wxWindow):
1274
1275 def __init__(self, rect, prop, shapeType,
1276 parent = None, id = -1, size = wxDefaultSize):
1277 if parent is not None:
1278 wxWindow.__init__(self, parent, id, (0, 0), size)
1279 EVT_PAINT(self, self._OnPaint)
1280
1281 self.rect = rect
1282
1283 self.prop = prop
1284 self.shapeType = shapeType
1285 self.previewer = ClassDataPreviewer()
1286
1287 def GetProperties():
1288 return self.prop
1289
1290 def _OnPaint(self, event):
1291 dc = wxPaintDC(self)
1292
1293 # XXX: this doesn't seem to be having an effect:
1294 dc.DestroyClippingRegion()
1295
1296 if self.rect is None:
1297 w, h = self.GetSize()
1298 rect = wxRect(0, 0, w, h)
1299 else:
1300 rect = self.rect
1301
1302 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1303
1304 class ClassDataPreviewer:
1305
1306 def Draw(self, dc, rect, prop, shapeType):
1307
1308 assert dc is not None
1309 assert isinstance(prop, ClassGroupProperties)
1310
1311 if rect is None:
1312 x = 0
1313 y = 0
1314 w, h = dc.GetSize()
1315 else:
1316 x = rect.GetX()
1317 y = rect.GetY()
1318 w = rect.GetWidth()
1319 h = rect.GetHeight()
1320
1321 stroke = prop.GetLineColor()
1322 if stroke is Transparent:
1323 pen = wxTRANSPARENT_PEN
1324 else:
1325 pen = wxPen(Color2wxColour(stroke),
1326 prop.GetLineWidth(),
1327 wxSOLID)
1328
1329 stroke = prop.GetFill()
1330 if stroke is Transparent:
1331 brush = wxTRANSPARENT_BRUSH
1332 else:
1333 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1334
1335 dc.SetPen(pen)
1336 dc.SetBrush(brush)
1337
1338 if shapeType == SHAPETYPE_ARC:
1339 dc.DrawSpline([wxPoint(x, y + h),
1340 wxPoint(x + w/2, y + h/4),
1341 wxPoint(x + w/2, y + h/4*3),
1342 wxPoint(x + w, y)])
1343
1344 elif shapeType == SHAPETYPE_POINT:
1345
1346 dc.DrawCircle(x + w/2, y + h/2,
1347 (min(w, h) - prop.GetLineWidth())/2)
1348
1349 elif shapeType == SHAPETYPE_POLYGON:
1350 dc.DrawRectangle(x, y, w, h)
1351
1352 class ClassRenderer(wxPyGridCellRenderer):
1353
1354 def __init__(self, shapeType):
1355 wxPyGridCellRenderer.__init__(self)
1356 self.shapeType = shapeType
1357 self.previewer = ClassDataPreviewer()
1358
1359 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1360 data = grid.GetTable().GetClassGroup(row)
1361
1362 dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1363 rect.GetWidth(), rect.GetHeight())
1364 dc.SetPen(wxPen(wxLIGHT_GREY))
1365 dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1366 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1367 rect.GetWidth(), rect.GetHeight())
1368
1369 if not isinstance(data, ClassGroupMap):
1370 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1371
1372 if isSelected:
1373 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1374 dc.SetBrush(wxTRANSPARENT_BRUSH)
1375
1376 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1377 rect.GetWidth(), rect.GetHeight())
1378
1379 dc.DestroyClippingRegion()
1380
1381
1382 class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1383
1384 def __init__(self, parent, id, props, shapeType,
1385 size = wxDefaultSize, style = 0):
1386
1387 wxWindow.__init__(self, parent, id, size = size, style = style)
1388
1389 self.parent = parent
1390
1391 self.SetProperties(props)
1392 self.SetShapeType(shapeType)
1393 self.AllowEdit(True)
1394
1395 EVT_PAINT(self, self._OnPaint)
1396 EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1397
1398 self.previewer = ClassDataPreviewer()
1399
1400 def _OnPaint(self, event):
1401 dc = wxPaintDC(self)
1402
1403 # XXX: this doesn't seem to be having an effect:
1404 dc.DestroyClippingRegion()
1405
1406 w, h = self.GetClientSize()
1407
1408 self.previewer.Draw(dc,
1409 wxRect(0, 0, w, h),
1410 self.GetProperties(),
1411 self.GetShapeType())
1412
1413
1414 def GetProperties(self):
1415 return self.props
1416
1417 def SetProperties(self, props):
1418 self.props = props
1419 self.Refresh()
1420
1421 def GetShapeType(self):
1422 return self.shapeType
1423
1424 def SetShapeType(self, shapeType):
1425 self.shapeType = shapeType
1426 self.Refresh()
1427
1428 def AllowEdit(self, allow):
1429 self.allowEdit = allow
1430
1431 def DoEdit(self):
1432 if not self.allowEdit: return
1433
1434 propDlg = SelectPropertiesDialog(self.parent,
1435 self.GetProperties(),
1436 self.GetShapeType())
1437
1438 if propDlg.ShowModal() == wxID_OK:
1439 new_prop = propDlg.GetClassGroupProperties()
1440 self.SetProperties(new_prop)
1441 self.Refresh()
1442
1443 propDlg.Destroy()
1444
1445 def _OnLeftDClick(self, event):
1446 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