/[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 1307 - (hide annotations)
Thu Jun 26 17:00:17 2003 UTC (21 years, 8 months ago) by jonathan
File MIME type: text/x-python
File size: 46429 byte(s)
(Classifier.__EnableButtons):
        Reset the status of the buttons as the situation warrants,
        but in a better more reliable way by not relying on the
        current status to determine what needs to change.

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