/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Annotation of /trunk/thuban/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1433 - (hide annotations)
Wed Jul 16 13:24:25 2003 UTC (21 years, 7 months ago) by jonathan
File MIME type: text/x-python
File size: 46292 byte(s)
(ClassGrid.CreateTable): Add fieldType parameter.
(ClassTable.Reset): Add fieldType parameter and use it, rather
        than asking the classification.
(Classifier.__init__): Remember the original class's field
        and ask the layer for the field type, rather than the classification.
(Classifier.__SetGridTable): Retrieve the field and field type
        for the table because they are not in the classification.
(Classifier._OnTry, Classifier._OnRevert): Set the classification
        field on the layer in addition to the classification itself.

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