/[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 638 - (hide annotations)
Thu Apr 10 14:35:20 2003 UTC (21 years, 10 months ago) by jonathan
File MIME type: text/x-python
File size: 44305 byte(s)
(ClassTable): Add a new column for the "Visible" check boxes.
(Classifier): Rename the buttons and refactor the code to match the new labels.

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 415 from Thuban.common import *
22 jonathan 441 from Thuban.UI.common import *
23 jan 374
24 jonathan 451 from Thuban.Model.classification import *
25 jonathan 392
26 jonathan 415 from Thuban.Model.color import Color
27    
28 jonathan 460 from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
29 jonathan 392
30 jonathan 606 from Thuban.UI.classgen import ClassGenDialog, ClassGenerator
31    
32 jonathan 485 from dialogs import NonModalDialog
33    
34 jonathan 460 # widget id's
35 jonathan 372 ID_PROPERTY_SELECT = 4010
36     ID_CLASS_TABLE = 40011
37    
38    
39 jonathan 460 # table columns
40 jonathan 638 COL_VISIBLE = 0
41     COL_SYMBOL = 1
42     COL_VALUE = 2
43     COL_LABEL = 3
44     NUM_COLS = 4
45 jonathan 415
46 jonathan 460 # indices into the client data lists in Classifier.fields
47 jonathan 451 FIELD_CLASS = 0
48     FIELD_TYPE = 1
49     FIELD_NAME = 2
50    
51 jonathan 415 #
52     # this is a silly work around to ensure that the table that is
53     # passed into SetTable is the same that is returned by GetTable
54     #
55     import weakref
56     class ClassGrid(wxGrid):
57    
58 jonathan 570
59 jonathan 606 def __init__(self, parent, classifier):
60 jonathan 460 """Constructor.
61    
62     parent -- the parent window
63    
64     clazz -- the working classification that this grid should
65     use for display.
66     """
67    
68 jonathan 485 wxGrid.__init__(self, parent, ID_CLASS_TABLE)
69 jonathan 415
70 jonathan 606 self.classifier = classifier
71    
72 jonathan 570 self.currentSelection = []
73    
74 jonathan 460 EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
75 jonathan 451 EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
76     EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
77 jonathan 606 EVT_GRID_COL_SIZE(self, self._OnCellResize)
78     EVT_GRID_ROW_SIZE(self, self._OnCellResize)
79 jonathan 451
80 jonathan 638 #def GetCellAttr(self, row, col):
81     #print "GetCellAttr ", row, col
82     #wxGrid.GetCellAttr(self, row, col)
83    
84 jonathan 570 def CreateTable(self, clazz, shapeType, group = None):
85    
86 jonathan 606 assert isinstance(clazz, Classification)
87 jonathan 460
88     table = self.GetTable()
89     if table is None:
90 jonathan 500 w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()
91     h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()
92     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 jonathan 460 return _("")
401    
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     conv = lambda p: int(float(p))
481     else:
482     conv = lambda p: p
483    
484 jonathan 451 #
485     # first try to take the input as a single number
486     # if there's an exception try to break it into
487     # a range seperated by a '-'. take care to ignore
488     # a leading '-' as that could be for a negative number.
489     # then try to parse the individual parts. if there
490     # is an exception here, let it pass up to the calling
491     # function.
492     #
493     try:
494 jonathan 460 return (conv(Str2Num(value)),)
495     except ValueError:
496 jonathan 451 i = value.find('-')
497     if i == 0:
498     i = value.find('-', 1)
499    
500 jonathan 460 return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
501    
502 jonathan 606 assert False # shouldn't get here
503 jonathan 485 return (0,)
504 jonathan 415
505    
506     def SetValueAsCustom(self, row, col, typeName, value):
507 jonathan 460 """Set the cell specified by 'row' and 'col' to 'value'.
508 jonathan 415
509 jonathan 460 If column represents the value column, the input is parsed
510     to determine if a string, number, or range was entered.
511     A new ClassGroup may be created if the type of data changes.
512    
513     The table is considered modified after this operation.
514    
515     typeName -- unused, but needed to overload wxPyGridTableBase
516     """
517    
518 jonathan 606 assert col >= 0 and col < self.GetNumberCols()
519     assert row >= 0 and row < self.GetNumberRows()
520 jonathan 460
521 jonathan 615 if row == 0:
522     group = self.clazz.GetDefaultGroup()
523     else:
524     group = self.clazz.GetGroup(row - 1)
525 jonathan 460
526 jonathan 485 mod = True # assume the data will change
527 jonathan 460
528 jonathan 638 if col == COL_VISIBLE:
529     group.SetVisible(value)
530     elif col == COL_SYMBOL:
531 jonathan 485 group.SetProperties(value)
532     elif col == COL_LABEL:
533     group.SetLabel(value)
534 jonathan 415 elif col == COL_VALUE:
535 jonathan 451 if isinstance(group, ClassGroupDefault):
536     # not allowed to modify the default value
537     pass
538     elif isinstance(group, ClassGroupMap):
539     # something special
540     pass
541     else: # SINGLETON, RANGE
542     try:
543     dataInfo = self.__ParseInput(value)
544 jonathan 460 except ValueError:
545 jonathan 451 # bad input, ignore the request
546 jonathan 485 mod = False
547 jonathan 451 else:
548 jonathan 415
549 jonathan 485 changed = False
550 jonathan 451 ngroup = group
551     props = group.GetProperties()
552 jonathan 460
553     #
554     # try to update the values, which may include
555     # changing the underlying group type if the
556     # group was a singleton and a range was entered
557     #
558 jonathan 451 if len(dataInfo) == 1:
559     if not isinstance(group, ClassGroupSingleton):
560     ngroup = ClassGroupSingleton(prop = props)
561 jonathan 485 changed = True
562 jonathan 451 ngroup.SetValue(dataInfo[0])
563     elif len(dataInfo) == 2:
564     if not isinstance(group, ClassGroupRange):
565     ngroup = ClassGroupRange(prop = props)
566 jonathan 485 changed = True
567 jonathan 451 ngroup.SetRange(dataInfo[0], dataInfo[1])
568 jonathan 415 else:
569 jonathan 606 assert False
570 jonathan 485 pass
571 jonathan 415
572 jonathan 485 if changed:
573     ngroup.SetLabel(group.GetLabel())
574     self.SetClassGroup(row, ngroup)
575     else:
576 jonathan 606 assert False # shouldn't be here
577 jonathan 485 pass
578 jonathan 460
579 jonathan 485 if mod:
580 jonathan 460 self.__Modified()
581     self.GetView().Refresh()
582 jonathan 415
583     def GetAttr(self, row, col, someExtraParameter):
584 jonathan 460 """Returns the cell attributes"""
585    
586 jonathan 638 return self.__colAttr.get(col, wxGridCellAttr()).Clone()
587 jonathan 415
588 jonathan 441 def GetClassGroup(self, row):
589 jonathan 460 """Return the ClassGroup object representing row 'row'."""
590 jonathan 415
591 jonathan 615 #return self.GetValueAsCustom(row, COL_SYMBOL, None)
592     if row == 0:
593     return self.clazz.GetDefaultGroup()
594     else:
595     return self.clazz.GetGroup(row - 1)
596 jonathan 415
597 jonathan 460 def SetClassGroup(self, row, group):
598 jonathan 485 self.__SetRow(row, group)
599     self.GetView().Refresh()
600 jonathan 460
601     def __Modified(self, mod = True):
602 jonathan 485 """Adjust the modified flag.
603 jonathan 460
604 jonathan 485 mod -- if -1 set the modified flag to False, otherwise perform
605     an 'or' operation with the current value of the flag and
606     'mod'
607     """
608    
609     if mod == -1:
610     self.modified = False
611     else:
612     self.modified = mod or self.modified
613    
614 jonathan 415 def IsModified(self):
615 jonathan 460 """True if this table is considered modified."""
616 jonathan 415 return self.modified
617    
618 jonathan 451 def DeleteRows(self, pos, numRows = 1):
619 jonathan 485 """Deletes 'numRows' beginning at row 'pos'.
620 jonathan 460
621 jonathan 485 The row representing the default group is not removed.
622    
623     The table is considered modified if any rows are removed.
624 jonathan 460 """
625    
626 jonathan 606 assert pos >= 0
627 jonathan 615 old_len = self.GetNumberRows()
628 jonathan 451 for row in range(pos, pos - numRows, -1):
629 jonathan 485 group = self.GetClassGroup(row)
630 jonathan 615 if row != 0:
631     self.clazz.RemoveGroup(row - 1)
632 jonathan 451 self.__Modified()
633    
634     if self.IsModified():
635 jonathan 615 self.__NotifyRowChanges(old_len, self.GetNumberRows())
636 jonathan 415
637 jonathan 451 def AppendRows(self, numRows = 1):
638 jonathan 485 """Append 'numRows' empty rows to the end of the table.
639 jonathan 460
640 jonathan 485 The table is considered modified if any rows are appended.
641     """
642    
643 jonathan 615 old_len = self.GetNumberRows()
644 jonathan 451 for i in range(numRows):
645     np = ClassGroupSingleton()
646 jonathan 606 self.__SetRow(None, np)
647 jonathan 451
648     if self.IsModified():
649 jonathan 615 self.__NotifyRowChanges(old_len, self.GetNumberRows())
650 jonathan 451
651    
652 jonathan 630 ID_CLASSIFY_OK = 4001
653 jonathan 638 ID_CLASSIFY_REVERT = 4002
654 jonathan 630 ID_CLASSIFY_ADD = 4003
655     ID_CLASSIFY_GENCLASS = 4004
656     ID_CLASSIFY_REMOVE = 4005
657     ID_CLASSIFY_MOVEUP = 4006
658     ID_CLASSIFY_MOVEDOWN = 4007
659 jonathan 638 ID_CLASSIFY_TRY = 4008
660     ID_CLASSIFY_EDITSYM = 4009
661 jonathan 630 ID_CLASSIFY_CLOSE = 4010
662    
663     BTN_ADD = 0
664     BTN_EDIT = 1
665     BTN_GEN = 2
666     BTN_UP = 3
667     BTN_DOWN = 4
668     BTN_RM = 5
669    
670 jonathan 485 class Classifier(NonModalDialog):
671 bh 535
672 jonathan 630 type2string = {None: _("None"),
673     FIELDTYPE_STRING: _("Text"),
674     FIELDTYPE_INT: _("Integer"),
675     FIELDTYPE_DOUBLE: _("Decimal")}
676    
677 jonathan 570 def __init__(self, parent, name, layer, group = None):
678 bh 535 NonModalDialog.__init__(self, parent, name,
679 jonathan 485 _("Classifier: %s") % layer.Title())
680 jonathan 372
681 jonathan 511 panel = wxPanel(self, -1, size=(100, 100))
682 jonathan 509
683 jonathan 415 self.layer = layer
684    
685 jonathan 485 self.originalClass = self.layer.GetClassification()
686     field = self.originalClass.GetField()
687     fieldType = self.originalClass.GetFieldType()
688    
689 jonathan 630 self.genDlg = None
690    
691 jonathan 372 topBox = wxBoxSizer(wxVERTICAL)
692 jonathan 511 panelBox = wxBoxSizer(wxVERTICAL)
693 jonathan 372
694 jonathan 511 #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),
695 jonathan 485 #0, wxALIGN_LEFT | wxALL, 4)
696 jonathan 511 panelBox.Add(wxStaticText(panel, -1,
697 jonathan 485 _("Layer Type: %s") % layer.ShapeType()),
698 jonathan 451 0, wxALIGN_LEFT | wxALL, 4)
699 jonathan 415
700 jonathan 372
701 jonathan 485 #
702     # make field combo box
703     #
704 jonathan 509 self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
705 jonathan 372 style = wxCB_READONLY)
706    
707     self.num_cols = layer.table.field_count()
708 jonathan 441 # just assume the first field in case one hasn't been
709     # specified in the file.
710 jonathan 451 self.__cur_field = 0
711 jonathan 460
712     self.fields.Append("<None>")
713     self.fields.SetClientData(0, None)
714    
715 jonathan 372 for i in range(self.num_cols):
716     type, name, len, decc = layer.table.field_info(i)
717 jonathan 451 self.fields.Append(name)
718    
719 jonathan 415 if name == field:
720 jonathan 460 self.__cur_field = i + 1
721 jonathan 615 self.fields.SetClientData(i + 1,
722     copy.deepcopy(self.originalClass))
723 jonathan 451 else:
724 jonathan 460 self.fields.SetClientData(i + 1, None)
725 jonathan 372
726    
727 jonathan 509 ###########
728 jonathan 485
729 jonathan 509 self.fieldTypeText = wxStaticText(panel, -1, "")
730 jonathan 560 panelBox.Add(self.fieldTypeText, 0,
731     wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
732 jonathan 485
733     propertyBox = wxBoxSizer(wxHORIZONTAL)
734 jonathan 509 propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),
735 jonathan 506 0, wxALIGN_LEFT | wxALL, 4)
736 jonathan 451 propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
737 jonathan 460 EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
738 jonathan 451
739 jonathan 511 panelBox.Add(propertyBox, 0, wxGROW, 4)
740 jonathan 372
741 jonathan 570
742 jonathan 372 #
743 jonathan 570 # Control Box
744 jonathan 372 #
745 jonathan 415 controlBox = wxBoxSizer(wxHORIZONTAL)
746 jonathan 485
747 jonathan 460
748 jonathan 509 ###########
749 jonathan 485 #
750     # Control buttons:
751     #
752     self.controlButtons = []
753 jonathan 460
754 jonathan 509 controlButtonBox = wxBoxSizer(wxVERTICAL)
755    
756 jonathan 638 button = wxButton(panel, ID_CLASSIFY_GENCLASS, _("Generate Class"))
757 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
758     self.controlButtons.append(button)
759 jonathan 451
760 jonathan 638 button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))
761 jonathan 570 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
762     self.controlButtons.append(button)
763 jonathan 485
764 jonathan 638 button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))
765 jonathan 606 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
766     self.controlButtons.append(button)
767    
768 jonathan 638 button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))
769 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
770     self.controlButtons.append(button)
771    
772 jonathan 638 button = wxButton(panel, ID_CLASSIFY_EDITSYM, _("Edit Symbol"))
773 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
774     self.controlButtons.append(button)
775    
776     controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
777    
778 jonathan 509 button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))
779 jonathan 485 controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
780     self.controlButtons.append(button)
781    
782 jonathan 570
783     ###########
784     #
785     # Classification data table
786     #
787    
788 jonathan 606 self.classGrid = ClassGrid(panel, self)
789 jonathan 638 # self.classGrid = wxGrid(panel, -1)
790     # self.classGrid.SetDefaultRenderer(wxGridCellBoolRenderer())
791     # self.classGrid.SetDefaultEditor(wxGridCellBoolEditor())
792     #self.classGrid.CreateGrid(5, 5)
793     #self.classGrid.SetCellEditor(0, 0, wxGridCellBoolEditor())
794     #self.classGrid.SetCellRenderer(0, 0, wxGridCellBoolRenderer())
795     #print self.classGrid.GetCellEditor(0, 0)
796     #print self.classGrid.GetCellRenderer(0, 0)
797     #self.classGrid = ClassGrid(panel, self)
798 jonathan 570
799 jonathan 638 # need these
800     self.__SetGridTable(self.__cur_field, group)
801     self.fields.SetSelection(self.__cur_field)
802     #self.classGrid.SetCellEditor(0, 0, wxGridCellBoolEditor())
803     #self.classGrid.SetCellRenderer(0, 0, wxGridCellBoolRenderer())
804    
805 jonathan 570 # calling __SelectField after creating the classGrid fills in the
806     # grid with the correct information
807 jonathan 638 #self.fields.SetSelection(self.__cur_field)
808     #self.__SelectField(self.__cur_field, group = group)
809 jonathan 570
810     #self.classGrid.SelectGroup(group)
811    
812     controlBox.Add(self.classGrid, 1, wxGROW, 0)
813    
814    
815    
816 jonathan 415 controlBox.Add(controlButtonBox, 0, wxGROW, 10)
817 jonathan 511 panelBox.Add(controlBox, 1, wxGROW, 10)
818 jonathan 415
819 jonathan 460 EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)
820 jonathan 638 EVT_BUTTON(self, ID_CLASSIFY_EDITSYM, self._OnEditSymbol)
821 jonathan 460 EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)
822 jonathan 606 EVT_BUTTON(self, ID_CLASSIFY_GENCLASS, self._OnGenClass)
823 jonathan 460 EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)
824     EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)
825 jonathan 415
826 jonathan 509 ###########
827    
828 jonathan 372 buttonBox = wxBoxSizer(wxHORIZONTAL)
829 jonathan 638 buttonBox.Add(wxButton(panel, ID_CLASSIFY_TRY, _("Try")),
830 jonathan 372 0, wxALL, 4)
831 jonathan 485 buttonBox.Add(60, 20, 0, wxALL, 4)
832 jonathan 638 buttonBox.Add(wxButton(panel, ID_CLASSIFY_REVERT, _("Revert")),
833 jonathan 485 0, wxALL, 4)
834     buttonBox.Add(60, 20, 0, wxALL, 4)
835 jonathan 638 buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),
836 jonathan 615 0, wxALL, 4)
837     buttonBox.Add(60, 20, 0, wxALL, 4)
838 jonathan 638 buttonBox.Add(wxButton(panel, ID_CLASSIFY_CLOSE, _("Close")),
839 jonathan 372 0, wxALL, 4)
840 jonathan 511 panelBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
841 jonathan 372
842 jonathan 460 EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)
843 jonathan 638 EVT_BUTTON(self, ID_CLASSIFY_TRY, self._OnTry)
844 jonathan 615 EVT_BUTTON(self, ID_CLASSIFY_CLOSE, self._OnCloseBtn)
845 jonathan 638 EVT_BUTTON(self, ID_CLASSIFY_REVERT, self._OnRevert)
846 jonathan 372
847 jonathan 509 ###########
848    
849 jonathan 496
850 jonathan 509 panel.SetAutoLayout(True)
851 jonathan 511 panel.SetSizer(panelBox)
852     panelBox.SetSizeHints(panel)
853 jonathan 372
854 jonathan 606 topBox.Add(panel, 1, wxGROW, 0)
855 jonathan 511 panelBox.SetSizeHints(self)
856 jonathan 509 self.SetAutoLayout(True)
857 jonathan 511 self.SetSizer(topBox)
858 jonathan 509
859 jonathan 570 #self.Fit()
860 jonathan 549 ######################
861    
862     self.haveApplied = False
863    
864 jonathan 638 def EditSymbol(self, row):
865 jonathan 606 table = self.classGrid.GetTable()
866     prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
867    
868     # get a new ClassGroupProperties object and copy the
869     # values over to our current object
870     propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
871 jonathan 630
872     self.Enable(False)
873 jonathan 606 if propDlg.ShowModal() == wxID_OK:
874     new_prop = propDlg.GetClassGroupProperties()
875     table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
876 jonathan 630 self.Enable(True)
877 jonathan 606 propDlg.Destroy()
878    
879 jonathan 630 def _SetClassification(self, clazz):
880    
881     self.fields.SetClientData(self.__cur_field, clazz)
882     self.classGrid.GetTable().SetClassification(clazz)
883 jonathan 606
884 jonathan 615 def __BuildClassification(self, fieldIndex, copyClass = False):
885 jonathan 415
886 jonathan 615 # numRows = self.classGrid.GetNumberRows()
887     # assert numRows > 0 # there should always be a default row
888 jonathan 496
889 jonathan 615 # clazz = Classification()
890 jonathan 496 if fieldIndex == 0:
891     fieldName = None
892     fieldType = None
893     else:
894     fieldName = self.fields.GetString(fieldIndex)
895     fieldType = self.layer.GetFieldType(fieldName)
896 jonathan 415
897 jonathan 615 clazz = self.classGrid.GetTable().GetClassification()
898    
899     if copyClass:
900     clazz = copy.deepcopy(clazz)
901    
902 jonathan 460 clazz.SetField(fieldName)
903     clazz.SetFieldType(fieldType)
904    
905 jonathan 415
906 jonathan 615 # table = self.classGrid.GetTable()
907     # clazz.SetDefaultGroup(table.GetClassGroup(0))
908 jonathan 415
909 jonathan 615 # for i in range(1, numRows):
910     # clazz.AppendGroup(table.GetClassGroup(i))
911 jonathan 460
912 jonathan 415 return clazz
913    
914 jonathan 570 def __SetGridTable(self, fieldIndex, group = None):
915 jonathan 415
916 jonathan 460 clazz = self.fields.GetClientData(fieldIndex)
917 jonathan 415
918 jonathan 460 if clazz is None:
919     clazz = Classification()
920     clazz.SetDefaultGroup(
921     ClassGroupDefault(
922 jonathan 485 self.layer.GetClassification().
923     GetDefaultGroup().GetProperties()))
924 jonathan 460
925     fieldName = self.fields.GetString(fieldIndex)
926     fieldType = self.layer.GetFieldType(fieldName)
927     clazz.SetFieldType(fieldType)
928    
929 jonathan 570 self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
930 jonathan 460
931 jonathan 485 def __SetFieldTypeText(self, fieldIndex):
932     fieldName = self.fields.GetString(fieldIndex)
933     fieldType = self.layer.GetFieldType(fieldName)
934    
935 jonathan 606 assert Classifier.type2string.has_key(fieldType)
936 jonathan 485
937 jonathan 496 text = Classifier.type2string[fieldType]
938    
939 jonathan 485 self.fieldTypeText.SetLabel(_("Field Type: %s") % text)
940    
941 jonathan 570 def __SelectField(self, newIndex, oldIndex = -1, group = None):
942 jonathan 611 """This method assumes that the current selection for the
943     combo has already been set by a call to SetSelection().
944     """
945 jonathan 460
946 jonathan 606 assert oldIndex >= -1
947 jonathan 415
948 jonathan 498 if oldIndex != -1:
949     clazz = self.__BuildClassification(oldIndex)
950     self.fields.SetClientData(oldIndex, clazz)
951 jonathan 485
952 jonathan 570 self.__SetGridTable(newIndex, group)
953 jonathan 498
954     enabled = newIndex != 0
955    
956 jonathan 485 for b in self.controlButtons:
957     b.Enable(enabled)
958    
959 jonathan 498 self.__SetFieldTypeText(newIndex)
960 jonathan 485
961 jonathan 496
962 jonathan 638 def _OnEditSymbol(self, event):
963 jonathan 606 sel = self.classGrid.GetCurrentSelection()
964    
965     if len(sel) == 1:
966 jonathan 638 self.EditSymbol(sel[0])
967 jonathan 606
968 jonathan 496 def _OnFieldSelect(self, event):
969 jonathan 498 index = self.fields.GetSelection()
970     self.__SelectField(index, self.__cur_field)
971     self.__cur_field = index
972 jonathan 485
973 jonathan 638 def _OnTry(self, event):
974 jonathan 415 """Put the data from the table into a new Classification and hand
975     it to the layer.
976     """
977    
978 jonathan 460 clazz = self.fields.GetClientData(self.__cur_field)
979 jonathan 415
980     #
981     # only build the classification if there wasn't one to
982     # to begin with or it has been modified
983     #
984     if clazz is None or self.classGrid.GetTable().IsModified():
985 jonathan 615 clazz = self.__BuildClassification(self.__cur_field, True)
986 jonathan 415
987     self.layer.SetClassification(clazz)
988    
989 jonathan 549 self.haveApplied = True
990    
991 jonathan 485 def _OnOK(self, event):
992 jonathan 638 self._OnTry(event)
993 jonathan 615 self.Close()
994 jonathan 415
995 jonathan 615 def _OnCloseBtn(self, event):
996     """Close is similar to Cancel except that any changes that were
997     made and applied remain applied, but the currently displayed
998     classification is discarded.
999     """
1000    
1001     self.Close()
1002    
1003 jonathan 638 def _OnRevert(self, event):
1004 jonathan 485 """The layer's current classification stays the same."""
1005 jonathan 549 if self.haveApplied:
1006     self.layer.SetClassification(self.originalClass)
1007    
1008 jonathan 638 #self.Close()
1009 jonathan 415
1010 jonathan 460 def _OnAdd(self, event):
1011 jonathan 451 self.classGrid.AppendRows()
1012 jonathan 415
1013 jonathan 460 def _OnRemove(self, event):
1014 jonathan 451 self.classGrid.DeleteSelectedRows()
1015    
1016 jonathan 606 def _OnGenClass(self, event):
1017 jonathan 415
1018 jonathan 630 #if self.genDlg is None:
1019     self.genDlg = ClassGenDialog(self, self.layer,
1020     self.fields.GetString(self.__cur_field))
1021 jonathan 606
1022 jonathan 630 EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1023 jonathan 606
1024 jonathan 630 self.fields.Enable(False)
1025     self.controlButtons[BTN_EDIT].Enable(False)
1026     self.controlButtons[BTN_GEN].Enable(False)
1027    
1028     self.genDlg.Show()
1029     #if self.genDlg.ShowModal() == wxID_OK:
1030     # clazz = self.genDlg.GetClassification()
1031     # self.fields.SetClientData(self.__cur_field, clazz)
1032     # self.classGrid.GetTable().SetClassification(clazz)
1033     #self.Enable(True)
1034     #self.genDlg.Destroy()
1035    
1036     def _OnGenDialogClose(self, event):
1037     self.genDlg.Destroy()
1038    
1039     self.fields.Enable(True)
1040     self.controlButtons[BTN_EDIT].Enable(True)
1041     self.controlButtons[BTN_GEN].Enable(True)
1042    
1043 jonathan 460 def _OnMoveUp(self, event):
1044     sel = self.classGrid.GetCurrentSelection()
1045 jonathan 415
1046 jonathan 460 if len(sel) == 1:
1047     i = sel[0]
1048     if i > 1:
1049     table = self.classGrid.GetTable()
1050     x = table.GetClassGroup(i - 1)
1051     y = table.GetClassGroup(i)
1052     table.SetClassGroup(i - 1, y)
1053     table.SetClassGroup(i, x)
1054     self.classGrid.ClearSelection()
1055     self.classGrid.SelectRow(i - 1)
1056 jonathan 570 self.classGrid.MakeCellVisible(i - 1, 0)
1057 jonathan 460
1058     def _OnMoveDown(self, event):
1059     sel = self.classGrid.GetCurrentSelection()
1060    
1061     if len(sel) == 1:
1062     i = sel[0]
1063     table = self.classGrid.GetTable()
1064     if 0 < i < table.GetNumberRows() - 1:
1065     x = table.GetClassGroup(i)
1066     y = table.GetClassGroup(i + 1)
1067     table.SetClassGroup(i, y)
1068     table.SetClassGroup(i + 1, x)
1069     self.classGrid.ClearSelection()
1070     self.classGrid.SelectRow(i + 1)
1071 jonathan 570 self.classGrid.MakeCellVisible(i + 1, 0)
1072 jonathan 460
1073    
1074 jonathan 415 ID_SELPROP_OK = 4001
1075     ID_SELPROP_CANCEL = 4002
1076     ID_SELPROP_SPINCTRL = 4002
1077 jonathan 430 ID_SELPROP_PREVIEW = 4003
1078     ID_SELPROP_STROKECLR = 4004
1079     ID_SELPROP_FILLCLR = 4005
1080 jonathan 485 ID_SELPROP_STROKECLRTRANS = 4006
1081     ID_SELPROP_FILLCLRTRANS = 4007
1082 jonathan 415
1083     class SelectPropertiesDialog(wxDialog):
1084    
1085     def __init__(self, parent, prop, shapeType):
1086     wxDialog.__init__(self, parent, -1, _("Select Properties"),
1087 jonathan 507 style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1088 jonathan 415
1089 jonathan 441 self.prop = ClassGroupProperties(prop)
1090 jonathan 415
1091 jonathan 430 topBox = wxBoxSizer(wxVERTICAL)
1092 jonathan 415
1093 jonathan 430 itemBox = wxBoxSizer(wxHORIZONTAL)
1094    
1095     # preview box
1096     previewBox = wxBoxSizer(wxVERTICAL)
1097     previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1098     0, wxALIGN_LEFT | wxALL, 4)
1099 jonathan 630
1100     self.previewWin = ClassGroupPropertiesCtrl(
1101     self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1102     (40, 40), wxSIMPLE_BORDER)
1103    
1104     self.previewWin.AllowEdit(False)
1105    
1106 jonathan 615 previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1107 jonathan 430
1108     itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1109    
1110     # control box
1111     ctrlBox = wxBoxSizer(wxVERTICAL)
1112 jonathan 485
1113     lineColorBox = wxBoxSizer(wxHORIZONTAL)
1114     lineColorBox.Add(
1115 jonathan 500 wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1116 jonathan 485 1, wxALL | wxGROW, 4)
1117 jonathan 460 EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1118 jonathan 430
1119 jonathan 485 lineColorBox.Add(
1120 jonathan 500 wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1121 jonathan 485 1, wxALL | wxGROW, 4)
1122     EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1123     self._OnChangeLineColorTrans)
1124    
1125     ctrlBox.Add(lineColorBox, 0,
1126     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1127    
1128 jonathan 430 if shapeType != SHAPETYPE_ARC:
1129 jonathan 485 fillColorBox = wxBoxSizer(wxHORIZONTAL)
1130     fillColorBox.Add(
1131 jonathan 500 wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1132 jonathan 485 1, wxALL | wxGROW, 4)
1133 jonathan 460 EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1134 jonathan 485 fillColorBox.Add(
1135 jonathan 500 wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1136 jonathan 485 1, wxALL | wxGROW, 4)
1137     EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1138     self._OnChangeFillColorTrans)
1139     ctrlBox.Add(fillColorBox, 0,
1140     wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1141 jonathan 430
1142 jonathan 415 spinBox = wxBoxSizer(wxHORIZONTAL)
1143 jonathan 460 spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1144 jonathan 430 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1145 jonathan 415 self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1146     min=1, max=10,
1147 jonathan 460 value=str(prop.GetLineWidth()),
1148     initial=prop.GetLineWidth())
1149 jonathan 415
1150 jonathan 460 EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1151 jonathan 415
1152     spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1153    
1154 jonathan 430 ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1155     itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1156     topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1157 jonathan 415
1158     #
1159     # Control buttons:
1160     #
1161     buttonBox = wxBoxSizer(wxHORIZONTAL)
1162 jonathan 485 buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1163 jonathan 415 0, wxALL, 4)
1164 jonathan 485 buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1165 jonathan 415 0, wxALL, 4)
1166     topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1167    
1168 jonathan 460 EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1169     EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1170 jonathan 415
1171 jonathan 509 self.SetAutoLayout(True)
1172 jonathan 415 self.SetSizer(topBox)
1173     topBox.Fit(self)
1174     topBox.SetSizeHints(self)
1175    
1176 jonathan 460 def _OnOK(self, event):
1177 jonathan 372 self.EndModal(wxID_OK)
1178    
1179 jonathan 460 def _OnCancel(self, event):
1180 jonathan 372 self.EndModal(wxID_CANCEL)
1181    
1182 jonathan 460 def _OnSpin(self, event):
1183     self.prop.SetLineWidth(self.spinCtrl.GetValue())
1184 jonathan 549 self.previewWin.Refresh()
1185 jonathan 392
1186 jonathan 430 def __GetColor(self, cur):
1187     dialog = wxColourDialog(self)
1188 jonathan 610 if cur is not Color.Transparent:
1189 jonathan 606 dialog.GetColourData().SetColour(Color2wxColour(cur))
1190    
1191 jonathan 430 ret = None
1192     if dialog.ShowModal() == wxID_OK:
1193     ret = wxColour2Color(dialog.GetColourData().GetColour())
1194    
1195     dialog.Destroy()
1196    
1197     return ret
1198    
1199 jonathan 460 def _OnChangeLineColor(self, event):
1200     clr = self.__GetColor(self.prop.GetLineColor())
1201 jonathan 430 if clr is not None:
1202 jonathan 460 self.prop.SetLineColor(clr)
1203 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1204 jonathan 430
1205 jonathan 485 def _OnChangeLineColorTrans(self, event):
1206 jonathan 610 self.prop.SetLineColor(Color.Transparent)
1207 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1208 jonathan 485
1209 jonathan 460 def _OnChangeFillColor(self, event):
1210 jonathan 430 clr = self.__GetColor(self.prop.GetFill())
1211     if clr is not None:
1212     self.prop.SetFill(clr)
1213 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1214 jonathan 430
1215 jonathan 485 def _OnChangeFillColorTrans(self, event):
1216 jonathan 610 self.prop.SetFill(Color.Transparent)
1217 jonathan 549 self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1218 jonathan 485
1219 jonathan 441 def GetClassGroupProperties(self):
1220 jonathan 415 return self.prop
1221 jonathan 392
1222    
1223 jonathan 549 class ClassDataPreviewWindow(wxWindow):
1224 jonathan 415
1225 jonathan 441 def __init__(self, rect, prop, shapeType,
1226 jonathan 430 parent = None, id = -1, size = wxDefaultSize):
1227     if parent is not None:
1228 jonathan 549 wxWindow.__init__(self, parent, id, (0, 0), size)
1229 jonathan 460 EVT_PAINT(self, self._OnPaint)
1230 jonathan 415
1231 jonathan 430 self.rect = rect
1232 jonathan 549
1233 jonathan 441 self.prop = prop
1234 jonathan 430 self.shapeType = shapeType
1235 jonathan 549 self.previewer = ClassDataPreviewer()
1236 jonathan 430
1237 jonathan 630 def GetProperties():
1238     return self.prop
1239    
1240 jonathan 460 def _OnPaint(self, event):
1241 jonathan 430 dc = wxPaintDC(self)
1242    
1243     # XXX: this doesn't seem to be having an effect:
1244     dc.DestroyClippingRegion()
1245    
1246 jonathan 549 if self.rect is None:
1247     w, h = self.GetSize()
1248     rect = wxRect(0, 0, w, h)
1249     else:
1250     rect = self.rect
1251 jonathan 430
1252 jonathan 549 self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1253 jonathan 430
1254 jonathan 549 class ClassDataPreviewer:
1255 jonathan 430
1256 jonathan 549 def Draw(self, dc, rect, prop, shapeType):
1257    
1258 jonathan 606 assert dc is not None
1259     assert isinstance(prop, ClassGroupProperties)
1260 jonathan 549
1261 jonathan 430 if rect is None:
1262 jonathan 549 x = 0
1263     y = 0
1264     w, h = dc.GetSize()
1265 jonathan 430 else:
1266     x = rect.GetX()
1267     y = rect.GetY()
1268     w = rect.GetWidth()
1269     h = rect.GetHeight()
1270    
1271 jonathan 460 stroke = prop.GetLineColor()
1272 jonathan 610 if stroke is Color.Transparent:
1273 jonathan 392 pen = wxTRANSPARENT_PEN
1274     else:
1275 jonathan 430 pen = wxPen(Color2wxColour(stroke),
1276 jonathan 460 prop.GetLineWidth(),
1277 jonathan 392 wxSOLID)
1278    
1279 jonathan 441 stroke = prop.GetFill()
1280 jonathan 610 if stroke is Color.Transparent:
1281 jonathan 392 brush = wxTRANSPARENT_BRUSH
1282     else:
1283 jonathan 430 brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1284 jonathan 392
1285     dc.SetPen(pen)
1286     dc.SetBrush(brush)
1287    
1288 jonathan 415 if shapeType == SHAPETYPE_ARC:
1289 jonathan 430 dc.DrawSpline([wxPoint(x, y + h),
1290     wxPoint(x + w/2, y + h/4),
1291     wxPoint(x + w/2, y + h/4*3),
1292     wxPoint(x + w, y)])
1293 jonathan 392
1294 jonathan 576 elif shapeType == SHAPETYPE_POINT:
1295 jonathan 415
1296 jonathan 430 dc.DrawCircle(x + w/2, y + h/2,
1297 jonathan 460 (min(w, h) - prop.GetLineWidth())/2)
1298 jonathan 392
1299 jonathan 576 elif shapeType == SHAPETYPE_POLYGON:
1300     dc.DrawRectangle(x, y, w, h)
1301    
1302 jonathan 415 class ClassRenderer(wxPyGridCellRenderer):
1303    
1304     def __init__(self, shapeType):
1305     wxPyGridCellRenderer.__init__(self)
1306 jonathan 549 self.shapeType = shapeType
1307     self.previewer = ClassDataPreviewer()
1308 jonathan 415
1309     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1310 jonathan 485 data = grid.GetTable().GetClassGroup(row)
1311 jonathan 415
1312     dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1313     rect.GetWidth(), rect.GetHeight())
1314     dc.SetPen(wxPen(wxLIGHT_GREY))
1315     dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1316     dc.DrawRectangle(rect.GetX(), rect.GetY(),
1317     rect.GetWidth(), rect.GetHeight())
1318    
1319 jonathan 441 if not isinstance(data, ClassGroupMap):
1320 jonathan 549 self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1321 jonathan 415
1322     if isSelected:
1323 jonathan 615 dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1324 jonathan 415 dc.SetBrush(wxTRANSPARENT_BRUSH)
1325 jonathan 615
1326 jonathan 415 dc.DrawRectangle(rect.GetX(), rect.GetY(),
1327     rect.GetWidth(), rect.GetHeight())
1328    
1329 jonathan 392 dc.DestroyClippingRegion()
1330    
1331 jonathan 630
1332     class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1333    
1334     def __init__(self, parent, id, props, shapeType,
1335     size = wxDefaultSize, style = 0):
1336    
1337     wxWindow.__init__(self, parent, id, size = size, style = style)
1338    
1339     self.SetProperties(props)
1340     self.SetShapeType(shapeType)
1341     self.AllowEdit(True)
1342    
1343     EVT_PAINT(self, self._OnPaint)
1344     EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1345    
1346     self.previewer = ClassDataPreviewer()
1347    
1348     def _OnPaint(self, event):
1349     dc = wxPaintDC(self)
1350    
1351     # XXX: this doesn't seem to be having an effect:
1352     dc.DestroyClippingRegion()
1353    
1354     w, h = self.GetClientSize()
1355    
1356     self.previewer.Draw(dc,
1357     wxRect(0, 0, w, h),
1358     self.GetProperties(),
1359     self.GetShapeType())
1360    
1361    
1362     def GetProperties(self):
1363     return self.props
1364    
1365     def SetProperties(self, props):
1366     self.props = props
1367     self.Refresh()
1368    
1369     def GetShapeType(self):
1370     return self.shapeType
1371    
1372     def SetShapeType(self, shapeType):
1373     self.shapeType = shapeType
1374     self.Refresh()
1375    
1376     def AllowEdit(self, allow):
1377     self.allowEdit = allow
1378    
1379     def DoEdit(self):
1380     if not self.allowEdit: return
1381    
1382     propDlg = SelectPropertiesDialog(NULL,
1383     self.GetProperties(),
1384     self.GetShapeType())
1385    
1386     if propDlg.ShowModal() == wxID_OK:
1387     new_prop = propDlg.GetClassGroupProperties()
1388     self.SetProperties(new_prop)
1389     self.Refresh()
1390    
1391     propDlg.Destroy()
1392    
1393     def _OnLeftDClick(self, event):
1394     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