/[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 935 - (hide annotations)
Tue May 20 15:24:17 2003 UTC (21 years, 9 months ago) by jonathan
File MIME type: text/x-python
File size: 45044 byte(s)
(Classifier.__init__): Rearrange how
        the dialog is constructed so that we can support layers that
        do not have classifications.
(Classifier._OnTry): Only build a classification if the layer supports one.

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 jonathan 878 from Thuban.Model.range import Range
24     from Thuban.Model.classification import \
25     Classification, ClassGroupDefault, \
26     ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
27     ClassGroupProperties
28 jonathan 392
29 jonathan 415 from Thuban.Model.color import Color
30    
31 jonathan 935 from Thuban.Model.layer import Layer, RasterLayer, \
32 jonathan 878 SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
33 jonathan 392
34 jonathan 606 from Thuban.UI.classgen import ClassGenDialog, ClassGenerator
35    
36 jonathan 485 from dialogs import NonModalDialog
37    
38 jonathan 372 ID_CLASS_TABLE = 40011
39    
40    
41 jonathan 460 # table columns
42 jonathan 638 COL_VISIBLE = 0
43     COL_SYMBOL = 1
44     COL_VALUE = 2
45     COL_LABEL = 3
46     NUM_COLS = 4
47 jonathan 415
48 jonathan 460 # indices into the client data lists in Classifier.fields
49 jonathan 451 FIELD_CLASS = 0
50     FIELD_TYPE = 1
51     FIELD_NAME = 2
52    
53 jonathan 415 #
54     # this is a silly work around to ensure that the table that is
55     # passed into SetTable is the same that is returned by GetTable
56     #
57     import weakref
58     class ClassGrid(wxGrid):
59    
60 jonathan 570
61 jonathan 606 def __init__(self, parent, classifier):
62 jonathan 460 """Constructor.
63    
64     parent -- the parent window
65    
66     clazz -- the working classification that this grid should
67     use for display.
68     """
69    
70 jonathan 813 wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
71 jonathan 415
72 jonathan 606 self.classifier = classifier
73    
74 jonathan 570 self.currentSelection = []
75    
76 jonathan 460 EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
77 jonathan 451 EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
78     EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
79 jonathan 606 EVT_GRID_COL_SIZE(self, self._OnCellResize)
80     EVT_GRID_ROW_SIZE(self, self._OnCellResize)
81 jonathan 451
82 jonathan 638 #def GetCellAttr(self, row, col):
83     #print "GetCellAttr ", row, col
84     #wxGrid.GetCellAttr(self, row, col)
85    
86 jonathan 570 def CreateTable(self, clazz, shapeType, group = None):
87    
88 jonathan 606 assert isinstance(clazz, Classification)
89 jonathan 460
90     table = self.GetTable()
91     if table is None:
92 jonathan 650 w = self.GetDefaultColSize() * NUM_COLS \
93     + self.GetDefaultRowLabelSize()
94     h = self.GetDefaultRowSize() * 4 \
95     + self.GetDefaultColLabelSize()
96    
97 jonathan 500 self.SetDimensions(-1, -1, w, h)
98     self.SetSizeHints(w, h, -1, -1)
99 jonathan 570 table = ClassTable(self)
100     self.SetTable(table, True)
101 jonathan 460
102 jonathan 570
103 bh 476 self.SetSelectionMode(wxGrid.wxGridSelectRows)
104 jonathan 460 self.ClearSelection()
105    
106 jonathan 606 table.Reset(clazz, shapeType, group)
107 jonathan 570
108 jonathan 451 def GetCurrentSelection(self):
109 jonathan 460 """Return the currently highlighted rows as an increasing list
110     of row numbers."""
111 jonathan 451 sel = copy.copy(self.currentSelection)
112     sel.sort()
113     return sel
114    
115 jonathan 570 def GetSelectedRows(self):
116     return self.GetCurrentSelection()
117    
118 jonathan 638 #def SetCellRenderer(self, row, col, renderer):
119     #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
120 jonathan 415
121 jonathan 460 #
122     # [Set|Get]Table is taken from http://wiki.wxpython.org
123     # they are needed as a work around to ensure that the table
124     # that is passed to SetTable is the one that is returned
125     # by GetTable.
126     #
127 jonathan 415 def SetTable(self, object, *attributes):
128     self.tableRef = weakref.ref(object)
129     return wxGrid.SetTable(self, object, *attributes)
130    
131     def GetTable(self):
132 jonathan 460 try:
133     return self.tableRef()
134     except:
135     return None
136 jonathan 415
137 jonathan 451 def DeleteSelectedRows(self):
138 jonathan 460 """Deletes all highlighted rows.
139    
140     If only one row is highlighted then after it is deleted the
141     row that was below the deleted row is highlighted."""
142    
143 jonathan 451 sel = self.GetCurrentSelection()
144 jonathan 415
145 jonathan 460 # nothing to do
146     if len(sel) == 0: return
147    
148     # if only one thing is selected check if it is the default
149     # data row, because we can't remove that
150 jonathan 451 if len(sel) == 1:
151 jonathan 485 #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
152     group = self.GetTable().GetClassGroup(sel[0])
153 jonathan 451 if isinstance(group, ClassGroupDefault):
154     wxMessageDialog(self,
155     "The Default group cannot be removed.",
156     style = wxOK | wxICON_EXCLAMATION).ShowModal()
157     return
158    
159 jonathan 460
160 jonathan 451 self.ClearSelection()
161    
162 jonathan 460 # we need to remove things from the bottom up so we don't
163     # change the indexes of rows that will be deleted next
164 jonathan 451 sel.reverse()
165 jonathan 460
166     #
167     # actually remove the rows
168     #
169 jonathan 451 table = self.GetTable()
170     for row in sel:
171     table.DeleteRows(row)
172    
173 jonathan 460 #
174     # if there was only one row selected highlight the row
175     # that was directly below it, or move up one if the
176     # deleted row was the last row.
177     #
178 jonathan 451 if len(sel) == 1:
179     r = sel[0]
180     if r > self.GetNumberRows() - 1:
181     r = self.GetNumberRows() - 1
182     self.SelectRow(r)
183    
184 jonathan 570
185     def SelectGroup(self, group, makeVisible = True):
186     if group is None: return
187    
188 jonathan 606 assert isinstance(group, ClassGroup)
189 jonathan 570
190     table = self.GetTable()
191    
192 jonathan 606 assert table is not None
193 jonathan 570
194     for i in range(table.GetNumberRows()):
195     g = table.GetClassGroup(i)
196     if g is group:
197     self.SelectRow(i)
198     if makeVisible:
199     self.MakeCellVisible(i, 0)
200     break
201    
202 jonathan 451 #
203     # XXX: This isn't working, and there is no way to deselect rows wxPython!
204     #
205     # def DeselectRow(self, row):
206     # self.ProcessEvent(
207     # wxGridRangeSelectEvent(-1,
208     # wxEVT_GRID_RANGE_SELECT,
209     # self,
210     # (row, row), (row, row),
211     # sel = False))
212    
213 jonathan 460 def _OnCellDClick(self, event):
214 jonathan 638 """Handle a double click on a cell."""
215 jonathan 460
216 jonathan 451 r = event.GetRow()
217     c = event.GetCol()
218 jonathan 638
219 jonathan 460 if c == COL_SYMBOL:
220 jonathan 638 self.classifier.EditSymbol(r)
221     else:
222     event.Skip()
223 jonathan 460
224 jonathan 451 #
225     # _OnSelectedRange() and _OnSelectedCell() were borrowed
226 jonathan 460 # from http://wiki.wxpython.org to keep track of which
227     # cells are currently highlighted
228 jonathan 451 #
229     def _OnSelectedRange(self, event):
230     """Internal update to the selection tracking list"""
231     if event.Selecting():
232     for index in range( event.GetTopRow(), event.GetBottomRow()+1):
233     if index not in self.currentSelection:
234     self.currentSelection.append( index )
235     else:
236     for index in range( event.GetTopRow(), event.GetBottomRow()+1):
237     while index in self.currentSelection:
238     self.currentSelection.remove( index )
239     #self.ConfigureForSelection()
240    
241     event.Skip()
242    
243     def _OnSelectedCell( self, event ):
244     """Internal update to the selection tracking list"""
245     self.currentSelection = [ event.GetRow() ]
246     #self.ConfigureForSelection()
247     event.Skip()
248    
249 jonathan 606 def _OnCellResize(self, event):
250     self.FitInside()
251 jonathan 878 event.Skip()
252 jonathan 606
253 jonathan 376 class ClassTable(wxPyGridTableBase):
254 jonathan 460 """Represents the underlying data structure for the grid."""
255 jonathan 376
256 jonathan 638 __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
257 jonathan 415
258    
259 jonathan 570 def __init__(self, view = None):
260     #def __init__(self, clazz, shapeType, 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 570 def Reset(self, clazz, 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 485 self.fieldType = clazz.GetFieldType()
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 jonathan 485 class Classifier(NonModalDialog):
674 bh 535
675 jonathan 630 type2string = {None: _("None"),
676     FIELDTYPE_STRING: _("Text"),
677     FIELDTYPE_INT: _("Integer"),
678     FIELDTYPE_DOUBLE: _("Decimal")}
679    
680 jonathan 570 def __init__(self, parent, name, layer, group = None):
681 jonathan 650 NonModalDialog.__init__(self, parent, name, "")
682 jonathan 372
683 jonathan 650 self.__SetTitle(layer.Title())
684 jonathan 509
685 jonathan 415 self.layer = layer
686    
687 jonathan 485
688 jonathan 630 self.genDlg = None
689    
690 jonathan 813 ############################
691     # Create the controls
692     #
693 jonathan 372
694 jonathan 813 panel = wxPanel(self, -1)
695 jonathan 661
696 jonathan 813 text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
697 jonathan 935 self.fieldTypeText = wxStaticText(panel, -1, "")
698 jonathan 372
699 jonathan 935 if layer.HasClassification():
700     self.originalClass = self.layer.GetClassification()
701     field = self.originalClass.GetField()
702     fieldType = self.originalClass.GetFieldType()
703 jonathan 460
704 jonathan 935 #
705     # make field choice box
706     #
707     self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
708 jonathan 460
709 jonathan 935 self.num_cols = layer.table.NumColumns()
710     # just assume the first field in case one hasn't been
711     # specified in the file.
712     self.__cur_field = 0
713 jonathan 650
714 jonathan 935 self.fields.Append("<None>")
715 jonathan 451
716 jonathan 935 if self.originalClass.GetFieldType() is None:
717     self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
718 jonathan 451 else:
719 jonathan 935 self.fields.SetClientData(0, None)
720 jonathan 372
721 jonathan 935 for i in range(self.num_cols):
722     name = layer.table.Column(i).name
723     self.fields.Append(name)
724 jonathan 372
725 jonathan 935 if name == field:
726     self.__cur_field = i + 1
727     self.fields.SetClientData(i + 1,
728     copy.deepcopy(self.originalClass))
729     else:
730     self.fields.SetClientData(i + 1, None)
731 jonathan 485
732 jonathan 935 button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
733     _("Generate Class"))
734     button_add = wxButton(panel, ID_PROPERTY_ADD,
735     _("Add"))
736     button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
737     _("Move Up"))
738     button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
739     _("Move Down"))
740     button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
741     _("Edit Symbol"))
742     button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
743     _("Remove"))
744 jonathan 650
745 jonathan 935 self.classGrid = ClassGrid(panel, self)
746 jonathan 650
747 jonathan 935 # calling __SelectField after creating the classGrid fills in the
748     # grid with the correct information
749     self.fields.SetSelection(self.__cur_field)
750     self.__SelectField(self.__cur_field, group = group)
751 jonathan 650
752 jonathan 813 button_try = wxButton(panel, ID_PROPERTY_TRY, _("Try"))
753     button_revert = wxButton(panel, ID_PROPERTY_REVERT, _("Revert"))
754     button_ok = wxButton(panel, wxID_OK, _("OK"))
755     button_ok.SetDefault()
756     button_close = wxButton(panel, wxID_CANCEL, _("Close"))
757 jonathan 485
758 jonathan 813 ############################
759     # Layout the controls
760 jonathan 485 #
761 jonathan 509
762 jonathan 813 topBox = wxBoxSizer(wxVERTICAL)
763     panelBox = wxBoxSizer(wxVERTICAL)
764 jonathan 451
765 jonathan 813 sizer = wxBoxSizer(wxHORIZONTAL)
766     sizer.Add(wxStaticText(panel, -1, _("Title: ")),
767     0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
768     sizer.Add(text_title, 1, wxGROW, 0)
769 jonathan 485
770 jonathan 813 panelBox.Add(sizer, 0, wxGROW, 4)
771 jonathan 606
772 jonathan 935 if isinstance(layer, RasterLayer):
773     type = "Image"
774     else:
775     type = layer.ShapeType()
776    
777     panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
778 jonathan 813 0, wxALIGN_LEFT | wxALL, 4)
779 jonathan 485
780 jonathan 935 if layer.HasClassification():
781 jonathan 485
782 jonathan 935 classBox = wxStaticBoxSizer(
783     wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
784 jonathan 485
785    
786 jonathan 935 sizer = wxBoxSizer(wxHORIZONTAL)
787     sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
788     0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
789     sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
790 jonathan 570
791 jonathan 935 classBox.Add(sizer, 0, wxGROW, 4)
792 jonathan 570
793 jonathan 935 classBox.Add(self.fieldTypeText, 0,
794     wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
795 jonathan 570
796 jonathan 935 controlBox = wxBoxSizer(wxHORIZONTAL)
797     controlButtonBox = wxBoxSizer(wxVERTICAL)
798 jonathan 570
799 jonathan 935 controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
800     controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
801     controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
802     controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
803     controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
804     controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
805     controlButtonBox.Add(button_remove, 0,
806     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
807 jonathan 415
808 jonathan 935 controlBox.Add(self.classGrid, 1, wxGROW, 0)
809     controlBox.Add(controlButtonBox, 0, wxGROW, 10)
810 jonathan 661
811 jonathan 935 classBox.Add(controlBox, 1, wxGROW, 10)
812     panelBox.Add(classBox, 1, wxGROW, 0)
813 jonathan 415
814 jonathan 935
815 jonathan 813 buttonBox = wxBoxSizer(wxHORIZONTAL)
816     buttonBox.Add(button_try, 0, wxALL, 4)
817     buttonBox.Add(60, 20, 0, wxALL, 4)
818     buttonBox.Add(button_revert, 0, wxALL, 4)
819     buttonBox.Add(60, 20, 0, wxALL, 4)
820     buttonBox.Add(button_ok, 0, wxALL, 4)
821     buttonBox.Add(60, 20, 0, wxALL, 4)
822     buttonBox.Add(button_close, 0, wxALL, 4)
823     panelBox.Add(buttonBox, 0,
824     wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
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    
833 jonathan 813 self.SetAutoLayout(True)
834     self.SetSizer(topBox)
835     topBox.Fit(self)
836     topBox.SetSizeHints(self)
837     self.Layout()
838    
839 jonathan 650 ###########
840    
841 jonathan 813 EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
842     EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
843     EVT_BUTTON(self, wxID_OK, self._OnOK)
844 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
845 jonathan 813 EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
846 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
847 jonathan 372
848 jonathan 813 EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
849     EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
850     EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
851     EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
852     EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
853     EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
854 jonathan 509
855 jonathan 549 ######################
856    
857 jonathan 935 text_title.SetFocus()
858 jonathan 549 self.haveApplied = False
859    
860 jonathan 638 def EditSymbol(self, row):
861 jonathan 606 table = self.classGrid.GetTable()
862     prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
863    
864     # get a new ClassGroupProperties object and copy the
865     # values over to our current object
866     propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
867 jonathan 630
868     self.Enable(False)
869 jonathan 606 if propDlg.ShowModal() == wxID_OK:
870     new_prop = propDlg.GetClassGroupProperties()
871     table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
872 jonathan 630 self.Enable(True)
873 jonathan 606 propDlg.Destroy()
874    
875 jonathan 630 def _SetClassification(self, clazz):
876    
877     self.fields.SetClientData(self.__cur_field, clazz)
878     self.classGrid.GetTable().SetClassification(clazz)
879 jonathan 606
880 jonathan 615 def __BuildClassification(self, fieldIndex, copyClass = False):
881 jonathan 415
882 jonathan 615 # numRows = self.classGrid.GetNumberRows()
883     # assert numRows > 0 # there should always be a default row
884 jonathan 496
885 jonathan 615 # clazz = Classification()
886 jonathan 496 if fieldIndex == 0:
887     fieldName = None
888     fieldType = None
889     else:
890     fieldName = self.fields.GetString(fieldIndex)
891     fieldType = self.layer.GetFieldType(fieldName)
892 jonathan 415
893 jonathan 615 clazz = self.classGrid.GetTable().GetClassification()
894    
895     if copyClass:
896     clazz = copy.deepcopy(clazz)
897    
898 jonathan 460 clazz.SetField(fieldName)
899     clazz.SetFieldType(fieldType)
900    
901 jonathan 415
902 jonathan 615 # table = self.classGrid.GetTable()
903     # clazz.SetDefaultGroup(table.GetClassGroup(0))
904 jonathan 415
905 jonathan 615 # for i in range(1, numRows):
906     # clazz.AppendGroup(table.GetClassGroup(i))
907 jonathan 460
908 jonathan 415 return clazz
909    
910 jonathan 570 def __SetGridTable(self, fieldIndex, group = None):
911 jonathan 415
912 jonathan 460 clazz = self.fields.GetClientData(fieldIndex)
913 jonathan 415
914 jonathan 460 if clazz is None:
915     clazz = Classification()
916     clazz.SetDefaultGroup(
917     ClassGroupDefault(
918 jonathan 485 self.layer.GetClassification().
919     GetDefaultGroup().GetProperties()))
920 jonathan 460
921     fieldName = self.fields.GetString(fieldIndex)
922     fieldType = self.layer.GetFieldType(fieldName)
923     clazz.SetFieldType(fieldType)
924    
925 jonathan 570 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
926 jonathan 460
927 jonathan 485 def __SetFieldTypeText(self, fieldIndex):
928     fieldName = self.fields.GetString(fieldIndex)
929     fieldType = self.layer.GetFieldType(fieldName)
930    
931 jonathan 606 assert Classifier.type2string.has_key(fieldType)
932 jonathan 485
933 jonathan 496 text = Classifier.type2string[fieldType]
934    
935 jonathan 650 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
936 jonathan 485
937 jonathan 570 def __SelectField(self, newIndex, oldIndex = -1, group = None):
938 jonathan 611 """This method assumes that the current selection for the
939     combo has already been set by a call to SetSelection().
940     """
941 jonathan 460
942 jonathan 606 assert oldIndex >= -1
943 jonathan 415
944 jonathan 498 if oldIndex != -1:
945     clazz = self.__BuildClassification(oldIndex)
946     self.fields.SetClientData(oldIndex, clazz)
947 jonathan 485
948 jonathan 570 self.__SetGridTable(newIndex, group)
949 jonathan 498
950 jonathan 650 self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
951 jonathan 498
952     self.__SetFieldTypeText(newIndex)
953 jonathan 485
954 jonathan 650 def __SetTitle(self, title):
955     if title != "":
956     title = ": " + title
957    
958     self.SetTitle(_("Layer Properties") + title)
959 jonathan 496
960 jonathan 638 def _OnEditSymbol(self, event):
961 jonathan 606 sel = self.classGrid.GetCurrentSelection()
962    
963     if len(sel) == 1:
964 jonathan 638 self.EditSymbol(sel[0])
965 jonathan 606
966 jonathan 496 def _OnFieldSelect(self, event):
967 jonathan 498 index = self.fields.GetSelection()
968     self.__SelectField(index, self.__cur_field)
969     self.__cur_field = index
970 jonathan 485
971 jonathan 638 def _OnTry(self, event):
972 jonathan 415 """Put the data from the table into a new Classification and hand
973     it to the layer.
974     """
975    
976 jonathan 935 if self.layer.HasClassification():
977     clazz = self.fields.GetClientData(self.__cur_field)
978 jonathan 415
979 jonathan 935 #
980     # only build the classification if there wasn't one to
981     # to begin with or it has been modified
982     #
983     self.classGrid.SaveEditControlValue()
984     if clazz is None or self.classGrid.GetTable().IsModified():
985     clazz = self.__BuildClassification(self.__cur_field, True)
986 jonathan 415
987 jonathan 935 self.layer.SetClassification(clazz)
988 jonathan 415
989 jonathan 549 self.haveApplied = True
990    
991 jonathan 485 def _OnOK(self, event):
992 jonathan 638 self._OnTry(event)
993 jonathan 615 self.Close()
994 jonathan 415
995 jonathan 813 def OnClose(self, event):
996     NonModalDialog.OnClose(self, event)
997    
998 jonathan 615 def _OnCloseBtn(self, event):
999     """Close is similar to Cancel except that any changes that were
1000     made and applied remain applied, but the currently displayed
1001     classification is discarded.
1002     """
1003    
1004     self.Close()
1005    
1006 jonathan 638 def _OnRevert(self, event):
1007 jonathan 485 """The layer's current classification stays the same."""
1008 jonathan 549 if self.haveApplied:
1009     self.layer.SetClassification(self.originalClass)
1010    
1011 jonathan 638 #self.Close()
1012 jonathan 415
1013 jonathan 460 def _OnAdd(self, event):
1014 jonathan 451 self.classGrid.AppendRows()
1015 jonathan 415
1016 jonathan 460 def _OnRemove(self, event):
1017 jonathan 451 self.classGrid.DeleteSelectedRows()
1018    
1019 jonathan 606 def _OnGenClass(self, event):
1020 jonathan 415
1021 jonathan 630 self.genDlg = ClassGenDialog(self, self.layer,
1022     self.fields.GetString(self.__cur_field))
1023 jonathan 606
1024 jonathan 630 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1025 jonathan 606
1026 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, False)
1027 jonathan 630
1028     self.genDlg.Show()
1029    
1030     def _OnGenDialogClose(self, event):
1031     self.genDlg.Destroy()
1032 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, True)
1033 jonathan 630
1034 jonathan 460 def _OnMoveUp(self, event):
1035     sel = self.classGrid.GetCurrentSelection()
1036 jonathan 415
1037 jonathan 460 if len(sel) == 1:
1038     i = sel[0]
1039     if i > 1:
1040     table = self.classGrid.GetTable()
1041     x = table.GetClassGroup(i - 1)
1042     y = table.GetClassGroup(i)
1043     table.SetClassGroup(i - 1, y)
1044     table.SetClassGroup(i, x)
1045     self.classGrid.ClearSelection()
1046     self.classGrid.SelectRow(i - 1)
1047 jonathan 570 self.classGrid.MakeCellVisible(i - 1, 0)
1048 jonathan 460
1049     def _OnMoveDown(self, event):
1050     sel = self.classGrid.GetCurrentSelection()
1051    
1052     if len(sel) == 1:
1053     i = sel[0]
1054     table = self.classGrid.GetTable()
1055     if 0 < i < table.GetNumberRows() - 1:
1056     x = table.GetClassGroup(i)
1057     y = table.GetClassGroup(i + 1)
1058     table.SetClassGroup(i, y)
1059     table.SetClassGroup(i + 1, x)
1060     self.classGrid.ClearSelection()
1061     self.classGrid.SelectRow(i + 1)
1062 jonathan 570 self.classGrid.MakeCellVisible(i + 1, 0)
1063 jonathan 460
1064 jonathan 650 def _OnTitleChanged(self, event):
1065     obj = event.GetEventObject()
1066 jonathan 460
1067 jonathan 650 self.layer.SetTitle(obj.GetValue())
1068     self.__SetTitle(self.layer.Title())
1069    
1070     self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1071    
1072     def __EnableButtons(self, case, enable):
1073    
1074     if case == EB_LAYER_TITLE:
1075 jonathan 813 list = (wxID_OK,
1076     wxID_CANCEL)
1077 jonathan 650
1078     elif case == EB_SELECT_FIELD:
1079     list = (ID_PROPERTY_GENCLASS,
1080     ID_PROPERTY_ADD,
1081     ID_PROPERTY_MOVEUP,
1082     ID_PROPERTY_MOVEDOWN,
1083     ID_PROPERTY_EDITSYM,
1084     ID_PROPERTY_REMOVE)
1085    
1086     elif case == EB_GEN_CLASS:
1087     list = (ID_PROPERTY_SELECT,
1088     ID_PROPERTY_FIELDTEXT,
1089     ID_PROPERTY_GENCLASS,
1090     ID_PROPERTY_EDITSYM)
1091    
1092     for id in list:
1093     self.FindWindowById(id).Enable(enable)
1094    
1095 jonathan 415 ID_SELPROP_SPINCTRL = 4002
1096 jonathan 430 ID_SELPROP_PREVIEW = 4003
1097     ID_SELPROP_STROKECLR = 4004
1098     ID_SELPROP_FILLCLR = 4005
1099 jonathan 485 ID_SELPROP_STROKECLRTRANS = 4006
1100     ID_SELPROP_FILLCLRTRANS = 4007
1101 jonathan 415
1102     class SelectPropertiesDialog(wxDialog):
1103    
1104     def __init__(self, parent, prop, shapeType):
1105     wxDialog.__init__(self, parent, -1, _("Select Properties"),
1106 jonathan 507 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1107 jonathan 415
1108 jonathan 441 self.prop = ClassGroupProperties(prop)
1109 jonathan 415
1110 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
1111 jonathan 415
1112 jonathan 430 itemBox = wxBoxSizer(wxHORIZONTAL)
1113    
1114     # preview box
1115     previewBox = wxBoxSizer(wxVERTICAL)
1116     previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1117     0, wxALIGN_LEFT | wxALL, 4)
1118 jonathan 630
1119     self.previewWin = ClassGroupPropertiesCtrl(
1120     self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1121     (40, 40), wxSIMPLE_BORDER)
1122    
1123     self.previewWin.AllowEdit(False)
1124    
1125 jonathan 615 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1126 jonathan 430
1127     itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1128    
1129     # control box
1130     ctrlBox = wxBoxSizer(wxVERTICAL)
1131 jonathan 485
1132     lineColorBox = wxBoxSizer(wxHORIZONTAL)
1133 jonathan 813 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1134     button.SetFocus()
1135     lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1136 jonathan 460 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1137 jonathan 430
1138 jonathan 485 lineColorBox.Add(
1139 jonathan 500 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1140 jonathan 485 1, wxALL | wxGROW, 4)
1141     EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1142     self._OnChangeLineColorTrans)
1143    
1144     ctrlBox.Add(lineColorBox, 0,
1145     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1146    
1147 jonathan 430 if shapeType != SHAPETYPE_ARC:
1148 jonathan 485 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1149     fillColorBox.Add(
1150 jonathan 500 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1151 jonathan 485 1, wxALL | wxGROW, 4)
1152 jonathan 460 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1153 jonathan 485 fillColorBox.Add(
1154 jonathan 500 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1155 jonathan 485 1, wxALL | wxGROW, 4)
1156     EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1157     self._OnChangeFillColorTrans)
1158     ctrlBox.Add(fillColorBox, 0,
1159     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1160 jonathan 430
1161 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
1162 jonathan 460 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1163 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1164 jonathan 415 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1165     min=1, max=10,
1166 jonathan 460 value=str(prop.GetLineWidth()),
1167     initial=prop.GetLineWidth())
1168 jonathan 415
1169 jonathan 460 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1170 jonathan 415
1171     spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1172    
1173 jonathan 430 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1174     itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1175     topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1176 jonathan 415
1177     #
1178     # Control buttons:
1179     #
1180     buttonBox = wxBoxSizer(wxHORIZONTAL)
1181 jonathan 813 button_ok = wxButton(self, wxID_OK, _("OK"))
1182     button_ok.SetDefault()
1183     buttonBox.Add(button_ok, 0, wxALL, 4)
1184     buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1185 jonathan 415 0, wxALL, 4)
1186     topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1187    
1188 jonathan 813 #EVT_BUTTON(self, wxID_OK, self._OnOK)
1189     #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1190 jonathan 415
1191 jonathan 509 self.SetAutoLayout(True)
1192 jonathan 415 self.SetSizer(topBox)
1193     topBox.Fit(self)
1194     topBox.SetSizeHints(self)
1195    
1196 jonathan 813 def OnOK(self, event):
1197 jonathan 372 self.EndModal(wxID_OK)
1198    
1199 jonathan 813 def OnCancel(self, event):
1200 jonathan 372 self.EndModal(wxID_CANCEL)
1201    
1202 jonathan 460 def _OnSpin(self, event):
1203     self.prop.SetLineWidth(self.spinCtrl.GetValue())
1204 jonathan 549 self.previewWin.Refresh()
1205 jonathan 392
1206 jonathan 430 def __GetColor(self, cur):
1207     dialog = wxColourDialog(self)
1208 jonathan 610 if cur is not Color.Transparent:
1209 jonathan 606 dialog.GetColourData().SetColour(Color2wxColour(cur))
1210    
1211 jonathan 430 ret = None
1212     if dialog.ShowModal() == wxID_OK:
1213     ret = wxColour2Color(dialog.GetColourData().GetColour())
1214    
1215     dialog.Destroy()
1216    
1217     return ret
1218    
1219 jonathan 460 def _OnChangeLineColor(self, event):
1220     clr = self.__GetColor(self.prop.GetLineColor())
1221 jonathan 430 if clr is not None:
1222 jonathan 460 self.prop.SetLineColor(clr)
1223 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1224 jonathan 430
1225 jonathan 485 def _OnChangeLineColorTrans(self, event):
1226 jonathan 610 self.prop.SetLineColor(Color.Transparent)
1227 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1228 jonathan 485
1229 jonathan 460 def _OnChangeFillColor(self, event):
1230 jonathan 430 clr = self.__GetColor(self.prop.GetFill())
1231     if clr is not None:
1232     self.prop.SetFill(clr)
1233 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1234 jonathan 430
1235 jonathan 485 def _OnChangeFillColorTrans(self, event):
1236 jonathan 610 self.prop.SetFill(Color.Transparent)
1237 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1238 jonathan 485
1239 jonathan 441 def GetClassGroupProperties(self):
1240 jonathan 415 return self.prop
1241 jonathan 392
1242    
1243 jonathan 549 class ClassDataPreviewWindow(wxWindow):
1244 jonathan 415
1245 jonathan 441 def __init__(self, rect, prop, shapeType,
1246 jonathan 430 parent = None, id = -1, size = wxDefaultSize):
1247     if parent is not None:
1248 jonathan 549 wxWindow.__init__(self, parent, id, (0, 0), size)
1249 jonathan 460 EVT_PAINT(self, self._OnPaint)
1250 jonathan 415
1251 jonathan 430 self.rect = rect
1252 jonathan 549
1253 jonathan 441 self.prop = prop
1254 jonathan 430 self.shapeType = shapeType
1255 jonathan 549 self.previewer = ClassDataPreviewer()
1256 jonathan 430
1257 jonathan 630 def GetProperties():
1258     return self.prop
1259    
1260 jonathan 460 def _OnPaint(self, event):
1261 jonathan 430 dc = wxPaintDC(self)
1262    
1263     # XXX: this doesn't seem to be having an effect:
1264     dc.DestroyClippingRegion()
1265    
1266 jonathan 549 if self.rect is None:
1267     w, h = self.GetSize()
1268     rect = wxRect(0, 0, w, h)
1269     else:
1270     rect = self.rect
1271 jonathan 430
1272 jonathan 549 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1273 jonathan 430
1274 jonathan 549 class ClassDataPreviewer:
1275 jonathan 430
1276 jonathan 549 def Draw(self, dc, rect, prop, shapeType):
1277    
1278 jonathan 606 assert dc is not None
1279     assert isinstance(prop, ClassGroupProperties)
1280 jonathan 549
1281 jonathan 430 if rect is None:
1282 jonathan 549 x = 0
1283     y = 0
1284     w, h = dc.GetSize()
1285 jonathan 430 else:
1286     x = rect.GetX()
1287     y = rect.GetY()
1288     w = rect.GetWidth()
1289     h = rect.GetHeight()
1290    
1291 jonathan 460 stroke = prop.GetLineColor()
1292 jonathan 610 if stroke is Color.Transparent:
1293 jonathan 392 pen = wxTRANSPARENT_PEN
1294     else:
1295 jonathan 430 pen = wxPen(Color2wxColour(stroke),
1296 jonathan 460 prop.GetLineWidth(),
1297 jonathan 392 wxSOLID)
1298    
1299 jonathan 441 stroke = prop.GetFill()
1300 jonathan 610 if stroke is Color.Transparent:
1301 jonathan 392 brush = wxTRANSPARENT_BRUSH
1302     else:
1303 jonathan 430 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1304 jonathan 392
1305     dc.SetPen(pen)
1306     dc.SetBrush(brush)
1307    
1308 jonathan 415 if shapeType == SHAPETYPE_ARC:
1309 jonathan 430 dc.DrawSpline([wxPoint(x, y + h),
1310     wxPoint(x + w/2, y + h/4),
1311     wxPoint(x + w/2, y + h/4*3),
1312     wxPoint(x + w, y)])
1313 jonathan 392
1314 jonathan 576 elif shapeType == SHAPETYPE_POINT:
1315 jonathan 415
1316 jonathan 430 dc.DrawCircle(x + w/2, y + h/2,
1317 jonathan 460 (min(w, h) - prop.GetLineWidth())/2)
1318 jonathan 392
1319 jonathan 576 elif shapeType == SHAPETYPE_POLYGON:
1320     dc.DrawRectangle(x, y, w, h)
1321    
1322 jonathan 415 class ClassRenderer(wxPyGridCellRenderer):
1323    
1324     def __init__(self, shapeType):
1325     wxPyGridCellRenderer.__init__(self)
1326 jonathan 549 self.shapeType = shapeType
1327     self.previewer = ClassDataPreviewer()
1328 jonathan 415
1329     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1330 jonathan 485 data = grid.GetTable().GetClassGroup(row)
1331 jonathan 415
1332     dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1333     rect.GetWidth(), rect.GetHeight())
1334     dc.SetPen(wxPen(wxLIGHT_GREY))
1335     dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1336     dc.DrawRectangle(rect.GetX(), rect.GetY(),
1337     rect.GetWidth(), rect.GetHeight())
1338    
1339 jonathan 441 if not isinstance(data, ClassGroupMap):
1340 jonathan 549 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1341 jonathan 415
1342     if isSelected:
1343 jonathan 615 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1344 jonathan 415 dc.SetBrush(wxTRANSPARENT_BRUSH)
1345 jonathan 615
1346 jonathan 415 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1347     rect.GetWidth(), rect.GetHeight())
1348    
1349 jonathan 392 dc.DestroyClippingRegion()
1350    
1351 jonathan 630
1352     class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1353    
1354     def __init__(self, parent, id, props, shapeType,
1355     size = wxDefaultSize, style = 0):
1356    
1357     wxWindow.__init__(self, parent, id, size = size, style = style)
1358    
1359     self.SetProperties(props)
1360     self.SetShapeType(shapeType)
1361     self.AllowEdit(True)
1362    
1363     EVT_PAINT(self, self._OnPaint)
1364     EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1365    
1366     self.previewer = ClassDataPreviewer()
1367    
1368     def _OnPaint(self, event):
1369     dc = wxPaintDC(self)
1370    
1371     # XXX: this doesn't seem to be having an effect:
1372     dc.DestroyClippingRegion()
1373    
1374     w, h = self.GetClientSize()
1375    
1376     self.previewer.Draw(dc,
1377     wxRect(0, 0, w, h),
1378     self.GetProperties(),
1379     self.GetShapeType())
1380    
1381    
1382     def GetProperties(self):
1383     return self.props
1384    
1385     def SetProperties(self, props):
1386     self.props = props
1387     self.Refresh()
1388    
1389     def GetShapeType(self):
1390     return self.shapeType
1391    
1392     def SetShapeType(self, shapeType):
1393     self.shapeType = shapeType
1394     self.Refresh()
1395    
1396     def AllowEdit(self, allow):
1397     self.allowEdit = allow
1398    
1399     def DoEdit(self):
1400     if not self.allowEdit: return
1401    
1402     propDlg = SelectPropertiesDialog(NULL,
1403     self.GetProperties(),
1404     self.GetShapeType())
1405    
1406     if propDlg.ShowModal() == wxID_OK:
1407     new_prop = propDlg.GetClassGroupProperties()
1408     self.SetProperties(new_prop)
1409     self.Refresh()
1410    
1411     propDlg.Destroy()
1412    
1413     def _OnLeftDClick(self, event):
1414     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