/[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 1447 - (show annotations)
Thu Jul 17 14:59:29 2003 UTC (21 years, 7 months ago) by jonathan
File MIME type: text/x-python
File size: 46354 byte(s)
(Classifier.__BuildClassification):
        Return both the new class and the field name.

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