/[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 1342 - (hide annotations)
Tue Jul 1 16:10:54 2003 UTC (21 years, 8 months ago) by jonathan
File MIME type: text/x-python
File size: 46394 byte(s)
Fixes RTbug #1971.
(Classifier.__BuildClassification, Classifier.__SetGridTable):
        Call Classification.SetFieldInfo() instead of SetFieldType.

1 bh 476 # Copyright (c) 2001, 2003 by Intevation GmbH
2 jonathan 372 # Authors:
3     # Jonathan Coles <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with Thuban for details.
7    
8     """Dialog for classifying how layers are displayed"""
9    
10     __version__ = "$Revision$"
11    
12 jonathan 376 import copy
13    
14 jonathan 473 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
15     FIELDTYPE_STRING
16    
17 jonathan 372 from wxPython.wx import *
18     from wxPython.grid import *
19    
20 jan 374 from Thuban import _
21 jonathan 878 from Thuban.UI.common import Color2wxColour, wxColour2Color
22 jan 374
23 bh 1142 from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
24 jonathan 878 from Thuban.Model.range import Range
25     from Thuban.Model.classification import \
26     Classification, ClassGroupDefault, \
27     ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
28     ClassGroupProperties
29 jonathan 392
30 jonathan 1342 from Thuban.Model.color import Transparent
31 jonathan 415
32 jonathan 935 from Thuban.Model.layer import Layer, RasterLayer, \
33 jonathan 878 SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
34 jonathan 392
35 jonathan 1101 from Thuban.UI.classgen import ClassGenDialog
36 jonathan 606
37 frank 1058 from dialogs import NonModalNonParentDialog
38 jonathan 485
39 jonathan 372 ID_CLASS_TABLE = 40011
40    
41    
42 jonathan 460 # table columns
43 jonathan 638 COL_VISIBLE = 0
44     COL_SYMBOL = 1
45     COL_VALUE = 2
46     COL_LABEL = 3
47     NUM_COLS = 4
48 jonathan 415
49 jonathan 460 # indices into the client data lists in Classifier.fields
50 jonathan 451 FIELD_CLASS = 0
51     FIELD_TYPE = 1
52     FIELD_NAME = 2
53    
54 jonathan 415 #
55     # this is a silly work around to ensure that the table that is
56     # passed into SetTable is the same that is returned by GetTable
57     #
58     import weakref
59     class ClassGrid(wxGrid):
60    
61 jonathan 570
62 jonathan 606 def __init__(self, parent, classifier):
63 jonathan 460 """Constructor.
64    
65     parent -- the parent window
66    
67     clazz -- the working classification that this grid should
68     use for display.
69     """
70    
71 jonathan 813 wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
72 jonathan 415
73 jonathan 606 self.classifier = classifier
74    
75 jonathan 570 self.currentSelection = []
76    
77 jonathan 460 EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
78 jonathan 451 EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
79     EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
80 jonathan 606 EVT_GRID_COL_SIZE(self, self._OnCellResize)
81     EVT_GRID_ROW_SIZE(self, self._OnCellResize)
82 jonathan 451
83 jonathan 638 #def GetCellAttr(self, row, col):
84     #print "GetCellAttr ", row, col
85     #wxGrid.GetCellAttr(self, row, col)
86    
87 jonathan 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 1342 button_close = wxButton(self, wxID_CANCEL, _("Close"))
762 jonathan 813 button_ok.SetDefault()
763 jonathan 485
764 jonathan 813 ############################
765     # Layout the controls
766 jonathan 485 #
767 jonathan 509
768 jonathan 813 topBox = wxBoxSizer(wxVERTICAL)
769     panelBox = wxBoxSizer(wxVERTICAL)
770 jonathan 451
771 jonathan 813 sizer = wxBoxSizer(wxHORIZONTAL)
772     sizer.Add(wxStaticText(panel, -1, _("Title: ")),
773     0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
774     sizer.Add(text_title, 1, wxGROW, 0)
775 jonathan 485
776 jonathan 813 panelBox.Add(sizer, 0, wxGROW, 4)
777 jonathan 606
778 jonathan 935 if isinstance(layer, RasterLayer):
779     type = "Image"
780     else:
781     type = layer.ShapeType()
782    
783     panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
784 jonathan 813 0, wxALIGN_LEFT | wxALL, 4)
785 jonathan 485
786 jonathan 935 if layer.HasClassification():
787 jonathan 485
788 jonathan 935 classBox = wxStaticBoxSizer(
789     wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
790 jonathan 485
791    
792 jonathan 935 sizer = wxBoxSizer(wxHORIZONTAL)
793     sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
794     0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
795     sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
796 jonathan 570
797 jonathan 935 classBox.Add(sizer, 0, wxGROW, 4)
798 jonathan 570
799 jonathan 935 classBox.Add(self.fieldTypeText, 0,
800     wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
801 jonathan 570
802 jonathan 935 controlBox = wxBoxSizer(wxHORIZONTAL)
803     controlButtonBox = wxBoxSizer(wxVERTICAL)
804 jonathan 570
805 jonathan 935 controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
806     controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
807     controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
808     controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
809     controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
810     controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
811     controlButtonBox.Add(button_remove, 0,
812     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
813 jonathan 415
814 jonathan 935 controlBox.Add(self.classGrid, 1, wxGROW, 0)
815     controlBox.Add(controlButtonBox, 0, wxGROW, 10)
816 jonathan 661
817 jonathan 935 classBox.Add(controlBox, 1, wxGROW, 10)
818     panelBox.Add(classBox, 1, wxGROW, 0)
819 jonathan 415
820 jonathan 935
821 jonathan 813 buttonBox = wxBoxSizer(wxHORIZONTAL)
822 frank 977 buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)
823     buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)
824     buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
825     buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)
826 jonathan 509
827 jonathan 650 panel.SetAutoLayout(True)
828     panel.SetSizer(panelBox)
829 jonathan 813 panelBox.Fit(panel)
830 jonathan 650 panelBox.SetSizeHints(panel)
831    
832     topBox.Add(panel, 1, wxGROW | wxALL, 4)
833 frank 977 topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
834 jonathan 650
835 jonathan 813 self.SetAutoLayout(True)
836     self.SetSizer(topBox)
837     topBox.Fit(self)
838     topBox.SetSizeHints(self)
839     self.Layout()
840    
841 jonathan 650 ###########
842    
843 jonathan 813 EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
844     EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
845     EVT_BUTTON(self, wxID_OK, self._OnOK)
846 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
847 jonathan 813 EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
848 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
849 jonathan 372
850 jonathan 813 EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
851     EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
852     EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
853     EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
854     EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
855     EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
856 jonathan 509
857 jonathan 549 ######################
858    
859 jonathan 935 text_title.SetFocus()
860 jonathan 549 self.haveApplied = False
861    
862 bh 1142 def unsubscribe_messages(self):
863     self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
864     self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
865     self.layer_shapestore_replaced)
866    
867     def map_layers_removed(self, map):
868     if self.layer not in self.map.Layers():
869     self.Close()
870    
871     def layer_shapestore_replaced(self, *args):
872     self.Close()
873    
874 jonathan 638 def EditSymbol(self, row):
875 jonathan 606 table = self.classGrid.GetTable()
876     prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
877    
878     # get a new ClassGroupProperties object and copy the
879     # values over to our current object
880 jonathan 1302 propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
881 jonathan 630
882     self.Enable(False)
883 jonathan 606 if propDlg.ShowModal() == wxID_OK:
884     new_prop = propDlg.GetClassGroupProperties()
885     table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
886 jonathan 630 self.Enable(True)
887 jonathan 606 propDlg.Destroy()
888    
889 jonathan 630 def _SetClassification(self, clazz):
890    
891     self.fields.SetClientData(self.__cur_field, clazz)
892     self.classGrid.GetTable().SetClassification(clazz)
893 jonathan 606
894 jonathan 615 def __BuildClassification(self, fieldIndex, copyClass = False):
895 jonathan 415
896 jonathan 615 # numRows = self.classGrid.GetNumberRows()
897     # assert numRows > 0 # there should always be a default row
898 jonathan 496
899 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 1342 clazz.SetFieldInfo(fieldName, fieldType)
913 jonathan 460
914 jonathan 415
915 jonathan 615 # table = self.classGrid.GetTable()
916     # clazz.SetDefaultGroup(table.GetClassGroup(0))
917 jonathan 415
918 jonathan 615 # for i in range(1, numRows):
919     # clazz.AppendGroup(table.GetClassGroup(i))
920 jonathan 460
921 jonathan 415 return clazz
922    
923 jonathan 570 def __SetGridTable(self, fieldIndex, group = None):
924 jonathan 415
925 jonathan 460 clazz = self.fields.GetClientData(fieldIndex)
926 jonathan 415
927 jonathan 460 if clazz is None:
928     clazz = Classification()
929     clazz.SetDefaultGroup(
930     ClassGroupDefault(
931 jonathan 485 self.layer.GetClassification().
932     GetDefaultGroup().GetProperties()))
933 jonathan 460
934     fieldName = self.fields.GetString(fieldIndex)
935     fieldType = self.layer.GetFieldType(fieldName)
936 jonathan 1342 clazz.SetFieldInfo(fieldName, fieldType)
937 jonathan 460
938 jonathan 570 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
939 jonathan 460
940 jonathan 485 def __SetFieldTypeText(self, fieldIndex):
941     fieldName = self.fields.GetString(fieldIndex)
942     fieldType = self.layer.GetFieldType(fieldName)
943    
944 jonathan 606 assert Classifier.type2string.has_key(fieldType)
945 jonathan 485
946 jonathan 496 text = Classifier.type2string[fieldType]
947    
948 jonathan 650 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
949 jonathan 485
950 jonathan 570 def __SelectField(self, newIndex, oldIndex = -1, group = None):
951 jonathan 611 """This method assumes that the current selection for the
952     combo has already been set by a call to SetSelection().
953     """
954 jonathan 460
955 jonathan 606 assert oldIndex >= -1
956 jonathan 415
957 jonathan 498 if oldIndex != -1:
958     clazz = self.__BuildClassification(oldIndex)
959     self.fields.SetClientData(oldIndex, clazz)
960 jonathan 485
961 jonathan 570 self.__SetGridTable(newIndex, group)
962 jonathan 498
963 jonathan 1307 self.__EnableButtons(EB_SELECT_FIELD)
964 jonathan 498
965     self.__SetFieldTypeText(newIndex)
966 jonathan 485
967 jonathan 650 def __SetTitle(self, title):
968     if title != "":
969     title = ": " + title
970    
971     self.SetTitle(_("Layer Properties") + title)
972 jonathan 496
973 jonathan 638 def _OnEditSymbol(self, event):
974 jonathan 606 sel = self.classGrid.GetCurrentSelection()
975    
976     if len(sel) == 1:
977 jonathan 638 self.EditSymbol(sel[0])
978 jonathan 606
979 jonathan 496 def _OnFieldSelect(self, event):
980 jonathan 498 index = self.fields.GetSelection()
981     self.__SelectField(index, self.__cur_field)
982     self.__cur_field = index
983 jonathan 485
984 jonathan 638 def _OnTry(self, event):
985 jonathan 415 """Put the data from the table into a new Classification and hand
986     it to the layer.
987     """
988    
989 jonathan 935 if self.layer.HasClassification():
990     clazz = self.fields.GetClientData(self.__cur_field)
991 jonathan 415
992 jonathan 935 #
993     # only build the classification if there wasn't one to
994     # to begin with or it has been modified
995     #
996     self.classGrid.SaveEditControlValue()
997     if clazz is None or self.classGrid.GetTable().IsModified():
998     clazz = self.__BuildClassification(self.__cur_field, True)
999 jonathan 415
1000 jonathan 935 self.layer.SetClassification(clazz)
1001 jonathan 415
1002 jonathan 549 self.haveApplied = True
1003    
1004 jonathan 485 def _OnOK(self, event):
1005 jonathan 638 self._OnTry(event)
1006 jonathan 615 self.Close()
1007 jonathan 415
1008 jonathan 813 def OnClose(self, event):
1009 bh 1207 self.unsubscribe_messages()
1010 frank 1058 NonModalNonParentDialog.OnClose(self, event)
1011 jonathan 813
1012 jonathan 615 def _OnCloseBtn(self, event):
1013     """Close is similar to Cancel except that any changes that were
1014     made and applied remain applied, but the currently displayed
1015     classification is discarded.
1016     """
1017    
1018     self.Close()
1019    
1020 jonathan 638 def _OnRevert(self, event):
1021 jonathan 485 """The layer's current classification stays the same."""
1022 jonathan 549 if self.haveApplied:
1023     self.layer.SetClassification(self.originalClass)
1024    
1025 jonathan 638 #self.Close()
1026 jonathan 415
1027 jonathan 460 def _OnAdd(self, event):
1028 jonathan 451 self.classGrid.AppendRows()
1029 jonathan 415
1030 jonathan 460 def _OnRemove(self, event):
1031 jonathan 451 self.classGrid.DeleteSelectedRows()
1032    
1033 jonathan 606 def _OnGenClass(self, event):
1034 jonathan 415
1035 jonathan 630 self.genDlg = ClassGenDialog(self, self.layer,
1036     self.fields.GetString(self.__cur_field))
1037 jonathan 606
1038 jonathan 630 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1039 jonathan 606
1040 jonathan 1307 self.__EnableButtons(EB_GEN_CLASS)
1041 jonathan 630
1042     self.genDlg.Show()
1043    
1044     def _OnGenDialogClose(self, event):
1045     self.genDlg.Destroy()
1046 jonathan 1307 self.genDlg = None
1047     self.__EnableButtons(EB_GEN_CLASS)
1048 jonathan 630
1049 jonathan 460 def _OnMoveUp(self, event):
1050     sel = self.classGrid.GetCurrentSelection()
1051 jonathan 415
1052 jonathan 460 if len(sel) == 1:
1053     i = sel[0]
1054     if i > 1:
1055     table = self.classGrid.GetTable()
1056     x = table.GetClassGroup(i - 1)
1057     y = table.GetClassGroup(i)
1058     table.SetClassGroup(i - 1, y)
1059     table.SetClassGroup(i, x)
1060     self.classGrid.ClearSelection()
1061     self.classGrid.SelectRow(i - 1)
1062 jonathan 570 self.classGrid.MakeCellVisible(i - 1, 0)
1063 jonathan 460
1064     def _OnMoveDown(self, event):
1065     sel = self.classGrid.GetCurrentSelection()
1066    
1067     if len(sel) == 1:
1068     i = sel[0]
1069     table = self.classGrid.GetTable()
1070     if 0 < i < table.GetNumberRows() - 1:
1071     x = table.GetClassGroup(i)
1072     y = table.GetClassGroup(i + 1)
1073     table.SetClassGroup(i, y)
1074     table.SetClassGroup(i + 1, x)
1075     self.classGrid.ClearSelection()
1076     self.classGrid.SelectRow(i + 1)
1077 jonathan 570 self.classGrid.MakeCellVisible(i + 1, 0)
1078 jonathan 460
1079 jonathan 650 def _OnTitleChanged(self, event):
1080     obj = event.GetEventObject()
1081 jonathan 460
1082 jonathan 650 self.layer.SetTitle(obj.GetValue())
1083     self.__SetTitle(self.layer.Title())
1084    
1085 jonathan 1307 self.__EnableButtons(EB_LAYER_TITLE)
1086 jonathan 650
1087 jonathan 1307 def __EnableButtons(self, case):
1088 jonathan 650
1089 jonathan 1307 list = {wxID_OK : True,
1090     wxID_CANCEL : True,
1091     ID_PROPERTY_ADD : True,
1092     ID_PROPERTY_MOVEUP : True,
1093     ID_PROPERTY_MOVEDOWN : True,
1094     ID_PROPERTY_REMOVE : True,
1095     ID_PROPERTY_SELECT : True,
1096     ID_PROPERTY_FIELDTEXT : True,
1097     ID_PROPERTY_GENCLASS : True,
1098     ID_PROPERTY_EDITSYM : True}
1099    
1100 jonathan 650 if case == EB_LAYER_TITLE:
1101 jonathan 1307 if self.layer.Title() == "":
1102     list[wxID_OK] = False
1103     list[wxID_CANCEL] = False
1104 jonathan 650
1105     elif case == EB_SELECT_FIELD:
1106 jonathan 1307 if self.fields.GetSelection() == 0:
1107     list[ID_PROPERTY_GENCLASS] = False
1108     list[ID_PROPERTY_ADD] = False
1109     list[ID_PROPERTY_MOVEUP] = False
1110     list[ID_PROPERTY_MOVEDOWN] = False
1111     list[ID_PROPERTY_REMOVE] = False
1112 jonathan 650
1113     elif case == EB_GEN_CLASS:
1114 jonathan 1307 if self.genDlg is not None:
1115     list[ID_PROPERTY_SELECT] = False
1116     list[ID_PROPERTY_FIELDTEXT] = False
1117     list[ID_PROPERTY_GENCLASS] = False
1118 jonathan 650
1119 jonathan 1307 for id, enable in list.items():
1120     win = self.FindWindowById(id)
1121     if win:
1122     win.Enable(enable)
1123 jonathan 650
1124 jonathan 415 ID_SELPROP_SPINCTRL = 4002
1125 jonathan 430 ID_SELPROP_PREVIEW = 4003
1126     ID_SELPROP_STROKECLR = 4004
1127     ID_SELPROP_FILLCLR = 4005
1128 jonathan 485 ID_SELPROP_STROKECLRTRANS = 4006
1129     ID_SELPROP_FILLCLRTRANS = 4007
1130 jonathan 415
1131     class SelectPropertiesDialog(wxDialog):
1132    
1133     def __init__(self, parent, prop, shapeType):
1134     wxDialog.__init__(self, parent, -1, _("Select Properties"),
1135 jonathan 507 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1136 jonathan 415
1137 jonathan 441 self.prop = ClassGroupProperties(prop)
1138 jonathan 415
1139 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
1140 jonathan 415
1141 jonathan 430 itemBox = wxBoxSizer(wxHORIZONTAL)
1142    
1143     # preview box
1144     previewBox = wxBoxSizer(wxVERTICAL)
1145     previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1146     0, wxALIGN_LEFT | wxALL, 4)
1147 jonathan 630
1148     self.previewWin = ClassGroupPropertiesCtrl(
1149     self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1150     (40, 40), wxSIMPLE_BORDER)
1151    
1152     self.previewWin.AllowEdit(False)
1153    
1154 jonathan 615 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1155 jonathan 430
1156     itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1157    
1158     # control box
1159     ctrlBox = wxBoxSizer(wxVERTICAL)
1160 jonathan 485
1161     lineColorBox = wxBoxSizer(wxHORIZONTAL)
1162 jonathan 813 button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1163     button.SetFocus()
1164     lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1165 jonathan 460 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1166 jonathan 430
1167 jonathan 485 lineColorBox.Add(
1168 jonathan 500 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1169 jonathan 485 1, wxALL | wxGROW, 4)
1170     EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1171     self._OnChangeLineColorTrans)
1172    
1173     ctrlBox.Add(lineColorBox, 0,
1174     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1175    
1176 jonathan 430 if shapeType != SHAPETYPE_ARC:
1177 jonathan 485 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1178     fillColorBox.Add(
1179 jonathan 500 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1180 jonathan 485 1, wxALL | wxGROW, 4)
1181 jonathan 460 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1182 jonathan 485 fillColorBox.Add(
1183 jonathan 500 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1184 jonathan 485 1, wxALL | wxGROW, 4)
1185     EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1186     self._OnChangeFillColorTrans)
1187     ctrlBox.Add(fillColorBox, 0,
1188     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1189 jonathan 430
1190 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
1191 jonathan 460 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1192 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1193 jonathan 415 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1194     min=1, max=10,
1195 jonathan 460 value=str(prop.GetLineWidth()),
1196     initial=prop.GetLineWidth())
1197 jonathan 415
1198 jonathan 460 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1199 jonathan 415
1200     spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1201    
1202 jonathan 430 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1203     itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1204     topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1205 jonathan 415
1206     #
1207     # Control buttons:
1208     #
1209     buttonBox = wxBoxSizer(wxHORIZONTAL)
1210 jonathan 813 button_ok = wxButton(self, wxID_OK, _("OK"))
1211 frank 977 buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1212 jonathan 813 buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1213 frank 977 0, wxRIGHT|wxEXPAND, 10)
1214     topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1215 jonathan 1342
1216     button_ok.SetDefault()
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 1342 if cur is not 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 1342 self.prop.SetLineColor(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 1342 self.prop.SetFill(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 1342 if stroke is 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 1342 if stroke is 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