/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1207 - (hide annotations)
Fri Jun 13 18:16:10 2003 UTC (21 years, 8 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/classifier.py
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 bh 476 # Copyright (c) 2001, 2003 by Intevation GmbH
2 jonathan 372 # 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 jonathan 376 import copy
13    
14 jonathan 473 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
15     FIELDTYPE_STRING
16    
17 jonathan 372 from wxPython.wx import *
18     from wxPython.grid import *
19    
20 jan 374 from Thuban import _
21 jonathan 878 from Thuban.UI.common import Color2wxColour, wxColour2Color
22 jan 374
23 bh 1142 from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
24 jonathan 878 from Thuban.Model.range import Range
25     from Thuban.Model.classification import \
26     Classification, ClassGroupDefault, \
27     ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
28     ClassGroupProperties
29 jonathan 392
30 jonathan 415 from Thuban.Model.color import Color
31    
32 jonathan 935 from Thuban.Model.layer import Layer, RasterLayer, \
33 jonathan 878 SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
34 jonathan 392
35 jonathan 1101 from Thuban.UI.classgen import ClassGenDialog
36 jonathan 606
37 frank 1058 from dialogs import NonModalNonParentDialog
38 jonathan 485
39 jonathan 372 ID_CLASS_TABLE = 40011
40    
41    
42 jonathan 460 # table columns
43 jonathan 638 COL_VISIBLE = 0
44     COL_SYMBOL = 1
45     COL_VALUE = 2
46     COL_LABEL = 3
47     NUM_COLS = 4
48 jonathan 415
49 jonathan 460 # indices into the client data lists in Classifier.fields
50 jonathan 451 FIELD_CLASS = 0
51     FIELD_TYPE = 1
52     FIELD_NAME = 2
53    
54 jonathan 415 #
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 jonathan 570
62 jonathan 606 def __init__(self, parent, classifier):
63 jonathan 460 """Constructor.
64    
65     parent -- the parent window
66    
67     clazz -- the working classification that this grid should
68     use for display.
69     """
70    
71 jonathan 813 wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
72 jonathan 415
73 jonathan 606 self.classifier = classifier
74    
75 jonathan 570 self.currentSelection = []
76    
77 jonathan 460 EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
78 jonathan 451 EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
79     EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
80 jonathan 606 EVT_GRID_COL_SIZE(self, self._OnCellResize)
81     EVT_GRID_ROW_SIZE(self, self._OnCellResize)
82 jonathan 451
83 jonathan 638 #def GetCellAttr(self, row, col):
84     #print "GetCellAttr ", row, col
85     #wxGrid.GetCellAttr(self, row, col)
86    
87 jonathan 570 def CreateTable(self, clazz, shapeType, group = None):
88    
89 jonathan 606 assert isinstance(clazz, Classification)
90 jonathan 460
91     table = self.GetTable()
92     if table is None:
93 jonathan 650 w = self.GetDefaultColSize() * NUM_COLS \
94     + self.GetDefaultRowLabelSize()
95     h = self.GetDefaultRowSize() * 4 \
96     + self.GetDefaultColLabelSize()
97    
98 jonathan 500 self.SetDimensions(-1, -1, w, h)
99     self.SetSizeHints(w, h, -1, -1)
100 jonathan 570 table = ClassTable(self)
101     self.SetTable(table, True)
102 jonathan 460
103 jonathan 570
104 bh 476 self.SetSelectionMode(wxGrid.wxGridSelectRows)
105 jonathan 460 self.ClearSelection()
106    
107 jonathan 606 table.Reset(clazz, shapeType, group)
108 jonathan 570
109 jonathan 451 def GetCurrentSelection(self):
110 jonathan 460 """Return the currently highlighted rows as an increasing list
111     of row numbers."""
112 jonathan 451 sel = copy.copy(self.currentSelection)
113     sel.sort()
114     return sel
115    
116 jonathan 570 def GetSelectedRows(self):
117     return self.GetCurrentSelection()
118    
119 jonathan 638 #def SetCellRenderer(self, row, col, renderer):
120     #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
121 jonathan 415
122 jonathan 460 #
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 jonathan 415 def SetTable(self, object, *attributes):
129     self.tableRef = weakref.ref(object)
130     return wxGrid.SetTable(self, object, *attributes)
131    
132     def GetTable(self):
133 jonathan 460 try:
134     return self.tableRef()
135     except:
136     return None
137 jonathan 415
138 jonathan 451 def DeleteSelectedRows(self):
139 jonathan 460 """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 jonathan 451 sel = self.GetCurrentSelection()
145 jonathan 415
146 jonathan 460 # 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 jonathan 451 if len(sel) == 1:
152 jonathan 485 #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
153     group = self.GetTable().GetClassGroup(sel[0])
154 jonathan 451 if isinstance(group, ClassGroupDefault):
155     wxMessageDialog(self,
156     "The Default group cannot be removed.",
157     style = wxOK | wxICON_EXCLAMATION).ShowModal()
158     return
159    
160 jonathan 460
161 jonathan 451 self.ClearSelection()
162    
163 jonathan 460 # 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 jonathan 451 sel.reverse()
166 jonathan 460
167     #
168     # actually remove the rows
169     #
170 jonathan 451 table = self.GetTable()
171     for row in sel:
172     table.DeleteRows(row)
173    
174 jonathan 460 #
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 jonathan 451 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 jonathan 570
186     def SelectGroup(self, group, makeVisible = True):
187     if group is None: return
188    
189 jonathan 606 assert isinstance(group, ClassGroup)
190 jonathan 570
191     table = self.GetTable()
192    
193 jonathan 606 assert table is not None
194 jonathan 570
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 jonathan 451 #
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 jonathan 460 def _OnCellDClick(self, event):
215 jonathan 638 """Handle a double click on a cell."""
216 jonathan 460
217 jonathan 451 r = event.GetRow()
218     c = event.GetCol()
219 jonathan 638
220 jonathan 460 if c == COL_SYMBOL:
221 jonathan 638 self.classifier.EditSymbol(r)
222     else:
223     event.Skip()
224 jonathan 460
225 jonathan 451 #
226     # _OnSelectedRange() and _OnSelectedCell() were borrowed
227 jonathan 460 # from http://wiki.wxpython.org to keep track of which
228     # cells are currently highlighted
229 jonathan 451 #
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 jonathan 606 def _OnCellResize(self, event):
251     self.FitInside()
252 jonathan 878 event.Skip()
253 jonathan 606
254 jonathan 376 class ClassTable(wxPyGridTableBase):
255 jonathan 460 """Represents the underlying data structure for the grid."""
256 jonathan 376
257 jonathan 638 __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
258 jonathan 415
259    
260 jonathan 570 def __init__(self, view = None):
261     #def __init__(self, clazz, shapeType, view = None):
262 jonathan 460 """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 jonathan 376 wxPyGridTableBase.__init__(self)
270 jonathan 485
271 jonathan 638 assert len(ClassTable.__col_labels) == NUM_COLS
272    
273 jonathan 615 self.clazz = None
274 jonathan 638 self.__colAttr = {}
275 jonathan 376
276 jonathan 638 self.SetView(view)
277 jonathan 415
278 jonathan 570 def Reset(self, clazz, shapeType, group = None):
279 jonathan 460 """Reset the table with the given data.
280 jonathan 415
281 jonathan 460 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 jonathan 606 assert isinstance(clazz, Classification)
293 jonathan 460
294 jonathan 415 self.GetView().BeginBatch()
295    
296 jonathan 485 self.fieldType = clazz.GetFieldType()
297 jonathan 415 self.shapeType = shapeType
298    
299 jonathan 606 self.SetClassification(clazz, group)
300     self.__Modified(-1)
301 jonathan 415
302 jonathan 638 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 jonathan 606 self.GetView().EndBatch()
316     self.GetView().FitInside()
317    
318 jonathan 615 def GetClassification(self):
319     return self.clazz
320    
321 jonathan 606 def SetClassification(self, clazz, group = None):
322    
323     self.GetView().BeginBatch()
324    
325     old_len = self.GetNumberRows()
326 jonathan 376
327 jonathan 570 row = -1
328 jonathan 615 self.clazz = clazz
329 jonathan 441
330 jonathan 606 self.__NotifyRowChanges(old_len, self.GetNumberRows())
331 jonathan 415
332 jonathan 638 #
333     # XXX: this is dead code at the moment
334     #
335 jonathan 570 if row > -1:
336     self.GetView().ClearSelection()
337     self.GetView().SelectRow(row)
338     self.GetView().MakeCellVisible(row, 0)
339 jonathan 606
340 jonathan 615 self.__Modified()
341    
342 jonathan 638
343 jonathan 451 self.GetView().EndBatch()
344 jonathan 606 self.GetView().FitInside()
345 jonathan 451
346     def __NotifyRowChanges(self, curRows, newRows):
347 jonathan 415 #
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 jonathan 519 self.GetView().FitInside()
357 jonathan 415 elif newRows < curRows:
358     msg = wxGridTableMessage(self,
359     wxGRIDTABLE_NOTIFY_ROWS_DELETED,
360 jonathan 606 curRows, # position
361 jonathan 415 curRows - newRows) # how many
362     self.GetView().ProcessTableMessage(msg)
363 jonathan 519 self.GetView().FitInside()
364 jonathan 415
365 jonathan 615
366 jonathan 441 def __SetRow(self, row, group):
367 jonathan 460 """Set a row's data to that of the group.
368 jonathan 441
369 jonathan 485 The table is considered modified after this operation.
370    
371 jonathan 606 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 jonathan 460 """
376 jonathan 441
377 jonathan 460 # either append or replace
378 jonathan 606 if row is None or row >= self.GetNumberRows():
379 jonathan 615 self.clazz.AppendGroup(group)
380 jonathan 606 elif row < 0:
381 jonathan 615 self.clazz.InsertGroup(0, group)
382 jonathan 441 else:
383 jonathan 615 if row == 0:
384     self.clazz.SetDefaultGroup(group)
385     else:
386     self.clazz.ReplaceGroup(row - 1, group)
387 jonathan 441
388 jonathan 460 self.__Modified()
389    
390 jonathan 415 def GetColLabelValue(self, col):
391 jonathan 460 """Return the label for the given column."""
392 jonathan 415 return self.__col_labels[col]
393    
394     def GetRowLabelValue(self, row):
395 jonathan 460 """Return the label for the given row."""
396 jonathan 415
397 jonathan 615 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 jonathan 460
406 jonathan 606 assert False # shouldn't get here
407 bh 671 return ""
408 jonathan 460
409 jonathan 376 def GetNumberRows(self):
410 jonathan 460 """Return the number of rows."""
411 jonathan 615 if self.clazz is None:
412     return 0
413 jonathan 376
414 jonathan 615 return self.clazz.GetNumGroups() + 1 # +1 for default group
415    
416 jonathan 376 def GetNumberCols(self):
417 jonathan 460 """Return the number of columns."""
418 jonathan 638 return NUM_COLS
419 jonathan 376
420     def IsEmptyCell(self, row, col):
421 jonathan 460 """Determine if a cell is empty. This is always false."""
422     return False
423 jonathan 376
424     def GetValue(self, row, col):
425 jonathan 460 """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 jonathan 376
429     def SetValue(self, row, col, value):
430 jonathan 460 """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 jonathan 415
437 jonathan 392 def GetValueAsCustom(self, row, col, typeName):
438 jonathan 460 """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 jonathan 376
444 jonathan 615 if row == 0:
445     group = self.clazz.GetDefaultGroup()
446     else:
447     group = self.clazz.GetGroup(row - 1)
448 jonathan 460
449 jonathan 615
450 jonathan 638 if col == COL_VISIBLE:
451     return group.IsVisible()
452    
453 jonathan 460 if col == COL_SYMBOL:
454 jonathan 485 return group.GetProperties()
455 jonathan 460
456     if col == COL_LABEL:
457     return group.GetLabel()
458    
459     # col must be COL_VALUE
460 jonathan 606 assert col == COL_VALUE
461 jonathan 460
462     if isinstance(group, ClassGroupDefault):
463     return _("DEFAULT")
464     elif isinstance(group, ClassGroupSingleton):
465     return group.GetValue()
466     elif isinstance(group, ClassGroupRange):
467 jonathan 878 return group.GetRange()
468 jonathan 460
469 jonathan 878 assert False # shouldn't get here
470 jonathan 460 return None
471    
472 jonathan 415 def __ParseInput(self, value):
473     """Try to determine what kind of input value is
474 jonathan 460 (string, number, or range)
475    
476 jonathan 878 Returns a tuple (type, data) where type is 0 if data is
477     a singleton value, or 1 if is a range
478 jonathan 415 """
479 jonathan 392
480 jonathan 485 type = self.fieldType
481 jonathan 415
482 jonathan 460 if type == FIELDTYPE_STRING:
483 jonathan 878 return (0, value)
484 jonathan 630 elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
485 jonathan 460 if type == FIELDTYPE_INT:
486 jonathan 782 # the float call allows the user to enter 1.0 for 1
487 jonathan 460 conv = lambda p: int(float(p))
488     else:
489 jonathan 878 conv = float
490 jonathan 460
491 jonathan 451 #
492     # first try to take the input as a single number
493     # if there's an exception try to break it into
494 jonathan 878 # a range. if there is an exception here, let it
495     # pass up to the calling function.
496 jonathan 451 #
497     try:
498 jonathan 878 return (0, conv(value))
499 jonathan 460 except ValueError:
500 jonathan 878 return (1, Range(value))
501 jonathan 451
502 jonathan 606 assert False # shouldn't get here
503 jonathan 878 return (0,None)
504 jonathan 415
505     def SetValueAsCustom(self, row, col, typeName, value):
506 jonathan 460 """Set the cell specified by 'row' and 'col' to 'value'.
507 jonathan 415
508 jonathan 460 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 jonathan 650 assert 0 <= col < self.GetNumberCols()
518     assert 0 <= row < self.GetNumberRows()
519 jonathan 460
520 jonathan 615 if row == 0:
521     group = self.clazz.GetDefaultGroup()
522     else:
523     group = self.clazz.GetGroup(row - 1)
524 jonathan 460
525 jonathan 485 mod = True # assume the data will change
526 jonathan 460
527 jonathan 638 if col == COL_VISIBLE:
528     group.SetVisible(value)
529     elif col == COL_SYMBOL:
530 jonathan 485 group.SetProperties(value)
531     elif col == COL_LABEL:
532     group.SetLabel(value)
533 jonathan 415 elif col == COL_VALUE:
534 jonathan 451 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 jonathan 460 except ValueError:
544 jonathan 451 # bad input, ignore the request
545 jonathan 485 mod = False
546 jonathan 451 else:
547 jonathan 415
548 jonathan 485 changed = False
549 jonathan 451 ngroup = group
550     props = group.GetProperties()
551 jonathan 460
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 jonathan 878 if dataInfo[0] == 0:
558 jonathan 451 if not isinstance(group, ClassGroupSingleton):
559 jonathan 782 ngroup = ClassGroupSingleton(props = props)
560 jonathan 485 changed = True
561 jonathan 878 ngroup.SetValue(dataInfo[1])
562     elif dataInfo[0] == 1:
563 jonathan 451 if not isinstance(group, ClassGroupRange):
564 jonathan 782 ngroup = ClassGroupRange(props = props)
565 jonathan 485 changed = True
566 jonathan 878 ngroup.SetRange(dataInfo[1])
567 jonathan 415 else:
568 jonathan 606 assert False
569 jonathan 485 pass
570 jonathan 415
571 jonathan 485 if changed:
572     ngroup.SetLabel(group.GetLabel())
573     self.SetClassGroup(row, ngroup)
574     else:
575 jonathan 606 assert False # shouldn't be here
576 jonathan 485 pass
577 jonathan 460
578 jonathan 485 if mod:
579 jonathan 460 self.__Modified()
580     self.GetView().Refresh()
581 jonathan 415
582     def GetAttr(self, row, col, someExtraParameter):
583 jonathan 460 """Returns the cell attributes"""
584    
585 jonathan 638 return self.__colAttr.get(col, wxGridCellAttr()).Clone()
586 jonathan 415
587 jonathan 441 def GetClassGroup(self, row):
588 jonathan 460 """Return the ClassGroup object representing row 'row'."""
589 jonathan 415
590 jonathan 615 #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 jonathan 415
596 jonathan 460 def SetClassGroup(self, row, group):
597 jonathan 485 self.__SetRow(row, group)
598     self.GetView().Refresh()
599 jonathan 460
600     def __Modified(self, mod = True):
601 jonathan 485 """Adjust the modified flag.
602 jonathan 460
603 jonathan 485 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 jonathan 415 def IsModified(self):
614 jonathan 460 """True if this table is considered modified."""
615 jonathan 415 return self.modified
616    
617 jonathan 451 def DeleteRows(self, pos, numRows = 1):
618 jonathan 485 """Deletes 'numRows' beginning at row 'pos'.
619 jonathan 460
620 jonathan 485 The row representing the default group is not removed.
621    
622     The table is considered modified if any rows are removed.
623 jonathan 460 """
624    
625 jonathan 606 assert pos >= 0
626 jonathan 615 old_len = self.GetNumberRows()
627 jonathan 451 for row in range(pos, pos - numRows, -1):
628 jonathan 485 group = self.GetClassGroup(row)
629 jonathan 615 if row != 0:
630     self.clazz.RemoveGroup(row - 1)
631 jonathan 451 self.__Modified()
632    
633     if self.IsModified():
634 jonathan 615 self.__NotifyRowChanges(old_len, self.GetNumberRows())
635 jonathan 415
636 jonathan 451 def AppendRows(self, numRows = 1):
637 jonathan 485 """Append 'numRows' empty rows to the end of the table.
638 jonathan 460
639 jonathan 485 The table is considered modified if any rows are appended.
640     """
641    
642 jonathan 615 old_len = self.GetNumberRows()
643 jonathan 451 for i in range(numRows):
644     np = ClassGroupSingleton()
645 jonathan 606 self.__SetRow(None, np)
646 jonathan 451
647     if self.IsModified():
648 jonathan 615 self.__NotifyRowChanges(old_len, self.GetNumberRows())
649 jonathan 451
650    
651 jonathan 650 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 jonathan 630
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 jonathan 650 EB_LAYER_TITLE = 0
671     EB_SELECT_FIELD = 1
672     EB_GEN_CLASS = 2
673    
674 frank 1058 class Classifier(NonModalNonParentDialog):
675 bh 535
676 jonathan 630 type2string = {None: _("None"),
677     FIELDTYPE_STRING: _("Text"),
678     FIELDTYPE_INT: _("Integer"),
679     FIELDTYPE_DOUBLE: _("Decimal")}
680    
681 bh 1142 def __init__(self, parent, name, map, layer, group = None):
682 frank 1058 NonModalNonParentDialog.__init__(self, parent, name, "")
683 jonathan 372
684 jonathan 650 self.__SetTitle(layer.Title())
685 jonathan 509
686 jonathan 415 self.layer = layer
687 bh 1142 self.map = map
688 jonathan 415
689 bh 1142 self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
690     self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
691     self.layer_shapestore_replaced)
692 jonathan 485
693 jonathan 630 self.genDlg = None
694    
695 jonathan 813 ############################
696     # Create the controls
697     #
698 jonathan 372
699 jonathan 813 panel = wxPanel(self, -1)
700 jonathan 661
701 jonathan 813 text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
702 jonathan 935 self.fieldTypeText = wxStaticText(panel, -1, "")
703 jonathan 372
704 jonathan 935 if layer.HasClassification():
705     self.originalClass = self.layer.GetClassification()
706     field = self.originalClass.GetField()
707     fieldType = self.originalClass.GetFieldType()
708 jonathan 460
709 jonathan 935 #
710     # make field choice box
711     #
712     self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
713 jonathan 460
714 jonathan 935 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 jonathan 650
719 jonathan 935 self.fields.Append("<None>")
720 jonathan 451
721 jonathan 935 if self.originalClass.GetFieldType() is None:
722     self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
723 jonathan 451 else:
724 jonathan 935 self.fields.SetClientData(0, None)
725 jonathan 372
726 jonathan 935 for i in range(self.num_cols):
727     name = layer.table.Column(i).name
728     self.fields.Append(name)
729 jonathan 372
730 jonathan 935 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 jonathan 485
737 jonathan 935 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 jonathan 650
750 jonathan 935 self.classGrid = ClassGrid(panel, self)
751 jonathan 650
752 jonathan 935 # 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 jonathan 650
757 frank 977 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 jonathan 813 button_ok.SetDefault()
761 frank 977 button_close = wxButton(self, wxID_CANCEL, _("Close"))
762 jonathan 485
763 jonathan 813 ############################
764     # Layout the controls
765 jonathan 485 #
766 jonathan 509
767 jonathan 813 topBox = wxBoxSizer(wxVERTICAL)
768     panelBox = wxBoxSizer(wxVERTICAL)
769 jonathan 451
770 jonathan 813 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 jonathan 485
775 jonathan 813 panelBox.Add(sizer, 0, wxGROW, 4)
776 jonathan 606
777 jonathan 935 if isinstance(layer, RasterLayer):
778     type = "Image"
779     else:
780     type = layer.ShapeType()
781    
782     panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
783 jonathan 813 0, wxALIGN_LEFT | wxALL, 4)
784 jonathan 485
785 jonathan 935 if layer.HasClassification():
786 jonathan 485
787 jonathan 935 classBox = wxStaticBoxSizer(
788     wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
789 jonathan 485
790    
791 jonathan 935 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 jonathan 570
796 jonathan 935 classBox.Add(sizer, 0, wxGROW, 4)
797 jonathan 570
798 jonathan 935 classBox.Add(self.fieldTypeText, 0,
799     wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
800 jonathan 570
801 jonathan 935 controlBox = wxBoxSizer(wxHORIZONTAL)
802     controlButtonBox = wxBoxSizer(wxVERTICAL)
803 jonathan 570
804 jonathan 935 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 jonathan 415
813 jonathan 935 controlBox.Add(self.classGrid, 1, wxGROW, 0)
814     controlBox.Add(controlButtonBox, 0, wxGROW, 10)
815 jonathan 661
816 jonathan 935 classBox.Add(controlBox, 1, wxGROW, 10)
817     panelBox.Add(classBox, 1, wxGROW, 0)
818 jonathan 415
819 jonathan 935
820 jonathan 813 buttonBox = wxBoxSizer(wxHORIZONTAL)
821 frank 977 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 jonathan 509
826 jonathan 650 panel.SetAutoLayout(True)
827     panel.SetSizer(panelBox)
828 jonathan 813 panelBox.Fit(panel)
829 jonathan 650 panelBox.SetSizeHints(panel)
830    
831     topBox.Add(panel, 1, wxGROW | wxALL, 4)
832 frank 977 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
833 jonathan 650
834 jonathan 813 self.SetAutoLayout(True)
835     self.SetSizer(topBox)
836     topBox.Fit(self)
837     topBox.SetSizeHints(self)
838     self.Layout()
839    
840 jonathan 650 ###########
841    
842 jonathan 813 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 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
846 jonathan 813 EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
847 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
848 jonathan 372
849 jonathan 813 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 jonathan 509
856 jonathan 549 ######################
857    
858 jonathan 935 text_title.SetFocus()
859 jonathan 549 self.haveApplied = False
860    
861 bh 1142 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 jonathan 638 def EditSymbol(self, row):
874 jonathan 606 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 jonathan 630
881     self.Enable(False)
882 jonathan 606 if propDlg.ShowModal() == wxID_OK:
883     new_prop = propDlg.GetClassGroupProperties()
884     table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
885 jonathan 630 self.Enable(True)
886 jonathan 606 propDlg.Destroy()
887    
888 jonathan 630 def _SetClassification(self, clazz):
889    
890     self.fields.SetClientData(self.__cur_field, clazz)
891     self.classGrid.GetTable().SetClassification(clazz)
892 jonathan 606
893 jonathan 615 def __BuildClassification(self, fieldIndex, copyClass = False):
894 jonathan 415
895 jonathan 615 # numRows = self.classGrid.GetNumberRows()
896     # assert numRows > 0 # there should always be a default row
897 jonathan 496
898 jonathan 615 # clazz = Classification()
899 jonathan 496 if fieldIndex == 0:
900     fieldName = None
901     fieldType = None
902     else:
903     fieldName = self.fields.GetString(fieldIndex)
904     fieldType = self.layer.GetFieldType(fieldName)
905 jonathan 415
906 jonathan 615 clazz = self.classGrid.GetTable().GetClassification()
907    
908     if copyClass:
909     clazz = copy.deepcopy(clazz)
910    
911 jonathan 460 clazz.SetField(fieldName)
912     clazz.SetFieldType(fieldType)
913    
914 jonathan 415
915 jonathan 615 # table = self.classGrid.GetTable()
916     # clazz.SetDefaultGroup(table.GetClassGroup(0))
917 jonathan 415
918 jonathan 615 # for i in range(1, numRows):
919     # clazz.AppendGroup(table.GetClassGroup(i))
920 jonathan 460
921 jonathan 415 return clazz
922    
923 jonathan 570 def __SetGridTable(self, fieldIndex, group = None):
924 jonathan 415
925 jonathan 460 clazz = self.fields.GetClientData(fieldIndex)
926 jonathan 415
927 jonathan 460 if clazz is None:
928     clazz = Classification()
929     clazz.SetDefaultGroup(
930     ClassGroupDefault(
931 jonathan 485 self.layer.GetClassification().
932     GetDefaultGroup().GetProperties()))
933 jonathan 460
934     fieldName = self.fields.GetString(fieldIndex)
935     fieldType = self.layer.GetFieldType(fieldName)
936     clazz.SetFieldType(fieldType)
937    
938 jonathan 570 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
939 jonathan 460
940 jonathan 485 def __SetFieldTypeText(self, fieldIndex):
941     fieldName = self.fields.GetString(fieldIndex)
942     fieldType = self.layer.GetFieldType(fieldName)
943    
944 jonathan 606 assert Classifier.type2string.has_key(fieldType)
945 jonathan 485
946 jonathan 496 text = Classifier.type2string[fieldType]
947    
948 jonathan 650 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
949 jonathan 485
950 jonathan 570 def __SelectField(self, newIndex, oldIndex = -1, group = None):
951 jonathan 611 """This method assumes that the current selection for the
952     combo has already been set by a call to SetSelection().
953     """
954 jonathan 460
955 jonathan 606 assert oldIndex >= -1
956 jonathan 415
957 jonathan 498 if oldIndex != -1:
958     clazz = self.__BuildClassification(oldIndex)
959     self.fields.SetClientData(oldIndex, clazz)
960 jonathan 485
961 jonathan 570 self.__SetGridTable(newIndex, group)
962 jonathan 498
963 jonathan 650 self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
964 jonathan 498
965     self.__SetFieldTypeText(newIndex)
966 jonathan 485
967 jonathan 650 def __SetTitle(self, title):
968     if title != "":
969     title = ": " + title
970    
971     self.SetTitle(_("Layer Properties") + title)
972 jonathan 496
973 jonathan 638 def _OnEditSymbol(self, event):
974 jonathan 606 sel = self.classGrid.GetCurrentSelection()
975    
976     if len(sel) == 1:
977 jonathan 638 self.EditSymbol(sel[0])
978 jonathan 606
979 jonathan 496 def _OnFieldSelect(self, event):
980 jonathan 498 index = self.fields.GetSelection()
981     self.__SelectField(index, self.__cur_field)
982     self.__cur_field = index
983 jonathan 485
984 jonathan 638 def _OnTry(self, event):
985 jonathan 415 """Put the data from the table into a new Classification and hand
986     it to the layer.
987     """
988    
989 jonathan 935 if self.layer.HasClassification():
990     clazz = self.fields.GetClientData(self.__cur_field)
991 jonathan 415
992 jonathan 935 #
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 jonathan 415
1000 jonathan 935 self.layer.SetClassification(clazz)
1001 jonathan 415
1002 jonathan 549 self.haveApplied = True
1003    
1004 jonathan 485 def _OnOK(self, event):
1005 jonathan 638 self._OnTry(event)
1006 jonathan 615 self.Close()
1007 jonathan 415
1008 jonathan 813 def OnClose(self, event):
1009 bh 1207 self.unsubscribe_messages()
1010 frank 1058 NonModalNonParentDialog.OnClose(self, event)
1011 jonathan 813
1012 jonathan 615 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 jonathan 638 def _OnRevert(self, event):
1021 jonathan 485 """The layer's current classification stays the same."""
1022 jonathan 549 if self.haveApplied:
1023     self.layer.SetClassification(self.originalClass)
1024    
1025 jonathan 638 #self.Close()
1026 jonathan 415
1027 jonathan 460 def _OnAdd(self, event):
1028 jonathan 451 self.classGrid.AppendRows()
1029 jonathan 415
1030 jonathan 460 def _OnRemove(self, event):
1031 jonathan 451 self.classGrid.DeleteSelectedRows()
1032    
1033 jonathan 606 def _OnGenClass(self, event):
1034 jonathan 415
1035 jonathan 630 self.genDlg = ClassGenDialog(self, self.layer,
1036     self.fields.GetString(self.__cur_field))
1037 jonathan 606
1038 jonathan 630 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1039 jonathan 606
1040 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, False)
1041 jonathan 630
1042     self.genDlg.Show()
1043    
1044     def _OnGenDialogClose(self, event):
1045     self.genDlg.Destroy()
1046 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, True)
1047 jonathan 630
1048 jonathan 460 def _OnMoveUp(self, event):
1049     sel = self.classGrid.GetCurrentSelection()
1050 jonathan 415
1051 jonathan 460 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 jonathan 570 self.classGrid.MakeCellVisible(i - 1, 0)
1062 jonathan 460
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 jonathan 570 self.classGrid.MakeCellVisible(i + 1, 0)
1077 jonathan 460
1078 jonathan 650 def _OnTitleChanged(self, event):
1079     obj = event.GetEventObject()
1080 jonathan 460
1081 jonathan 650 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 jonathan 813 list = (wxID_OK,
1090     wxID_CANCEL)
1091 jonathan 650
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 jonathan 415 ID_SELPROP_SPINCTRL = 4002
1110 jonathan 430 ID_SELPROP_PREVIEW = 4003
1111     ID_SELPROP_STROKECLR = 4004
1112     ID_SELPROP_FILLCLR = 4005
1113 jonathan 485 ID_SELPROP_STROKECLRTRANS = 4006
1114     ID_SELPROP_FILLCLRTRANS = 4007
1115 jonathan 415
1116     class SelectPropertiesDialog(wxDialog):
1117    
1118     def __init__(self, parent, prop, shapeType):
1119     wxDialog.__init__(self, parent, -1, _("Select Properties"),
1120 jonathan 507 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1121 jonathan 415
1122 jonathan 441 self.prop = ClassGroupProperties(prop)
1123 jonathan 415
1124 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
1125 jonathan 415
1126 jonathan 430 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 jonathan 630
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 jonathan 615 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1140 jonathan 430
1141     itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1142    
1143     # control box
1144     ctrlBox = wxBoxSizer(wxVERTICAL)
1145 jonathan 485
1146     lineColorBox = wxBoxSizer(wxHORIZONTAL)
1147 jonathan 813 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1148     button.SetFocus()
1149     lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1150 jonathan 460 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1151 jonathan 430
1152 jonathan 485 lineColorBox.Add(
1153 jonathan 500 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1154 jonathan 485 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 jonathan 430 if shapeType != SHAPETYPE_ARC:
1162 jonathan 485 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1163     fillColorBox.Add(
1164 jonathan 500 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1165 jonathan 485 1, wxALL | wxGROW, 4)
1166 jonathan 460 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1167 jonathan 485 fillColorBox.Add(
1168 jonathan 500 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1169 jonathan 485 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 jonathan 430
1175 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
1176 jonathan 460 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1177 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1178 jonathan 415 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1179     min=1, max=10,
1180 jonathan 460 value=str(prop.GetLineWidth()),
1181     initial=prop.GetLineWidth())
1182 jonathan 415
1183 jonathan 460 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1184 jonathan 415
1185     spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1186    
1187 jonathan 430 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 jonathan 415
1191     #
1192     # Control buttons:
1193     #
1194     buttonBox = wxBoxSizer(wxHORIZONTAL)
1195 jonathan 813 button_ok = wxButton(self, wxID_OK, _("OK"))
1196     button_ok.SetDefault()
1197 frank 977 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1198 jonathan 813 buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1199 frank 977 0, wxRIGHT|wxEXPAND, 10)
1200     topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1201 jonathan 415
1202 jonathan 813 #EVT_BUTTON(self, wxID_OK, self._OnOK)
1203     #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1204 jonathan 415
1205 jonathan 509 self.SetAutoLayout(True)
1206 jonathan 415 self.SetSizer(topBox)
1207     topBox.Fit(self)
1208     topBox.SetSizeHints(self)
1209    
1210 jonathan 813 def OnOK(self, event):
1211 jonathan 372 self.EndModal(wxID_OK)
1212    
1213 jonathan 813 def OnCancel(self, event):
1214 jonathan 372 self.EndModal(wxID_CANCEL)
1215    
1216 jonathan 460 def _OnSpin(self, event):
1217     self.prop.SetLineWidth(self.spinCtrl.GetValue())
1218 jonathan 549 self.previewWin.Refresh()
1219 jonathan 392
1220 jonathan 430 def __GetColor(self, cur):
1221     dialog = wxColourDialog(self)
1222 jonathan 610 if cur is not Color.Transparent:
1223 jonathan 606 dialog.GetColourData().SetColour(Color2wxColour(cur))
1224    
1225 jonathan 430 ret = None
1226     if dialog.ShowModal() == wxID_OK:
1227     ret = wxColour2Color(dialog.GetColourData().GetColour())
1228    
1229     dialog.Destroy()
1230    
1231     return ret
1232    
1233 jonathan 460 def _OnChangeLineColor(self, event):
1234     clr = self.__GetColor(self.prop.GetLineColor())
1235 jonathan 430 if clr is not None:
1236 jonathan 460 self.prop.SetLineColor(clr)
1237 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1238 jonathan 430
1239 jonathan 485 def _OnChangeLineColorTrans(self, event):
1240 jonathan 610 self.prop.SetLineColor(Color.Transparent)
1241 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1242 jonathan 485
1243 jonathan 460 def _OnChangeFillColor(self, event):
1244 jonathan 430 clr = self.__GetColor(self.prop.GetFill())
1245     if clr is not None:
1246     self.prop.SetFill(clr)
1247 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1248 jonathan 430
1249 jonathan 485 def _OnChangeFillColorTrans(self, event):
1250 jonathan 610 self.prop.SetFill(Color.Transparent)
1251 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1252 jonathan 485
1253 jonathan 441 def GetClassGroupProperties(self):
1254 jonathan 415 return self.prop
1255 jonathan 392
1256    
1257 jonathan 549 class ClassDataPreviewWindow(wxWindow):
1258 jonathan 415
1259 jonathan 441 def __init__(self, rect, prop, shapeType,
1260 jonathan 430 parent = None, id = -1, size = wxDefaultSize):
1261     if parent is not None:
1262 jonathan 549 wxWindow.__init__(self, parent, id, (0, 0), size)
1263 jonathan 460 EVT_PAINT(self, self._OnPaint)
1264 jonathan 415
1265 jonathan 430 self.rect = rect
1266 jonathan 549
1267 jonathan 441 self.prop = prop
1268 jonathan 430 self.shapeType = shapeType
1269 jonathan 549 self.previewer = ClassDataPreviewer()
1270 jonathan 430
1271 jonathan 630 def GetProperties():
1272     return self.prop
1273    
1274 jonathan 460 def _OnPaint(self, event):
1275 jonathan 430 dc = wxPaintDC(self)
1276    
1277     # XXX: this doesn't seem to be having an effect:
1278     dc.DestroyClippingRegion()
1279    
1280 jonathan 549 if self.rect is None:
1281     w, h = self.GetSize()
1282     rect = wxRect(0, 0, w, h)
1283     else:
1284     rect = self.rect
1285 jonathan 430
1286 jonathan 549 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1287 jonathan 430
1288 jonathan 549 class ClassDataPreviewer:
1289 jonathan 430
1290 jonathan 549 def Draw(self, dc, rect, prop, shapeType):
1291    
1292 jonathan 606 assert dc is not None
1293     assert isinstance(prop, ClassGroupProperties)
1294 jonathan 549
1295 jonathan 430 if rect is None:
1296 jonathan 549 x = 0
1297     y = 0
1298     w, h = dc.GetSize()
1299 jonathan 430 else:
1300     x = rect.GetX()
1301     y = rect.GetY()
1302     w = rect.GetWidth()
1303     h = rect.GetHeight()
1304    
1305 jonathan 460 stroke = prop.GetLineColor()
1306 jonathan 610 if stroke is Color.Transparent:
1307 jonathan 392 pen = wxTRANSPARENT_PEN
1308     else:
1309 jonathan 430 pen = wxPen(Color2wxColour(stroke),
1310 jonathan 460 prop.GetLineWidth(),
1311 jonathan 392 wxSOLID)
1312    
1313 jonathan 441 stroke = prop.GetFill()
1314 jonathan 610 if stroke is Color.Transparent:
1315 jonathan 392 brush = wxTRANSPARENT_BRUSH
1316     else:
1317 jonathan 430 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1318 jonathan 392
1319     dc.SetPen(pen)
1320     dc.SetBrush(brush)
1321    
1322 jonathan 415 if shapeType == SHAPETYPE_ARC:
1323 jonathan 430 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 jonathan 392
1328 jonathan 576 elif shapeType == SHAPETYPE_POINT:
1329 jonathan 415
1330 jonathan 430 dc.DrawCircle(x + w/2, y + h/2,
1331 jonathan 460 (min(w, h) - prop.GetLineWidth())/2)
1332 jonathan 392
1333 jonathan 576 elif shapeType == SHAPETYPE_POLYGON:
1334     dc.DrawRectangle(x, y, w, h)
1335    
1336 jonathan 415 class ClassRenderer(wxPyGridCellRenderer):
1337    
1338     def __init__(self, shapeType):
1339     wxPyGridCellRenderer.__init__(self)
1340 jonathan 549 self.shapeType = shapeType
1341     self.previewer = ClassDataPreviewer()
1342 jonathan 415
1343     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1344 jonathan 485 data = grid.GetTable().GetClassGroup(row)
1345 jonathan 415
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 jonathan 441 if not isinstance(data, ClassGroupMap):
1354 jonathan 549 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1355 jonathan 415
1356     if isSelected:
1357 jonathan 615 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1358 jonathan 415 dc.SetBrush(wxTRANSPARENT_BRUSH)
1359 jonathan 615
1360 jonathan 415 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1361     rect.GetWidth(), rect.GetHeight())
1362    
1363 jonathan 392 dc.DestroyClippingRegion()
1364    
1365 jonathan 630
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