/[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 630 - (hide annotations)
Wed Apr 9 10:10:06 2003 UTC (21 years, 11 months ago) by jonathan
File MIME type: text/x-python
File size: 43670 byte(s)
Removed a lot of debugging code.
(Classifier._SetClassification): Callback method so that the
        class generator can set the classification in the grid.
(ClassGroupPropertiesCtrl): New. Encapsulates the drawing and
        editing of a group properties class into a wxWindows control.

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