/[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 835 - (hide annotations)
Tue May 6 15:52:41 2003 UTC (21 years, 10 months ago) by bh
File MIME type: text/x-python
File size: 44533 byte(s)
(Classifier.__init__)
(Classifier.__init__): Adapt to new table interface

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