/[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 1142 - (hide annotations)
Tue Jun 10 09:41:57 2003 UTC (21 years, 8 months ago) by bh
File MIME type: text/x-python
File size: 45747 byte(s)
* Thuban/Model/messages.py (LAYER_SHAPESTORE_REPLACED): New
message.

* Thuban/Model/layer.py (Layer.SetShapeStore): Send
LAYER_SHAPESTORE_REPLACED when the shapestore changes. A
LAYER_CHANGED will still be sent if the classification changes.

* Thuban/UI/classifier.py (Classifier.__init__): Add the map as
parameter so we can subscribe to some of its messages
(Classifier.__init__): Subscribe to the map's MAP_LAYERS_REMOVED
and the layer's LAYER_SHAPESTORE_REPLACED
(Classifier.unsubscribe_messages): New. Unsubscribe from message
subscribed to in __init__
(Classifier.map_layers_removed)
(Classifier.layer_shapestore_replaced): receivers for the messages
subscribed to in __init__. Unsubscribe and close the dialog

* Thuban/UI/mainwindow.py (MainWindow.OpenLayerProperties): Pass
the map to the Classifier dialog

* test/test_layer.py (SetShapeStoreTests): Derive from
SubscriberMixin as well so we can test messages
(SetShapeStoreTests.setUp): Subscribe to some of the layer's
messages
(SetShapeStoreTests.tearDown): Clear the messages again
(SetShapeStoreTests.test_sanity): Expand the doc-string and check
for the modified flag too
(SetShapeStoreTests.test_set_shape_store_modified_flag): New test
to check whether SetShapeStore sets the modified flag
(SetShapeStoreTests.test_set_shape_store_different_field_name)
(SetShapeStoreTests.test_set_shape_store_same_field)
(SetShapeStoreTests.test_set_shape_store_same_field_different_type):
Add tests for the messages. This checks both the new
LAYER_SHAPESTORE_REPLACED and the older LAYER_CHANGED

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