/[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 1307 - (show annotations)
Thu Jun 26 17:00:17 2003 UTC (21 years, 8 months ago) by jonathan
File MIME type: text/x-python
File size: 46429 byte(s)
(Classifier.__EnableButtons):
        Reset the status of the buttons as the situation warrants,
        but in a better more reliable way by not relying on the
        current status to determine what needs to change.

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 Color
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_ok.SetDefault()
762 button_close = wxButton(self, wxID_CANCEL, _("Close"))
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.SetField(fieldName)
913 clazz.SetFieldType(fieldType)
914
915
916 # table = self.classGrid.GetTable()
917 # clazz.SetDefaultGroup(table.GetClassGroup(0))
918
919 # for i in range(1, numRows):
920 # clazz.AppendGroup(table.GetClassGroup(i))
921
922 return clazz
923
924 def __SetGridTable(self, fieldIndex, group = None):
925
926 clazz = self.fields.GetClientData(fieldIndex)
927
928 if clazz is None:
929 clazz = Classification()
930 clazz.SetDefaultGroup(
931 ClassGroupDefault(
932 self.layer.GetClassification().
933 GetDefaultGroup().GetProperties()))
934
935 fieldName = self.fields.GetString(fieldIndex)
936 fieldType = self.layer.GetFieldType(fieldName)
937 clazz.SetFieldType(fieldType)
938
939 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
940
941 def __SetFieldTypeText(self, fieldIndex):
942 fieldName = self.fields.GetString(fieldIndex)
943 fieldType = self.layer.GetFieldType(fieldName)
944
945 assert Classifier.type2string.has_key(fieldType)
946
947 text = Classifier.type2string[fieldType]
948
949 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
950
951 def __SelectField(self, newIndex, oldIndex = -1, group = None):
952 """This method assumes that the current selection for the
953 combo has already been set by a call to SetSelection().
954 """
955
956 assert oldIndex >= -1
957
958 if oldIndex != -1:
959 clazz = self.__BuildClassification(oldIndex)
960 self.fields.SetClientData(oldIndex, clazz)
961
962 self.__SetGridTable(newIndex, group)
963
964 self.__EnableButtons(EB_SELECT_FIELD)
965
966 self.__SetFieldTypeText(newIndex)
967
968 def __SetTitle(self, title):
969 if title != "":
970 title = ": " + title
971
972 self.SetTitle(_("Layer Properties") + title)
973
974 def _OnEditSymbol(self, event):
975 sel = self.classGrid.GetCurrentSelection()
976
977 if len(sel) == 1:
978 self.EditSymbol(sel[0])
979
980 def _OnFieldSelect(self, event):
981 index = self.fields.GetSelection()
982 self.__SelectField(index, self.__cur_field)
983 self.__cur_field = index
984
985 def _OnTry(self, event):
986 """Put the data from the table into a new Classification and hand
987 it to the layer.
988 """
989
990 if self.layer.HasClassification():
991 clazz = self.fields.GetClientData(self.__cur_field)
992
993 #
994 # only build the classification if there wasn't one to
995 # to begin with or it has been modified
996 #
997 self.classGrid.SaveEditControlValue()
998 if clazz is None or self.classGrid.GetTable().IsModified():
999 clazz = self.__BuildClassification(self.__cur_field, True)
1000
1001 self.layer.SetClassification(clazz)
1002
1003 self.haveApplied = True
1004
1005 def _OnOK(self, event):
1006 self._OnTry(event)
1007 self.Close()
1008
1009 def OnClose(self, event):
1010 self.unsubscribe_messages()
1011 NonModalNonParentDialog.OnClose(self, event)
1012
1013 def _OnCloseBtn(self, event):
1014 """Close is similar to Cancel except that any changes that were
1015 made and applied remain applied, but the currently displayed
1016 classification is discarded.
1017 """
1018
1019 self.Close()
1020
1021 def _OnRevert(self, event):
1022 """The layer's current classification stays the same."""
1023 if self.haveApplied:
1024 self.layer.SetClassification(self.originalClass)
1025
1026 #self.Close()
1027
1028 def _OnAdd(self, event):
1029 self.classGrid.AppendRows()
1030
1031 def _OnRemove(self, event):
1032 self.classGrid.DeleteSelectedRows()
1033
1034 def _OnGenClass(self, event):
1035
1036 self.genDlg = ClassGenDialog(self, self.layer,
1037 self.fields.GetString(self.__cur_field))
1038
1039 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1040
1041 self.__EnableButtons(EB_GEN_CLASS)
1042
1043 self.genDlg.Show()
1044
1045 def _OnGenDialogClose(self, event):
1046 self.genDlg.Destroy()
1047 self.genDlg = None
1048 self.__EnableButtons(EB_GEN_CLASS)
1049
1050 def _OnMoveUp(self, event):
1051 sel = self.classGrid.GetCurrentSelection()
1052
1053 if len(sel) == 1:
1054 i = sel[0]
1055 if i > 1:
1056 table = self.classGrid.GetTable()
1057 x = table.GetClassGroup(i - 1)
1058 y = table.GetClassGroup(i)
1059 table.SetClassGroup(i - 1, y)
1060 table.SetClassGroup(i, x)
1061 self.classGrid.ClearSelection()
1062 self.classGrid.SelectRow(i - 1)
1063 self.classGrid.MakeCellVisible(i - 1, 0)
1064
1065 def _OnMoveDown(self, event):
1066 sel = self.classGrid.GetCurrentSelection()
1067
1068 if len(sel) == 1:
1069 i = sel[0]
1070 table = self.classGrid.GetTable()
1071 if 0 < i < table.GetNumberRows() - 1:
1072 x = table.GetClassGroup(i)
1073 y = table.GetClassGroup(i + 1)
1074 table.SetClassGroup(i, y)
1075 table.SetClassGroup(i + 1, x)
1076 self.classGrid.ClearSelection()
1077 self.classGrid.SelectRow(i + 1)
1078 self.classGrid.MakeCellVisible(i + 1, 0)
1079
1080 def _OnTitleChanged(self, event):
1081 obj = event.GetEventObject()
1082
1083 self.layer.SetTitle(obj.GetValue())
1084 self.__SetTitle(self.layer.Title())
1085
1086 self.__EnableButtons(EB_LAYER_TITLE)
1087
1088 def __EnableButtons(self, case):
1089
1090 list = {wxID_OK : True,
1091 wxID_CANCEL : True,
1092 ID_PROPERTY_ADD : True,
1093 ID_PROPERTY_MOVEUP : True,
1094 ID_PROPERTY_MOVEDOWN : True,
1095 ID_PROPERTY_REMOVE : True,
1096 ID_PROPERTY_SELECT : True,
1097 ID_PROPERTY_FIELDTEXT : True,
1098 ID_PROPERTY_GENCLASS : True,
1099 ID_PROPERTY_EDITSYM : True}
1100
1101 if case == EB_LAYER_TITLE:
1102 if self.layer.Title() == "":
1103 list[wxID_OK] = False
1104 list[wxID_CANCEL] = False
1105
1106 elif case == EB_SELECT_FIELD:
1107 if self.fields.GetSelection() == 0:
1108 list[ID_PROPERTY_GENCLASS] = False
1109 list[ID_PROPERTY_ADD] = False
1110 list[ID_PROPERTY_MOVEUP] = False
1111 list[ID_PROPERTY_MOVEDOWN] = False
1112 list[ID_PROPERTY_REMOVE] = False
1113
1114 elif case == EB_GEN_CLASS:
1115 if self.genDlg is not None:
1116 list[ID_PROPERTY_SELECT] = False
1117 list[ID_PROPERTY_FIELDTEXT] = False
1118 list[ID_PROPERTY_GENCLASS] = False
1119
1120 for id, enable in list.items():
1121 win = self.FindWindowById(id)
1122 if win:
1123 win.Enable(enable)
1124
1125 ID_SELPROP_SPINCTRL = 4002
1126 ID_SELPROP_PREVIEW = 4003
1127 ID_SELPROP_STROKECLR = 4004
1128 ID_SELPROP_FILLCLR = 4005
1129 ID_SELPROP_STROKECLRTRANS = 4006
1130 ID_SELPROP_FILLCLRTRANS = 4007
1131
1132 class SelectPropertiesDialog(wxDialog):
1133
1134 def __init__(self, parent, prop, shapeType):
1135 wxDialog.__init__(self, parent, -1, _("Select Properties"),
1136 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1137
1138 self.prop = ClassGroupProperties(prop)
1139
1140 topBox = wxBoxSizer(wxVERTICAL)
1141
1142 itemBox = wxBoxSizer(wxHORIZONTAL)
1143
1144 # preview box
1145 previewBox = wxBoxSizer(wxVERTICAL)
1146 previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1147 0, wxALIGN_LEFT | wxALL, 4)
1148
1149 self.previewWin = ClassGroupPropertiesCtrl(
1150 self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1151 (40, 40), wxSIMPLE_BORDER)
1152
1153 self.previewWin.AllowEdit(False)
1154
1155 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1156
1157 itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1158
1159 # control box
1160 ctrlBox = wxBoxSizer(wxVERTICAL)
1161
1162 lineColorBox = wxBoxSizer(wxHORIZONTAL)
1163 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1164 button.SetFocus()
1165 lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1166 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1167
1168 lineColorBox.Add(
1169 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1170 1, wxALL | wxGROW, 4)
1171 EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1172 self._OnChangeLineColorTrans)
1173
1174 ctrlBox.Add(lineColorBox, 0,
1175 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1176
1177 if shapeType != SHAPETYPE_ARC:
1178 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1179 fillColorBox.Add(
1180 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1181 1, wxALL | wxGROW, 4)
1182 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1183 fillColorBox.Add(
1184 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1185 1, wxALL | wxGROW, 4)
1186 EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1187 self._OnChangeFillColorTrans)
1188 ctrlBox.Add(fillColorBox, 0,
1189 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1190
1191 spinBox = wxBoxSizer(wxHORIZONTAL)
1192 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1193 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1194 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1195 min=1, max=10,
1196 value=str(prop.GetLineWidth()),
1197 initial=prop.GetLineWidth())
1198
1199 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1200
1201 spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1202
1203 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1204 itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1205 topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1206
1207 #
1208 # Control buttons:
1209 #
1210 buttonBox = wxBoxSizer(wxHORIZONTAL)
1211 button_ok = wxButton(self, wxID_OK, _("OK"))
1212 button_ok.SetDefault()
1213 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1214 buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1215 0, wxRIGHT|wxEXPAND, 10)
1216 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
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 Color.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(Color.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(Color.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 Color.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 Color.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