/[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 782 - (hide annotations)
Tue Apr 29 16:53:55 2003 UTC (21 years, 10 months ago) by jonathan
File MIME type: text/x-python
File size: 44716 byte(s)
Remove all uses of Str2Num.
(ClassTable.SetValueAsCustom): Rename keyword argument in
        ClassGroup* constructors to match argument name.

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