/[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 783 - (hide annotations)
Tue Apr 29 17:29:32 2003 UTC (21 years, 10 months ago) by jonathan
File MIME type: text/x-python
File size: 44688 byte(s)
Remove Thuban.common import.

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 485 wxGrid.__init__(self, parent, ID_CLASS_TABLE)
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_OK = 4001
654     ID_PROPERTY_REVERT = 4002
655     ID_PROPERTY_ADD = 4003
656     ID_PROPERTY_GENCLASS = 4004
657     ID_PROPERTY_REMOVE = 4005
658     ID_PROPERTY_MOVEUP = 4006
659     ID_PROPERTY_MOVEDOWN = 4007
660     ID_PROPERTY_TRY = 4008
661     ID_PROPERTY_EDITSYM = 4009
662     ID_PROPERTY_CLOSE = 4010
663     ID_PROPERTY_SELECT = 4011
664     ID_PROPERTY_TITLE = 4012
665     ID_PROPERTY_FIELDTEXT = 4013
666 jonathan 630
667     BTN_ADD = 0
668     BTN_EDIT = 1
669     BTN_GEN = 2
670     BTN_UP = 3
671     BTN_DOWN = 4
672     BTN_RM = 5
673    
674 jonathan 650 EB_LAYER_TITLE = 0
675     EB_SELECT_FIELD = 1
676     EB_GEN_CLASS = 2
677    
678 jonathan 485 class Classifier(NonModalDialog):
679 bh 535
680 jonathan 630 type2string = {None: _("None"),
681     FIELDTYPE_STRING: _("Text"),
682     FIELDTYPE_INT: _("Integer"),
683     FIELDTYPE_DOUBLE: _("Decimal")}
684    
685 jonathan 570 def __init__(self, parent, name, layer, group = None):
686 jonathan 650 NonModalDialog.__init__(self, parent, name, "")
687 jonathan 372
688 jonathan 650 self.__SetTitle(layer.Title())
689 jonathan 509
690 jonathan 415 self.layer = layer
691    
692 jonathan 485 self.originalClass = self.layer.GetClassification()
693     field = self.originalClass.GetField()
694     fieldType = self.originalClass.GetFieldType()
695    
696 jonathan 630 self.genDlg = None
697    
698 jonathan 372 topBox = wxBoxSizer(wxVERTICAL)
699    
700 jonathan 661 panel = wxPanel(self, -1, size=(100, 100))
701    
702     panelBox = wxBoxSizer(wxVERTICAL)
703    
704 jonathan 650 sizer = wxBoxSizer(wxHORIZONTAL)
705 jonathan 661 sizer.Add(wxStaticText(panel, -1, _("Title: ")),
706 jonathan 650 0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
707 jonathan 661 sizer.Add(wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title()),
708 jonathan 650 1, wxGROW | wxALL, 4)
709     EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
710    
711 jonathan 661 panelBox.Add(sizer, 0, wxGROW, 4)
712 jonathan 650
713 jonathan 661 panelBox.Add(wxStaticText(panel, -1,
714 jonathan 650 _("Type: %s") % layer.ShapeType()),
715 jonathan 451 0, wxALIGN_LEFT | wxALL, 4)
716 jonathan 415
717 jonathan 372
718 jonathan 650 #####################
719    
720     #panelBox = wxBoxSizer(wxVERTICAL)
721 jonathan 661 classBox = wxStaticBoxSizer(
722 jonathan 650 wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
723    
724    
725 jonathan 485 #
726 jonathan 712 # make field choice box
727 jonathan 485 #
728 jonathan 712 self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
729     #self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
730     # style = wxCB_READONLY)
731 jonathan 372
732     self.num_cols = layer.table.field_count()
733 jonathan 441 # just assume the first field in case one hasn't been
734     # specified in the file.
735 jonathan 451 self.__cur_field = 0
736 jonathan 460
737     self.fields.Append("<None>")
738    
739 jonathan 650 if self.originalClass.GetFieldType() is None:
740     self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
741     else:
742     self.fields.SetClientData(0, None)
743    
744 jonathan 372 for i in range(self.num_cols):
745     type, name, len, decc = layer.table.field_info(i)
746 jonathan 451 self.fields.Append(name)
747    
748 jonathan 415 if name == field:
749 jonathan 460 self.__cur_field = i + 1
750 jonathan 615 self.fields.SetClientData(i + 1,
751     copy.deepcopy(self.originalClass))
752 jonathan 451 else:
753 jonathan 460 self.fields.SetClientData(i + 1, None)
754 jonathan 372
755    
756 jonathan 509 ###########
757 jonathan 485
758 jonathan 650
759     sizer = wxBoxSizer(wxHORIZONTAL)
760     sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
761     0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
762     sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
763 jonathan 712 EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
764     #EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
765 jonathan 650
766 jonathan 661 classBox.Add(sizer, 0, wxGROW, 4)
767 jonathan 650
768 jonathan 509 self.fieldTypeText = wxStaticText(panel, -1, "")
769 jonathan 661 classBox.Add(self.fieldTypeText, 0,
770 jonathan 560 wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
771 jonathan 485
772 jonathan 451
773 jonathan 372 #
774 jonathan 570 # Control Box
775 jonathan 372 #
776 jonathan 415 controlBox = wxBoxSizer(wxHORIZONTAL)
777 jonathan 485
778 jonathan 460
779 jonathan 509 ###########
780 jonathan 485 #
781     # Control buttons:
782     #
783 jonathan 509 controlButtonBox = wxBoxSizer(wxVERTICAL)
784    
785 jonathan 650 button = wxButton(panel, ID_PROPERTY_GENCLASS, _("Generate Class"))
786 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
787 jonathan 451
788 jonathan 650 button = wxButton(panel, ID_PROPERTY_ADD, _("Add"))
789 jonathan 570 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
790 jonathan 485
791 jonathan 650 button = wxButton(panel, ID_PROPERTY_MOVEUP, _("Move Up"))
792 jonathan 606 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
793    
794 jonathan 650 button = wxButton(panel, ID_PROPERTY_MOVEDOWN, _("Move Down"))
795 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
796    
797 jonathan 650 button = wxButton(panel, ID_PROPERTY_EDITSYM, _("Edit Symbol"))
798 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
799    
800     controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
801    
802 jonathan 650 button = wxButton(panel, ID_PROPERTY_REMOVE, _("Remove"))
803 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
804    
805 jonathan 570
806     ###########
807     #
808     # Classification data table
809     #
810    
811 jonathan 606 self.classGrid = ClassGrid(panel, self)
812 jonathan 570
813     # calling __SelectField after creating the classGrid fills in the
814     # grid with the correct information
815 jonathan 650 self.fields.SetSelection(self.__cur_field)
816     self.__SelectField(self.__cur_field, group = group)
817 jonathan 570
818     controlBox.Add(self.classGrid, 1, wxGROW, 0)
819 jonathan 415 controlBox.Add(controlButtonBox, 0, wxGROW, 10)
820    
821 jonathan 661 classBox.Add(controlBox, 1, wxGROW, 10)
822     panelBox.Add(classBox, 1, wxGROW, 0)
823    
824 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
825     EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
826     EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
827     EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
828     EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
829     EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
830 jonathan 415
831 jonathan 509 ###########
832    
833 jonathan 661
834 jonathan 650 panel.SetAutoLayout(True)
835     panel.SetSizer(panelBox)
836     panelBox.SetSizeHints(panel)
837    
838     topBox.Add(panel, 1, wxGROW | wxALL, 4)
839    
840     ###########
841    
842 jonathan 372 buttonBox = wxBoxSizer(wxHORIZONTAL)
843 jonathan 650 buttonBox.Add(wxButton(self, ID_PROPERTY_TRY, _("Try")),
844 jonathan 372 0, wxALL, 4)
845 jonathan 485 buttonBox.Add(60, 20, 0, wxALL, 4)
846 jonathan 650 buttonBox.Add(wxButton(self, ID_PROPERTY_REVERT, _("Revert")),
847 jonathan 485 0, wxALL, 4)
848     buttonBox.Add(60, 20, 0, wxALL, 4)
849 jonathan 650 buttonBox.Add(wxButton(self, ID_PROPERTY_OK, _("OK")),
850 jonathan 615 0, wxALL, 4)
851     buttonBox.Add(60, 20, 0, wxALL, 4)
852 jonathan 650 buttonBox.Add(wxButton(self, ID_PROPERTY_CLOSE, _("Close")),
853 jonathan 372 0, wxALL, 4)
854 jonathan 650 topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
855 jonathan 372
856 jonathan 650 EVT_BUTTON(self, ID_PROPERTY_OK, self._OnOK)
857     EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
858     EVT_BUTTON(self, ID_PROPERTY_CLOSE, self._OnCloseBtn)
859     EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
860 jonathan 372
861 jonathan 509 ###########
862    
863 jonathan 650 topBox.SetSizeHints(self)
864 jonathan 509 self.SetAutoLayout(True)
865 jonathan 511 self.SetSizer(topBox)
866 jonathan 509
867 jonathan 570 #self.Fit()
868 jonathan 549 ######################
869    
870     self.haveApplied = False
871    
872 jonathan 638 def EditSymbol(self, row):
873 jonathan 606 table = self.classGrid.GetTable()
874     prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
875    
876     # get a new ClassGroupProperties object and copy the
877     # values over to our current object
878     propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
879 jonathan 630
880     self.Enable(False)
881 jonathan 606 if propDlg.ShowModal() == wxID_OK:
882     new_prop = propDlg.GetClassGroupProperties()
883     table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
884 jonathan 630 self.Enable(True)
885 jonathan 606 propDlg.Destroy()
886    
887 jonathan 630 def _SetClassification(self, clazz):
888    
889     self.fields.SetClientData(self.__cur_field, clazz)
890     self.classGrid.GetTable().SetClassification(clazz)
891 jonathan 606
892 jonathan 615 def __BuildClassification(self, fieldIndex, copyClass = False):
893 jonathan 415
894 jonathan 615 # numRows = self.classGrid.GetNumberRows()
895     # assert numRows > 0 # there should always be a default row
896 jonathan 496
897 jonathan 615 # clazz = Classification()
898 jonathan 496 if fieldIndex == 0:
899     fieldName = None
900     fieldType = None
901     else:
902     fieldName = self.fields.GetString(fieldIndex)
903     fieldType = self.layer.GetFieldType(fieldName)
904 jonathan 415
905 jonathan 615 clazz = self.classGrid.GetTable().GetClassification()
906    
907     if copyClass:
908     clazz = copy.deepcopy(clazz)
909    
910 jonathan 460 clazz.SetField(fieldName)
911     clazz.SetFieldType(fieldType)
912    
913 jonathan 415
914 jonathan 615 # table = self.classGrid.GetTable()
915     # clazz.SetDefaultGroup(table.GetClassGroup(0))
916 jonathan 415
917 jonathan 615 # for i in range(1, numRows):
918     # clazz.AppendGroup(table.GetClassGroup(i))
919 jonathan 460
920 jonathan 415 return clazz
921    
922 jonathan 570 def __SetGridTable(self, fieldIndex, group = None):
923 jonathan 415
924 jonathan 460 clazz = self.fields.GetClientData(fieldIndex)
925 jonathan 415
926 jonathan 460 if clazz is None:
927     clazz = Classification()
928     clazz.SetDefaultGroup(
929     ClassGroupDefault(
930 jonathan 485 self.layer.GetClassification().
931     GetDefaultGroup().GetProperties()))
932 jonathan 460
933     fieldName = self.fields.GetString(fieldIndex)
934     fieldType = self.layer.GetFieldType(fieldName)
935     clazz.SetFieldType(fieldType)
936    
937 jonathan 570 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
938 jonathan 460
939 jonathan 485 def __SetFieldTypeText(self, fieldIndex):
940     fieldName = self.fields.GetString(fieldIndex)
941     fieldType = self.layer.GetFieldType(fieldName)
942    
943 jonathan 606 assert Classifier.type2string.has_key(fieldType)
944 jonathan 485
945 jonathan 496 text = Classifier.type2string[fieldType]
946    
947 jonathan 650 self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
948 jonathan 485
949 jonathan 570 def __SelectField(self, newIndex, oldIndex = -1, group = None):
950 jonathan 611 """This method assumes that the current selection for the
951     combo has already been set by a call to SetSelection().
952     """
953 jonathan 460
954 jonathan 606 assert oldIndex >= -1
955 jonathan 415
956 jonathan 498 if oldIndex != -1:
957     clazz = self.__BuildClassification(oldIndex)
958     self.fields.SetClientData(oldIndex, clazz)
959 jonathan 485
960 jonathan 570 self.__SetGridTable(newIndex, group)
961 jonathan 498
962 jonathan 650 self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
963 jonathan 498
964     self.__SetFieldTypeText(newIndex)
965 jonathan 485
966 jonathan 650 def __SetTitle(self, title):
967     if title != "":
968     title = ": " + title
969    
970     self.SetTitle(_("Layer Properties") + title)
971 jonathan 496
972 jonathan 638 def _OnEditSymbol(self, event):
973 jonathan 606 sel = self.classGrid.GetCurrentSelection()
974    
975     if len(sel) == 1:
976 jonathan 638 self.EditSymbol(sel[0])
977 jonathan 606
978 jonathan 496 def _OnFieldSelect(self, event):
979 jonathan 498 index = self.fields.GetSelection()
980     self.__SelectField(index, self.__cur_field)
981     self.__cur_field = index
982 jonathan 485
983 jonathan 638 def _OnTry(self, event):
984 jonathan 415 """Put the data from the table into a new Classification and hand
985     it to the layer.
986     """
987    
988 jonathan 460 clazz = self.fields.GetClientData(self.__cur_field)
989 jonathan 415
990     #
991     # only build the classification if there wasn't one to
992     # to begin with or it has been modified
993     #
994     if clazz is None or self.classGrid.GetTable().IsModified():
995 jonathan 615 clazz = self.__BuildClassification(self.__cur_field, True)
996 jonathan 415
997     self.layer.SetClassification(clazz)
998    
999 jonathan 549 self.haveApplied = True
1000    
1001 jonathan 485 def _OnOK(self, event):
1002 jonathan 638 self._OnTry(event)
1003 jonathan 615 self.Close()
1004 jonathan 415
1005 jonathan 615 def _OnCloseBtn(self, event):
1006     """Close is similar to Cancel except that any changes that were
1007     made and applied remain applied, but the currently displayed
1008     classification is discarded.
1009     """
1010    
1011     self.Close()
1012    
1013 jonathan 638 def _OnRevert(self, event):
1014 jonathan 485 """The layer's current classification stays the same."""
1015 jonathan 549 if self.haveApplied:
1016     self.layer.SetClassification(self.originalClass)
1017    
1018 jonathan 638 #self.Close()
1019 jonathan 415
1020 jonathan 460 def _OnAdd(self, event):
1021 jonathan 451 self.classGrid.AppendRows()
1022 jonathan 415
1023 jonathan 460 def _OnRemove(self, event):
1024 jonathan 451 self.classGrid.DeleteSelectedRows()
1025    
1026 jonathan 606 def _OnGenClass(self, event):
1027 jonathan 415
1028 jonathan 630 self.genDlg = ClassGenDialog(self, self.layer,
1029     self.fields.GetString(self.__cur_field))
1030 jonathan 606
1031 jonathan 630 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1032 jonathan 606
1033 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, False)
1034 jonathan 630
1035     self.genDlg.Show()
1036    
1037     def _OnGenDialogClose(self, event):
1038     self.genDlg.Destroy()
1039 jonathan 650 self.__EnableButtons(EB_GEN_CLASS, True)
1040 jonathan 630
1041 jonathan 460 def _OnMoveUp(self, event):
1042     sel = self.classGrid.GetCurrentSelection()
1043 jonathan 415
1044 jonathan 460 if len(sel) == 1:
1045     i = sel[0]
1046     if i > 1:
1047     table = self.classGrid.GetTable()
1048     x = table.GetClassGroup(i - 1)
1049     y = table.GetClassGroup(i)
1050     table.SetClassGroup(i - 1, y)
1051     table.SetClassGroup(i, x)
1052     self.classGrid.ClearSelection()
1053     self.classGrid.SelectRow(i - 1)
1054 jonathan 570 self.classGrid.MakeCellVisible(i - 1, 0)
1055 jonathan 460
1056     def _OnMoveDown(self, event):
1057     sel = self.classGrid.GetCurrentSelection()
1058    
1059     if len(sel) == 1:
1060     i = sel[0]
1061     table = self.classGrid.GetTable()
1062     if 0 < i < table.GetNumberRows() - 1:
1063     x = table.GetClassGroup(i)
1064     y = table.GetClassGroup(i + 1)
1065     table.SetClassGroup(i, y)
1066     table.SetClassGroup(i + 1, x)
1067     self.classGrid.ClearSelection()
1068     self.classGrid.SelectRow(i + 1)
1069 jonathan 570 self.classGrid.MakeCellVisible(i + 1, 0)
1070 jonathan 460
1071 jonathan 650 def _OnTitleChanged(self, event):
1072     obj = event.GetEventObject()
1073 jonathan 460
1074 jonathan 650 self.layer.SetTitle(obj.GetValue())
1075     self.__SetTitle(self.layer.Title())
1076    
1077     self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1078    
1079     def __EnableButtons(self, case, enable):
1080    
1081     if case == EB_LAYER_TITLE:
1082     list = (ID_PROPERTY_OK,
1083     ID_PROPERTY_CLOSE)
1084    
1085     elif case == EB_SELECT_FIELD:
1086     list = (ID_PROPERTY_GENCLASS,
1087     ID_PROPERTY_ADD,
1088     ID_PROPERTY_MOVEUP,
1089     ID_PROPERTY_MOVEDOWN,
1090     ID_PROPERTY_EDITSYM,
1091     ID_PROPERTY_REMOVE)
1092    
1093     elif case == EB_GEN_CLASS:
1094     list = (ID_PROPERTY_SELECT,
1095     ID_PROPERTY_FIELDTEXT,
1096     ID_PROPERTY_GENCLASS,
1097     ID_PROPERTY_EDITSYM)
1098    
1099     for id in list:
1100     self.FindWindowById(id).Enable(enable)
1101    
1102 jonathan 415 ID_SELPROP_OK = 4001
1103     ID_SELPROP_CANCEL = 4002
1104     ID_SELPROP_SPINCTRL = 4002
1105 jonathan 430 ID_SELPROP_PREVIEW = 4003
1106     ID_SELPROP_STROKECLR = 4004
1107     ID_SELPROP_FILLCLR = 4005
1108 jonathan 485 ID_SELPROP_STROKECLRTRANS = 4006
1109     ID_SELPROP_FILLCLRTRANS = 4007
1110 jonathan 415
1111     class SelectPropertiesDialog(wxDialog):
1112    
1113     def __init__(self, parent, prop, shapeType):
1114     wxDialog.__init__(self, parent, -1, _("Select Properties"),
1115 jonathan 507 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1116 jonathan 415
1117 jonathan 441 self.prop = ClassGroupProperties(prop)
1118 jonathan 415
1119 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
1120 jonathan 415
1121 jonathan 430 itemBox = wxBoxSizer(wxHORIZONTAL)
1122    
1123     # preview box
1124     previewBox = wxBoxSizer(wxVERTICAL)
1125     previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1126     0, wxALIGN_LEFT | wxALL, 4)
1127 jonathan 630
1128     self.previewWin = ClassGroupPropertiesCtrl(
1129     self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1130     (40, 40), wxSIMPLE_BORDER)
1131    
1132     self.previewWin.AllowEdit(False)
1133    
1134 jonathan 615 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1135 jonathan 430
1136     itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1137    
1138     # control box
1139     ctrlBox = wxBoxSizer(wxVERTICAL)
1140 jonathan 485
1141     lineColorBox = wxBoxSizer(wxHORIZONTAL)
1142     lineColorBox.Add(
1143 jonathan 500 wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1144 jonathan 485 1, wxALL | wxGROW, 4)
1145 jonathan 460 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1146 jonathan 430
1147 jonathan 485 lineColorBox.Add(
1148 jonathan 500 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1149 jonathan 485 1, wxALL | wxGROW, 4)
1150     EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1151     self._OnChangeLineColorTrans)
1152    
1153     ctrlBox.Add(lineColorBox, 0,
1154     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1155    
1156 jonathan 430 if shapeType != SHAPETYPE_ARC:
1157 jonathan 485 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1158     fillColorBox.Add(
1159 jonathan 500 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1160 jonathan 485 1, wxALL | wxGROW, 4)
1161 jonathan 460 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1162 jonathan 485 fillColorBox.Add(
1163 jonathan 500 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1164 jonathan 485 1, wxALL | wxGROW, 4)
1165     EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1166     self._OnChangeFillColorTrans)
1167     ctrlBox.Add(fillColorBox, 0,
1168     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1169 jonathan 430
1170 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
1171 jonathan 460 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1172 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1173 jonathan 415 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1174     min=1, max=10,
1175 jonathan 460 value=str(prop.GetLineWidth()),
1176     initial=prop.GetLineWidth())
1177 jonathan 415
1178 jonathan 460 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1179 jonathan 415
1180     spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1181    
1182 jonathan 430 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1183     itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1184     topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1185 jonathan 415
1186     #
1187     # Control buttons:
1188     #
1189     buttonBox = wxBoxSizer(wxHORIZONTAL)
1190 jonathan 485 buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1191 jonathan 415 0, wxALL, 4)
1192 jonathan 485 buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1193 jonathan 415 0, wxALL, 4)
1194     topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1195    
1196 jonathan 460 EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1197     EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1198 jonathan 415
1199 jonathan 509 self.SetAutoLayout(True)
1200 jonathan 415 self.SetSizer(topBox)
1201     topBox.Fit(self)
1202     topBox.SetSizeHints(self)
1203    
1204 jonathan 460 def _OnOK(self, event):
1205 jonathan 372 self.EndModal(wxID_OK)
1206    
1207 jonathan 460 def _OnCancel(self, event):
1208 jonathan 372 self.EndModal(wxID_CANCEL)
1209    
1210 jonathan 460 def _OnSpin(self, event):
1211     self.prop.SetLineWidth(self.spinCtrl.GetValue())
1212 jonathan 549 self.previewWin.Refresh()
1213 jonathan 392
1214 jonathan 430 def __GetColor(self, cur):
1215     dialog = wxColourDialog(self)
1216 jonathan 610 if cur is not Color.Transparent:
1217 jonathan 606 dialog.GetColourData().SetColour(Color2wxColour(cur))
1218    
1219 jonathan 430 ret = None
1220     if dialog.ShowModal() == wxID_OK:
1221     ret = wxColour2Color(dialog.GetColourData().GetColour())
1222    
1223     dialog.Destroy()
1224    
1225     return ret
1226    
1227 jonathan 460 def _OnChangeLineColor(self, event):
1228     clr = self.__GetColor(self.prop.GetLineColor())
1229 jonathan 430 if clr is not None:
1230 jonathan 460 self.prop.SetLineColor(clr)
1231 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1232 jonathan 430
1233 jonathan 485 def _OnChangeLineColorTrans(self, event):
1234 jonathan 610 self.prop.SetLineColor(Color.Transparent)
1235 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1236 jonathan 485
1237 jonathan 460 def _OnChangeFillColor(self, event):
1238 jonathan 430 clr = self.__GetColor(self.prop.GetFill())
1239     if clr is not None:
1240     self.prop.SetFill(clr)
1241 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1242 jonathan 430
1243 jonathan 485 def _OnChangeFillColorTrans(self, event):
1244 jonathan 610 self.prop.SetFill(Color.Transparent)
1245 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1246 jonathan 485
1247 jonathan 441 def GetClassGroupProperties(self):
1248 jonathan 415 return self.prop
1249 jonathan 392
1250    
1251 jonathan 549 class ClassDataPreviewWindow(wxWindow):
1252 jonathan 415
1253 jonathan 441 def __init__(self, rect, prop, shapeType,
1254 jonathan 430 parent = None, id = -1, size = wxDefaultSize):
1255     if parent is not None:
1256 jonathan 549 wxWindow.__init__(self, parent, id, (0, 0), size)
1257 jonathan 460 EVT_PAINT(self, self._OnPaint)
1258 jonathan 415
1259 jonathan 430 self.rect = rect
1260 jonathan 549
1261 jonathan 441 self.prop = prop
1262 jonathan 430 self.shapeType = shapeType
1263 jonathan 549 self.previewer = ClassDataPreviewer()
1264 jonathan 430
1265 jonathan 630 def GetProperties():
1266     return self.prop
1267    
1268 jonathan 460 def _OnPaint(self, event):
1269 jonathan 430 dc = wxPaintDC(self)
1270    
1271     # XXX: this doesn't seem to be having an effect:
1272     dc.DestroyClippingRegion()
1273    
1274 jonathan 549 if self.rect is None:
1275     w, h = self.GetSize()
1276     rect = wxRect(0, 0, w, h)
1277     else:
1278     rect = self.rect
1279 jonathan 430
1280 jonathan 549 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1281 jonathan 430
1282 jonathan 549 class ClassDataPreviewer:
1283 jonathan 430
1284 jonathan 549 def Draw(self, dc, rect, prop, shapeType):
1285    
1286 jonathan 606 assert dc is not None
1287     assert isinstance(prop, ClassGroupProperties)
1288 jonathan 549
1289 jonathan 430 if rect is None:
1290 jonathan 549 x = 0
1291     y = 0
1292     w, h = dc.GetSize()
1293 jonathan 430 else:
1294     x = rect.GetX()
1295     y = rect.GetY()
1296     w = rect.GetWidth()
1297     h = rect.GetHeight()
1298    
1299 jonathan 460 stroke = prop.GetLineColor()
1300 jonathan 610 if stroke is Color.Transparent:
1301 jonathan 392 pen = wxTRANSPARENT_PEN
1302     else:
1303 jonathan 430 pen = wxPen(Color2wxColour(stroke),
1304 jonathan 460 prop.GetLineWidth(),
1305 jonathan 392 wxSOLID)
1306    
1307 jonathan 441 stroke = prop.GetFill()
1308 jonathan 610 if stroke is Color.Transparent:
1309 jonathan 392 brush = wxTRANSPARENT_BRUSH
1310     else:
1311 jonathan 430 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1312 jonathan 392
1313     dc.SetPen(pen)
1314     dc.SetBrush(brush)
1315    
1316 jonathan 415 if shapeType == SHAPETYPE_ARC:
1317 jonathan 430 dc.DrawSpline([wxPoint(x, y + h),
1318     wxPoint(x + w/2, y + h/4),
1319     wxPoint(x + w/2, y + h/4*3),
1320     wxPoint(x + w, y)])
1321 jonathan 392
1322 jonathan 576 elif shapeType == SHAPETYPE_POINT:
1323 jonathan 415
1324 jonathan 430 dc.DrawCircle(x + w/2, y + h/2,
1325 jonathan 460 (min(w, h) - prop.GetLineWidth())/2)
1326 jonathan 392
1327 jonathan 576 elif shapeType == SHAPETYPE_POLYGON:
1328     dc.DrawRectangle(x, y, w, h)
1329    
1330 jonathan 415 class ClassRenderer(wxPyGridCellRenderer):
1331    
1332     def __init__(self, shapeType):
1333     wxPyGridCellRenderer.__init__(self)
1334 jonathan 549 self.shapeType = shapeType
1335     self.previewer = ClassDataPreviewer()
1336 jonathan 415
1337     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1338 jonathan 485 data = grid.GetTable().GetClassGroup(row)
1339 jonathan 415
1340     dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1341     rect.GetWidth(), rect.GetHeight())
1342     dc.SetPen(wxPen(wxLIGHT_GREY))
1343     dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1344     dc.DrawRectangle(rect.GetX(), rect.GetY(),
1345     rect.GetWidth(), rect.GetHeight())
1346    
1347 jonathan 441 if not isinstance(data, ClassGroupMap):
1348 jonathan 549 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1349 jonathan 415
1350     if isSelected:
1351 jonathan 615 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1352 jonathan 415 dc.SetBrush(wxTRANSPARENT_BRUSH)
1353 jonathan 615
1354 jonathan 415 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1355     rect.GetWidth(), rect.GetHeight())
1356    
1357 jonathan 392 dc.DestroyClippingRegion()
1358    
1359 jonathan 630
1360     class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1361    
1362     def __init__(self, parent, id, props, shapeType,
1363     size = wxDefaultSize, style = 0):
1364    
1365     wxWindow.__init__(self, parent, id, size = size, style = style)
1366    
1367     self.SetProperties(props)
1368     self.SetShapeType(shapeType)
1369     self.AllowEdit(True)
1370    
1371     EVT_PAINT(self, self._OnPaint)
1372     EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1373    
1374     self.previewer = ClassDataPreviewer()
1375    
1376     def _OnPaint(self, event):
1377     dc = wxPaintDC(self)
1378    
1379     # XXX: this doesn't seem to be having an effect:
1380     dc.DestroyClippingRegion()
1381    
1382     w, h = self.GetClientSize()
1383    
1384     self.previewer.Draw(dc,
1385     wxRect(0, 0, w, h),
1386     self.GetProperties(),
1387     self.GetShapeType())
1388    
1389    
1390     def GetProperties(self):
1391     return self.props
1392    
1393     def SetProperties(self, props):
1394     self.props = props
1395     self.Refresh()
1396    
1397     def GetShapeType(self):
1398     return self.shapeType
1399    
1400     def SetShapeType(self, shapeType):
1401     self.shapeType = shapeType
1402     self.Refresh()
1403    
1404     def AllowEdit(self, allow):
1405     self.allowEdit = allow
1406    
1407     def DoEdit(self):
1408     if not self.allowEdit: return
1409    
1410     propDlg = SelectPropertiesDialog(NULL,
1411     self.GetProperties(),
1412     self.GetShapeType())
1413    
1414     if propDlg.ShowModal() == wxID_OK:
1415     new_prop = propDlg.GetClassGroupProperties()
1416     self.SetProperties(new_prop)
1417     self.Refresh()
1418    
1419     propDlg.Destroy()
1420    
1421     def _OnLeftDClick(self, event):
1422     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