/[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 1207 - (show annotations)
Fri Jun 13 18:16:10 2003 UTC (21 years, 8 months ago) by bh
File MIME type: text/x-python
File size: 45707 byte(s)
(Classifier.OnClose)
(Classifier.map_layers_removed)
(Classifier.layer_shapestore_replaced): Unsubscribe the messages
in OnClose and not in map_layers_removed or
layer_shapestore_replaced to make sure it always happens when the
dialog is closed

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 #
710 # make field choice box
711 #
712 self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
713
714 self.num_cols = layer.table.NumColumns()
715 # just assume the first field in case one hasn't been
716 # specified in the file.
717 self.__cur_field = 0
718
719 self.fields.Append("<None>")
720
721 if self.originalClass.GetFieldType() is None:
722 self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
723 else:
724 self.fields.SetClientData(0, None)
725
726 for i in range(self.num_cols):
727 name = layer.table.Column(i).name
728 self.fields.Append(name)
729
730 if name == field:
731 self.__cur_field = i + 1
732 self.fields.SetClientData(i + 1,
733 copy.deepcopy(self.originalClass))
734 else:
735 self.fields.SetClientData(i + 1, None)
736
737 button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
738 _("Generate Class"))
739 button_add = wxButton(panel, ID_PROPERTY_ADD,
740 _("Add"))
741 button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
742 _("Move Up"))
743 button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
744 _("Move Down"))
745 button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
746 _("Edit Symbol"))
747 button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
748 _("Remove"))
749
750 self.classGrid = ClassGrid(panel, self)
751
752 # calling __SelectField after creating the classGrid fills in the
753 # grid with the correct information
754 self.fields.SetSelection(self.__cur_field)
755 self.__SelectField(self.__cur_field, group = group)
756
757 button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))
758 button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))
759 button_ok = wxButton(self, wxID_OK, _("OK"))
760 button_ok.SetDefault()
761 button_close = wxButton(self, wxID_CANCEL, _("Close"))
762
763 ############################
764 # Layout the controls
765 #
766
767 topBox = wxBoxSizer(wxVERTICAL)
768 panelBox = wxBoxSizer(wxVERTICAL)
769
770 sizer = wxBoxSizer(wxHORIZONTAL)
771 sizer.Add(wxStaticText(panel, -1, _("Title: ")),
772 0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
773 sizer.Add(text_title, 1, wxGROW, 0)
774
775 panelBox.Add(sizer, 0, wxGROW, 4)
776
777 if isinstance(layer, RasterLayer):
778 type = "Image"
779 else:
780 type = layer.ShapeType()
781
782 panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
783 0, wxALIGN_LEFT | wxALL, 4)
784
785 if layer.HasClassification():
786
787 classBox = wxStaticBoxSizer(
788 wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
789
790
791 sizer = wxBoxSizer(wxHORIZONTAL)
792 sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
793 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
794 sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
795
796 classBox.Add(sizer, 0, wxGROW, 4)
797
798 classBox.Add(self.fieldTypeText, 0,
799 wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
800
801 controlBox = wxBoxSizer(wxHORIZONTAL)
802 controlButtonBox = wxBoxSizer(wxVERTICAL)
803
804 controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
805 controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
806 controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
807 controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
808 controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
809 controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
810 controlButtonBox.Add(button_remove, 0,
811 wxGROW|wxALL|wxALIGN_BOTTOM, 4)
812
813 controlBox.Add(self.classGrid, 1, wxGROW, 0)
814 controlBox.Add(controlButtonBox, 0, wxGROW, 10)
815
816 classBox.Add(controlBox, 1, wxGROW, 10)
817 panelBox.Add(classBox, 1, wxGROW, 0)
818
819
820 buttonBox = wxBoxSizer(wxHORIZONTAL)
821 buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)
822 buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)
823 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
824 buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)
825
826 panel.SetAutoLayout(True)
827 panel.SetSizer(panelBox)
828 panelBox.Fit(panel)
829 panelBox.SetSizeHints(panel)
830
831 topBox.Add(panel, 1, wxGROW | wxALL, 4)
832 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
833
834 self.SetAutoLayout(True)
835 self.SetSizer(topBox)
836 topBox.Fit(self)
837 topBox.SetSizeHints(self)
838 self.Layout()
839
840 ###########
841
842 EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
843 EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
844 EVT_BUTTON(self, wxID_OK, self._OnOK)
845 EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
846 EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
847 EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
848
849 EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
850 EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
851 EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
852 EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
853 EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
854 EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
855
856 ######################
857
858 text_title.SetFocus()
859 self.haveApplied = False
860
861 def unsubscribe_messages(self):
862 self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
863 self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
864 self.layer_shapestore_replaced)
865
866 def map_layers_removed(self, map):
867 if self.layer not in self.map.Layers():
868 self.Close()
869
870 def layer_shapestore_replaced(self, *args):
871 self.Close()
872
873 def EditSymbol(self, row):
874 table = self.classGrid.GetTable()
875 prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
876
877 # get a new ClassGroupProperties object and copy the
878 # values over to our current object
879 propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
880
881 self.Enable(False)
882 if propDlg.ShowModal() == wxID_OK:
883 new_prop = propDlg.GetClassGroupProperties()
884 table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
885 self.Enable(True)
886 propDlg.Destroy()
887
888 def _SetClassification(self, clazz):
889
890 self.fields.SetClientData(self.__cur_field, clazz)
891 self.classGrid.GetTable().SetClassification(clazz)
892
893 def __BuildClassification(self, fieldIndex, copyClass = False):
894
895 # numRows = self.classGrid.GetNumberRows()
896 # assert numRows > 0 # there should always be a default row
897
898 # clazz = Classification()
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 clazz.SetField(fieldName)
912 clazz.SetFieldType(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.SetFieldType(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, newIndex != 0)
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, False)
1041
1042 self.genDlg.Show()
1043
1044 def _OnGenDialogClose(self, event):
1045 self.genDlg.Destroy()
1046 self.__EnableButtons(EB_GEN_CLASS, True)
1047
1048 def _OnMoveUp(self, event):
1049 sel = self.classGrid.GetCurrentSelection()
1050
1051 if len(sel) == 1:
1052 i = sel[0]
1053 if i > 1:
1054 table = self.classGrid.GetTable()
1055 x = table.GetClassGroup(i - 1)
1056 y = table.GetClassGroup(i)
1057 table.SetClassGroup(i - 1, y)
1058 table.SetClassGroup(i, x)
1059 self.classGrid.ClearSelection()
1060 self.classGrid.SelectRow(i - 1)
1061 self.classGrid.MakeCellVisible(i - 1, 0)
1062
1063 def _OnMoveDown(self, event):
1064 sel = self.classGrid.GetCurrentSelection()
1065
1066 if len(sel) == 1:
1067 i = sel[0]
1068 table = self.classGrid.GetTable()
1069 if 0 < i < table.GetNumberRows() - 1:
1070 x = table.GetClassGroup(i)
1071 y = table.GetClassGroup(i + 1)
1072 table.SetClassGroup(i, y)
1073 table.SetClassGroup(i + 1, x)
1074 self.classGrid.ClearSelection()
1075 self.classGrid.SelectRow(i + 1)
1076 self.classGrid.MakeCellVisible(i + 1, 0)
1077
1078 def _OnTitleChanged(self, event):
1079 obj = event.GetEventObject()
1080
1081 self.layer.SetTitle(obj.GetValue())
1082 self.__SetTitle(self.layer.Title())
1083
1084 self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1085
1086 def __EnableButtons(self, case, enable):
1087
1088 if case == EB_LAYER_TITLE:
1089 list = (wxID_OK,
1090 wxID_CANCEL)
1091
1092 elif case == EB_SELECT_FIELD:
1093 list = (ID_PROPERTY_GENCLASS,
1094 ID_PROPERTY_ADD,
1095 ID_PROPERTY_MOVEUP,
1096 ID_PROPERTY_MOVEDOWN,
1097 ID_PROPERTY_EDITSYM,
1098 ID_PROPERTY_REMOVE)
1099
1100 elif case == EB_GEN_CLASS:
1101 list = (ID_PROPERTY_SELECT,
1102 ID_PROPERTY_FIELDTEXT,
1103 ID_PROPERTY_GENCLASS,
1104 ID_PROPERTY_EDITSYM)
1105
1106 for id in list:
1107 self.FindWindowById(id).Enable(enable)
1108
1109 ID_SELPROP_SPINCTRL = 4002
1110 ID_SELPROP_PREVIEW = 4003
1111 ID_SELPROP_STROKECLR = 4004
1112 ID_SELPROP_FILLCLR = 4005
1113 ID_SELPROP_STROKECLRTRANS = 4006
1114 ID_SELPROP_FILLCLRTRANS = 4007
1115
1116 class SelectPropertiesDialog(wxDialog):
1117
1118 def __init__(self, parent, prop, shapeType):
1119 wxDialog.__init__(self, parent, -1, _("Select Properties"),
1120 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1121
1122 self.prop = ClassGroupProperties(prop)
1123
1124 topBox = wxBoxSizer(wxVERTICAL)
1125
1126 itemBox = wxBoxSizer(wxHORIZONTAL)
1127
1128 # preview box
1129 previewBox = wxBoxSizer(wxVERTICAL)
1130 previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1131 0, wxALIGN_LEFT | wxALL, 4)
1132
1133 self.previewWin = ClassGroupPropertiesCtrl(
1134 self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1135 (40, 40), wxSIMPLE_BORDER)
1136
1137 self.previewWin.AllowEdit(False)
1138
1139 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1140
1141 itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1142
1143 # control box
1144 ctrlBox = wxBoxSizer(wxVERTICAL)
1145
1146 lineColorBox = wxBoxSizer(wxHORIZONTAL)
1147 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1148 button.SetFocus()
1149 lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1150 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1151
1152 lineColorBox.Add(
1153 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1154 1, wxALL | wxGROW, 4)
1155 EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1156 self._OnChangeLineColorTrans)
1157
1158 ctrlBox.Add(lineColorBox, 0,
1159 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1160
1161 if shapeType != SHAPETYPE_ARC:
1162 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1163 fillColorBox.Add(
1164 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1165 1, wxALL | wxGROW, 4)
1166 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1167 fillColorBox.Add(
1168 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1169 1, wxALL | wxGROW, 4)
1170 EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1171 self._OnChangeFillColorTrans)
1172 ctrlBox.Add(fillColorBox, 0,
1173 wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1174
1175 spinBox = wxBoxSizer(wxHORIZONTAL)
1176 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1177 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1178 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1179 min=1, max=10,
1180 value=str(prop.GetLineWidth()),
1181 initial=prop.GetLineWidth())
1182
1183 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1184
1185 spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1186
1187 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1188 itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1189 topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1190
1191 #
1192 # Control buttons:
1193 #
1194 buttonBox = wxBoxSizer(wxHORIZONTAL)
1195 button_ok = wxButton(self, wxID_OK, _("OK"))
1196 button_ok.SetDefault()
1197 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1198 buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1199 0, wxRIGHT|wxEXPAND, 10)
1200 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1201
1202 #EVT_BUTTON(self, wxID_OK, self._OnOK)
1203 #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1204
1205 self.SetAutoLayout(True)
1206 self.SetSizer(topBox)
1207 topBox.Fit(self)
1208 topBox.SetSizeHints(self)
1209
1210 def OnOK(self, event):
1211 self.EndModal(wxID_OK)
1212
1213 def OnCancel(self, event):
1214 self.EndModal(wxID_CANCEL)
1215
1216 def _OnSpin(self, event):
1217 self.prop.SetLineWidth(self.spinCtrl.GetValue())
1218 self.previewWin.Refresh()
1219
1220 def __GetColor(self, cur):
1221 dialog = wxColourDialog(self)
1222 if cur is not Color.Transparent:
1223 dialog.GetColourData().SetColour(Color2wxColour(cur))
1224
1225 ret = None
1226 if dialog.ShowModal() == wxID_OK:
1227 ret = wxColour2Color(dialog.GetColourData().GetColour())
1228
1229 dialog.Destroy()
1230
1231 return ret
1232
1233 def _OnChangeLineColor(self, event):
1234 clr = self.__GetColor(self.prop.GetLineColor())
1235 if clr is not None:
1236 self.prop.SetLineColor(clr)
1237 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1238
1239 def _OnChangeLineColorTrans(self, event):
1240 self.prop.SetLineColor(Color.Transparent)
1241 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1242
1243 def _OnChangeFillColor(self, event):
1244 clr = self.__GetColor(self.prop.GetFill())
1245 if clr is not None:
1246 self.prop.SetFill(clr)
1247 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1248
1249 def _OnChangeFillColorTrans(self, event):
1250 self.prop.SetFill(Color.Transparent)
1251 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1252
1253 def GetClassGroupProperties(self):
1254 return self.prop
1255
1256
1257 class ClassDataPreviewWindow(wxWindow):
1258
1259 def __init__(self, rect, prop, shapeType,
1260 parent = None, id = -1, size = wxDefaultSize):
1261 if parent is not None:
1262 wxWindow.__init__(self, parent, id, (0, 0), size)
1263 EVT_PAINT(self, self._OnPaint)
1264
1265 self.rect = rect
1266
1267 self.prop = prop
1268 self.shapeType = shapeType
1269 self.previewer = ClassDataPreviewer()
1270
1271 def GetProperties():
1272 return self.prop
1273
1274 def _OnPaint(self, event):
1275 dc = wxPaintDC(self)
1276
1277 # XXX: this doesn't seem to be having an effect:
1278 dc.DestroyClippingRegion()
1279
1280 if self.rect is None:
1281 w, h = self.GetSize()
1282 rect = wxRect(0, 0, w, h)
1283 else:
1284 rect = self.rect
1285
1286 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1287
1288 class ClassDataPreviewer:
1289
1290 def Draw(self, dc, rect, prop, shapeType):
1291
1292 assert dc is not None
1293 assert isinstance(prop, ClassGroupProperties)
1294
1295 if rect is None:
1296 x = 0
1297 y = 0
1298 w, h = dc.GetSize()
1299 else:
1300 x = rect.GetX()
1301 y = rect.GetY()
1302 w = rect.GetWidth()
1303 h = rect.GetHeight()
1304
1305 stroke = prop.GetLineColor()
1306 if stroke is Color.Transparent:
1307 pen = wxTRANSPARENT_PEN
1308 else:
1309 pen = wxPen(Color2wxColour(stroke),
1310 prop.GetLineWidth(),
1311 wxSOLID)
1312
1313 stroke = prop.GetFill()
1314 if stroke is Color.Transparent:
1315 brush = wxTRANSPARENT_BRUSH
1316 else:
1317 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1318
1319 dc.SetPen(pen)
1320 dc.SetBrush(brush)
1321
1322 if shapeType == SHAPETYPE_ARC:
1323 dc.DrawSpline([wxPoint(x, y + h),
1324 wxPoint(x + w/2, y + h/4),
1325 wxPoint(x + w/2, y + h/4*3),
1326 wxPoint(x + w, y)])
1327
1328 elif shapeType == SHAPETYPE_POINT:
1329
1330 dc.DrawCircle(x + w/2, y + h/2,
1331 (min(w, h) - prop.GetLineWidth())/2)
1332
1333 elif shapeType == SHAPETYPE_POLYGON:
1334 dc.DrawRectangle(x, y, w, h)
1335
1336 class ClassRenderer(wxPyGridCellRenderer):
1337
1338 def __init__(self, shapeType):
1339 wxPyGridCellRenderer.__init__(self)
1340 self.shapeType = shapeType
1341 self.previewer = ClassDataPreviewer()
1342
1343 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1344 data = grid.GetTable().GetClassGroup(row)
1345
1346 dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1347 rect.GetWidth(), rect.GetHeight())
1348 dc.SetPen(wxPen(wxLIGHT_GREY))
1349 dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1350 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1351 rect.GetWidth(), rect.GetHeight())
1352
1353 if not isinstance(data, ClassGroupMap):
1354 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1355
1356 if isSelected:
1357 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1358 dc.SetBrush(wxTRANSPARENT_BRUSH)
1359
1360 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1361 rect.GetWidth(), rect.GetHeight())
1362
1363 dc.DestroyClippingRegion()
1364
1365
1366 class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1367
1368 def __init__(self, parent, id, props, shapeType,
1369 size = wxDefaultSize, style = 0):
1370
1371 wxWindow.__init__(self, parent, id, size = size, style = style)
1372
1373 self.SetProperties(props)
1374 self.SetShapeType(shapeType)
1375 self.AllowEdit(True)
1376
1377 EVT_PAINT(self, self._OnPaint)
1378 EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1379
1380 self.previewer = ClassDataPreviewer()
1381
1382 def _OnPaint(self, event):
1383 dc = wxPaintDC(self)
1384
1385 # XXX: this doesn't seem to be having an effect:
1386 dc.DestroyClippingRegion()
1387
1388 w, h = self.GetClientSize()
1389
1390 self.previewer.Draw(dc,
1391 wxRect(0, 0, w, h),
1392 self.GetProperties(),
1393 self.GetShapeType())
1394
1395
1396 def GetProperties(self):
1397 return self.props
1398
1399 def SetProperties(self, props):
1400 self.props = props
1401 self.Refresh()
1402
1403 def GetShapeType(self):
1404 return self.shapeType
1405
1406 def SetShapeType(self, shapeType):
1407 self.shapeType = shapeType
1408 self.Refresh()
1409
1410 def AllowEdit(self, allow):
1411 self.allowEdit = allow
1412
1413 def DoEdit(self):
1414 if not self.allowEdit: return
1415
1416 propDlg = SelectPropertiesDialog(NULL,
1417 self.GetProperties(),
1418 self.GetShapeType())
1419
1420 if propDlg.ShowModal() == wxID_OK:
1421 new_prop = propDlg.GetClassGroupProperties()
1422 self.SetProperties(new_prop)
1423 self.Refresh()
1424
1425 propDlg.Destroy()
1426
1427 def _OnLeftDClick(self, event):
1428 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