/[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 671 - (show annotations)
Tue Apr 15 18:09:47 2003 UTC (21 years, 10 months ago) by bh
File MIME type: text/x-python
File size: 44544 byte(s)
* Thuban/__init__.py (_): Implement the translation function for
real using the python gettext module.

* Thuban/UI/classifier.py (ClassTable.GetRowLabelValue): Don't
translate empty strings.

* Thuban/UI/application.py (ThubanApplication.read_startup_files):
Add a missing space to a warning message

* po/README: New. Notes about the management of the translation
files.

* po/Makefile: New. Makefile to help manage the translation files.

* po/es.po: New. Spanish translation by Daniel Calvelo Aros

* MANIFEST.in: Include the *.mo files in Resources/Locale and the
translations and support files in po/

* setup.py (data_files): Add the *.mo files to the data_files too

* README: Add note about the translations when building from CVS

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