/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1447 by jonathan, Thu Jul 17 14:59:29 2003 UTC revision 2688 by frank, Fri Jun 30 12:27:20 2006 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2003-2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jan-Oliver Wagner <[email protected]> (2003-2004)
4    # Martin Schulze <[email protected]> (2004)
5    # Frank Koormann <[email protected]> (2003, 2006)
6    # Bernhard Herzog <[email protected]> (2003)
7    # Jonathan Coles <[email protected]> (2003)
8  #  #
9  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
10  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 8  Line 12 
12  """Dialog for classifying how layers are displayed"""  """Dialog for classifying how layers are displayed"""
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    # $Source$
16    # $Id$
17    
18  import copy  import copy
19    import re
20    
21  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
22       FIELDTYPE_STRING       FIELDTYPE_STRING
# Line 24  from Thuban.Model.messages import MAP_LA Line 31  from Thuban.Model.messages import MAP_LA
31  from Thuban.Model.range import Range  from Thuban.Model.range import Range
32  from Thuban.Model.classification import \  from Thuban.Model.classification import \
33      Classification, ClassGroupDefault, \      Classification, ClassGroupDefault, \
34      ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \      ClassGroupSingleton, ClassGroupPattern, ClassGroupRange, ClassGroupMap, \
35      ClassGroupProperties      ClassGroupProperties
36    
37  from Thuban.Model.color import Transparent  from Thuban.Model.color import Transparent
38    
39  from Thuban.Model.layer import Layer, RasterLayer, \  from Thuban.Model.layer import Layer
40      SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
41    
42  from Thuban.UI.classgen import ClassGenDialog  from Thuban.UI.classgen import ClassGenDialog
43    from Thuban.UI.colordialog import ColorDialog
44    
45  from dialogs import NonModalNonParentDialog  from Thuban.UI.layerproperties import LayerProperties
46    from messages import MAP_REPLACED
 ID_CLASS_TABLE = 40011  
47    
48    
49  # table columns  # table columns
# Line 67  class ClassGrid(wxGrid): Line 74  class ClassGrid(wxGrid):
74          clazz -- the working classification that this grid should          clazz -- the working classification that this grid should
75                   use for display.                   use for display.
76          """          """
77            wxGrid.__init__(self, parent, -1, style = 0)
         wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)  
78    
79          self.classifier = classifier          self.classifier = classifier
80    
# Line 79  class ClassGrid(wxGrid): Line 85  class ClassGrid(wxGrid):
85          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
86          EVT_GRID_COL_SIZE(self, self._OnCellResize)          EVT_GRID_COL_SIZE(self, self._OnCellResize)
87          EVT_GRID_ROW_SIZE(self, self._OnCellResize)          EVT_GRID_ROW_SIZE(self, self._OnCellResize)
88            EVT_GRID_LABEL_RIGHT_CLICK(self, self._OnLabelRightClicked)
89    
90    
91      #def GetCellAttr(self, row, col):      #def GetCellAttr(self, row, col):
92          #print "GetCellAttr ", row, col          #print "GetCellAttr ", row, col
# Line 153  class ClassGrid(wxGrid): Line 161  class ClassGrid(wxGrid):
161              group = self.GetTable().GetClassGroup(sel[0])              group = self.GetTable().GetClassGroup(sel[0])
162              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
163                  wxMessageDialog(self,                  wxMessageDialog(self,
164                                  "The Default group cannot be removed.",                                  _("The Default group cannot be removed."),
165                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()
166                  return                  return
167                    
# Line 251  class ClassGrid(wxGrid): Line 259  class ClassGrid(wxGrid):
259          self.FitInside()          self.FitInside()
260          event.Skip()          event.Skip()
261    
262        def _OnLabelRightClicked(self, event):
263            """Process right click on label, raise popup for row labels."""
264            row, col = event.GetRow(), event.GetCol()
265            if col == -1:
266                self.labelPopup(event, row)
267    
268        def labelPopup(self, event, row):
269            """Raise grid label popup."""
270            # check if row label is Pattern or Singleton
271            label = self.GetRowLabelValue(row)
272            if (label == _("Pattern") or label == _("Singleton")):
273                xe,ye = event.GetPosition()
274                x=self.GetRowSize(row)/2
275                menu = wxMenu()
276                patternID = wxNewId()
277                singletonID = wxNewId()
278    
279                def _SetSingleton(event, self=self, row=row):
280                    table = self.GetTable()
281                    group = table.clazz.GetGroup(row - 1)
282                    if not isinstance(group, ClassGroupSingleton):
283                        ngroup = ClassGroupSingleton(
284                                    group.GetPattern(),
285                                    group.GetProperties(),
286                                    group.GetLabel()
287                                    )
288                        table.SetClassGroup(row, ngroup)
289                            
290                def _SetPattern(event, self=self, row=row):
291                    table = self.GetTable()
292                    group = table.clazz.GetGroup(row - 1)
293                    if not isinstance(group, ClassGroupPattern):
294                        try:
295                            re.compile(group.GetValue())
296                        except:
297                            pass
298                        else:
299                            ngroup = ClassGroupPattern(
300                                    group.GetValue(),
301                                    group.GetProperties(),
302                                    group.GetLabel()
303                                    )
304                            table.SetClassGroup(row, ngroup)
305    
306                menu.Append(singletonID, _("Singleton"))
307                EVT_MENU(self, singletonID, _SetSingleton)
308                if self.GetTable().fieldType == FIELDTYPE_STRING:
309                    menu.Append(patternID, _("Pattern"))
310                    EVT_MENU(self, patternID, _SetPattern)
311                self.PopupMenu(menu, wxPoint(x,ye))
312                menu.Destroy()
313    
314  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
315      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
316    
# Line 315  class ClassTable(wxPyGridTableBase): Line 375  class ClassTable(wxPyGridTableBase):
375          self.GetView().FitInside()          self.GetView().FitInside()
376    
377      def GetClassification(self):      def GetClassification(self):
378            """Return the current classification."""
379          return self.clazz          return self.clazz
380    
381      def SetClassification(self, clazz, group = None):      def SetClassification(self, clazz, group = None):
382            """Fill in the table with the given classification.
383            Select the given group if group is not None.
384            """
385    
386          self.GetView().BeginBatch()          self.GetView().BeginBatch()
387    
# Line 325  class ClassTable(wxPyGridTableBase): Line 389  class ClassTable(wxPyGridTableBase):
389    
390          row = -1          row = -1
391          self.clazz = clazz          self.clazz = clazz
392    
393          self.__NotifyRowChanges(old_len, self.GetNumberRows())          self.__NotifyRowChanges(old_len, self.GetNumberRows())
394    
395          #          #
# Line 338  class ClassTable(wxPyGridTableBase): Line 402  class ClassTable(wxPyGridTableBase):
402    
403          self.__Modified()          self.__Modified()
404    
   
405          self.GetView().EndBatch()          self.GetView().EndBatch()
406          self.GetView().FitInside()          self.GetView().FitInside()
407    
408      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
409            """Make sure table updates correctly if the number of
410            rows changes.
411            """
412          #          #
413          # silly message processing for updates to the number of          # silly message processing for updates to the number of
414          # rows and columns          # rows and columns
# Line 361  class ClassTable(wxPyGridTableBase): Line 427  class ClassTable(wxPyGridTableBase):
427              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
428              self.GetView().FitInside()              self.GetView().FitInside()
429    
   
430      def __SetRow(self, row, group):      def __SetRow(self, row, group):
431          """Set a row's data to that of the group.          """Set a row's data to that of the group.
432    
# Line 399  class ClassTable(wxPyGridTableBase): Line 464  class ClassTable(wxPyGridTableBase):
464              group = self.clazz.GetGroup(row - 1)              group = self.clazz.GetGroup(row - 1)
465              if isinstance(group, ClassGroupDefault):   return _("Default")              if isinstance(group, ClassGroupDefault):   return _("Default")
466              if isinstance(group, ClassGroupSingleton): return _("Singleton")              if isinstance(group, ClassGroupSingleton): return _("Singleton")
467                if isinstance(group, ClassGroupPattern):   return _("Pattern")
468              if isinstance(group, ClassGroupRange):     return _("Range")              if isinstance(group, ClassGroupRange):     return _("Range")
469              if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupMap):       return _("Map")
470    
# Line 432  class ClassTable(wxPyGridTableBase): Line 498  class ClassTable(wxPyGridTableBase):
498          """          """
499    
500          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
501          
502      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
503          """Return the object that is used to represent the given          """Return the object that is used to represent the given
504             cell coordinates. This may not be a string.             cell coordinates. This may not be a string.
505    
506          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
507          """          """
508    
# Line 462  class ClassTable(wxPyGridTableBase): Line 528  class ClassTable(wxPyGridTableBase):
528              return _("DEFAULT")              return _("DEFAULT")
529          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
530              return group.GetValue()              return group.GetValue()
531            elif isinstance(group, ClassGroupPattern):
532                return group.GetPattern()
533          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
534              return group.GetRange()              return group.GetRange()
535    
# Line 473  class ClassTable(wxPyGridTableBase): Line 541  class ClassTable(wxPyGridTableBase):
541             (string, number, or range)             (string, number, or range)
542    
543          Returns a tuple (type, data) where type is 0 if data is          Returns a tuple (type, data) where type is 0 if data is
544          a singleton value, or 1 if is a range          a singleton value, 1 if is a range or 2 if it is a pattern.
545          """          """
546    
547          type = self.fieldType          type = self.fieldType
548    
549          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
550              return (0, value)              # Approach: if we can compile the value as an expression,
551                # make it a pattern, else a singleton.
552                # This is quite crude, however I don't have a better idea:
553                # How to distinct the singleton "Thuban" from the pattern "Thuban"?
554                try:
555                    re.compile(value)
556                except:
557                    return (0, value)
558                else:
559                    return (2, value)
560          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
561              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
562                  # the float call allows the user to enter 1.0 for 1                  # the float call allows the user to enter 1.0 for 1
# Line 563  class ClassTable(wxPyGridTableBase): Line 640  class ClassTable(wxPyGridTableBase):
640                              ngroup = ClassGroupRange(props = props)                              ngroup = ClassGroupRange(props = props)
641                              changed = True                              changed = True
642                          ngroup.SetRange(dataInfo[1])                          ngroup.SetRange(dataInfo[1])
643                        elif dataInfo[0] == 2:
644                            if not isinstance(group, ClassGroupPattern):
645                                ngroup = ClassGroupPattern(props = props)
646                                changed = True
647                            ngroup.SetPattern(dataInfo[1])
648                      else:                      else:
649                          assert False                          assert False
650                          pass                          pass
# Line 593  class ClassTable(wxPyGridTableBase): Line 675  class ClassTable(wxPyGridTableBase):
675              return self.clazz.GetGroup(row - 1)              return self.clazz.GetGroup(row - 1)
676    
677      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
678            """Set the given row to properties of group."""
679          self.__SetRow(row, group)          self.__SetRow(row, group)
680          self.GetView().Refresh()          self.GetView().Refresh()
681    
# Line 670  EB_LAYER_TITLE = 0 Line 753  EB_LAYER_TITLE = 0
753  EB_SELECT_FIELD = 1  EB_SELECT_FIELD = 1
754  EB_GEN_CLASS = 2  EB_GEN_CLASS = 2
755    
756  class Classifier(NonModalNonParentDialog):  class Classifier(LayerProperties):
757    
758      type2string = {None:             _("None"),      type2string = {None:             _("None"),
759                     FIELDTYPE_STRING: _("Text"),                     FIELDTYPE_STRING: _("Text"),
760                     FIELDTYPE_INT:    _("Integer"),                     FIELDTYPE_INT:    _("Integer"),
761                     FIELDTYPE_DOUBLE: _("Decimal")}                     FIELDTYPE_DOUBLE: _("Decimal")}
762    
763      def __init__(self, parent, name, map, layer, group = None):      def __init__(self, parent, name, layer, group = None):
764          NonModalNonParentDialog.__init__(self, parent, name, "")          """Create a Properties/Classification dialog for a layer.
765            The layer is part of map. If group is not None, select that
766          self.__SetTitle(layer.Title())          group in the classification table.
767            """
768    
769          self.layer = layer          LayerProperties.__init__(self, parent, name, layer)
         self.map = map  
770    
         self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)  
771          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
772                               self.layer_shapestore_replaced)                               self.layer_shapestore_replaced)
773    
774          self.genDlg = None          self.genDlg = None
775            self.group = group
776    
777          ############################          LayerProperties.dialog_layout(self)
         # Create the controls  
         #  
778    
779          panel = wxPanel(self, -1)      def dialog_layout(self, panel, panelBox):
780    
781            if self.layer.HasClassification():
782    
783          text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())              self.fieldTypeText = wxStaticText(panel, -1, "")
         self.fieldTypeText = wxStaticText(panel, -1, "")  
784    
         if layer.HasClassification():  
785              self.originalClass = self.layer.GetClassification()              self.originalClass = self.layer.GetClassification()
786              self.originalClassField = self.layer.GetClassificationField()              self.originalClassField = self.layer.GetClassificationColumn()
787              field = self.originalClassField              field = self.originalClassField
788              fieldType = self.layer.GetFieldType(field)              fieldType = self.layer.GetFieldType(field)
789    
790              table = layer.ShapeStore().Table()              table = self.layer.ShapeStore().Table()
791              #              #
792              # make field choice box              # make field choice box
793              #              #
# Line 753  class Classifier(NonModalNonParentDialog Line 834  class Classifier(NonModalNonParentDialog
834              # calling __SelectField after creating the classGrid fills in the              # calling __SelectField after creating the classGrid fills in the
835              # grid with the correct information              # grid with the correct information
836              self.fields.SetSelection(self.__cur_field)              self.fields.SetSelection(self.__cur_field)
837              self.__SelectField(self.__cur_field, group = group)              self.__SelectField(self.__cur_field, group = self.group)
838    
         button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))  
         button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))  
         button_ok = wxButton(self, wxID_OK, _("OK"))  
         button_close = wxButton(self, wxID_CANCEL, _("Close"))  
         button_ok.SetDefault()  
   
         ############################  
         # Layout the controls  
         #  
   
         topBox = wxBoxSizer(wxVERTICAL)  
         panelBox = wxBoxSizer(wxVERTICAL)  
   
         sizer = wxBoxSizer(wxHORIZONTAL)  
         sizer.Add(wxStaticText(panel, -1, _("Title: ")),  
             0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)  
         sizer.Add(text_title, 1, wxGROW, 0)  
   
         panelBox.Add(sizer, 0, wxGROW, 4)  
   
         if isinstance(layer, RasterLayer):  
             type = "Image"  
         else:  
             type = layer.ShapeType()  
   
         panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),  
             0, wxALIGN_LEFT | wxALL, 4)  
   
         if layer.HasClassification():  
839    
840              classBox = wxStaticBoxSizer(              classBox = wxStaticBoxSizer(
841                          wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)                          wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
# Line 807  class Classifier(NonModalNonParentDialog Line 859  class Classifier(NonModalNonParentDialog
859              controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
860              controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
861              controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
862              controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)              controlButtonBox.Add( (60, 20), 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
863              controlButtonBox.Add(button_remove, 0,              controlButtonBox.Add(button_remove, 0,
864                                   wxGROW|wxALL|wxALIGN_BOTTOM, 4)                                   wxGROW|wxALL|wxALIGN_BOTTOM, 4)
865    
# Line 818  class Classifier(NonModalNonParentDialog Line 870  class Classifier(NonModalNonParentDialog
870              panelBox.Add(classBox, 1, wxGROW, 0)              panelBox.Add(classBox, 1, wxGROW, 0)
871    
872    
         buttonBox = wxBoxSizer(wxHORIZONTAL)  
         buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)  
   
         panel.SetAutoLayout(True)  
         panel.SetSizer(panelBox)  
         panelBox.Fit(panel)  
         panelBox.SetSizeHints(panel)  
   
         topBox.Add(panel, 1, wxGROW | wxALL, 4)  
         topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)  
   
         self.SetAutoLayout(True)  
         self.SetSizer(topBox)  
         topBox.Fit(self)  
         topBox.SetSizeHints(self)  
         self.Layout()  
   
         ###########  
   
873          EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)          EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
         EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)  
         EVT_BUTTON(self, wxID_OK, self._OnOK)  
         EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)  
         EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)  
         EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)  
   
874          EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)          EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
875          EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)          EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
876          EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)          EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
# Line 854  class Classifier(NonModalNonParentDialog Line 878  class Classifier(NonModalNonParentDialog
878          EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)          EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
879          EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)          EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
880    
         ######################  
   
         text_title.SetFocus()  
         self.haveApplied = False  
   
881      def unsubscribe_messages(self):      def unsubscribe_messages(self):
882          self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)          """Unsubscribe from all messages."""
883            LayerProperties.unsubscribe_messages(self)
884          self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,          self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
885                                 self.layer_shapestore_replaced)                                 self.layer_shapestore_replaced)
886    
     def map_layers_removed(self, map):  
         if self.layer not in self.map.Layers():  
             self.Close()  
   
887      def layer_shapestore_replaced(self, *args):      def layer_shapestore_replaced(self, *args):
888            """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
889            Close self.
890            """
891          self.Close()          self.Close()
892    
893      def EditSymbol(self, row):      def EditSymbol(self, row):
894            """Open up a dialog where the user can select the properties
895            for a group.
896            """
897          table = self.classGrid.GetTable()          table = self.classGrid.GetTable()
898          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
899    
# Line 885  class Classifier(NonModalNonParentDialog Line 907  class Classifier(NonModalNonParentDialog
907              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
908          self.Enable(True)          self.Enable(True)
909          propDlg.Destroy()          propDlg.Destroy()
910            
911      def _SetClassification(self, clazz):      def _SetClassification(self, clazz):
912                    """Called from the ClassGen dialog when a new classification has
913            been created and should be set in the table.
914            """
915            # FIXME: This could be implemented using a message
916    
917          self.fields.SetClientData(self.__cur_field, clazz)          self.fields.SetClientData(self.__cur_field, clazz)
918          self.classGrid.GetTable().SetClassification(clazz)          self.classGrid.GetTable().SetClassification(clazz)
919    
920      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
921            """Pack the classification setting into a Classification object.
922            Returns (Classification, fieldName) where fieldName is the selected
923            field in the table that the classification should be used with.
924            """
925    
926  #       numRows = self.classGrid.GetNumberRows()  #       numRows = self.classGrid.GetNumberRows()
927  #       assert numRows > 0  # there should always be a default row  #       assert numRows > 0  # there should always be a default row
# Line 912  class Classifier(NonModalNonParentDialog Line 942  class Classifier(NonModalNonParentDialog
942          return clazz, fieldName          return clazz, fieldName
943    
944      def __SetGridTable(self, fieldIndex, group = None):      def __SetGridTable(self, fieldIndex, group = None):
945            """Set the table with the classification associated with the
946            selected field at fieldIndex. Select the specified group
947            if group is not None.
948            """
949    
950          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
951    
# Line 929  class Classifier(NonModalNonParentDialog Line 963  class Classifier(NonModalNonParentDialog
963                                     self.layer.ShapeType(), group)                                     self.layer.ShapeType(), group)
964    
965      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
966            """Set the field type string using the data type of the field
967            at fieldIndex.
968            """
969          fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
970          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
971    
# Line 956  class Classifier(NonModalNonParentDialog Line 993  class Classifier(NonModalNonParentDialog
993          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
994    
995      def __SetTitle(self, title):      def __SetTitle(self, title):
996            """Set the title of the dialog."""
997          if title != "":          if title != "":
998              title = ": " + title              title = ": " + title
999    
1000          self.SetTitle(_("Layer Properties") + title)          self.SetTitle(_("Layer Properties") + title)
1001    
1002      def _OnEditSymbol(self, event):      def _OnEditSymbol(self, event):
1003            """Open up a dialog for the user to select group properties."""
1004          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1005    
1006          if len(sel) == 1:          if len(sel) == 1:
# Line 972  class Classifier(NonModalNonParentDialog Line 1011  class Classifier(NonModalNonParentDialog
1011          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
1012          self.__cur_field = index          self.__cur_field = index
1013    
1014      def _OnTry(self, event):      def OnTry(self, event):
1015          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
1016             it to the layer.             it to the layer.
1017          """          """
# Line 987  class Classifier(NonModalNonParentDialog Line 1026  class Classifier(NonModalNonParentDialog
1026              self.classGrid.SaveEditControlValue()              self.classGrid.SaveEditControlValue()
1027              clazz, name = self.__BuildClassification(self.__cur_field, True)              clazz, name = self.__BuildClassification(self.__cur_field, True)
1028    
1029              self.layer.SetClassificationField(name)              self.layer.SetClassificationColumn(name)
1030              self.layer.SetClassification(clazz)              self.layer.SetClassification(clazz)
1031    
1032          self.haveApplied = True          self.haveApplied = True
1033    
1034      def _OnOK(self, event):      def OnOK(self, event):
1035          self._OnTry(event)          self.OnTry(event)
         self.Close()  
   
     def OnClose(self, event):  
         self.unsubscribe_messages()  
         NonModalNonParentDialog.OnClose(self, event)  
   
     def _OnCloseBtn(self, event):  
         """Close is similar to Cancel except that any changes that were  
         made and applied remain applied, but the currently displayed  
         classification is discarded.  
         """  
   
1036          self.Close()          self.Close()
1037    
1038      def _OnRevert(self, event):      def OnRevert(self, event):
1039          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
1040          if self.haveApplied:          if self.haveApplied and self.layer.HasClassification():
1041              self.layer.SetClassificationField(self.originalClassField)              self.layer.SetClassificationColumn(self.originalClassField)
1042              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
1043    
1044          #self.Close()          #self.Close()
# Line 1023  class Classifier(NonModalNonParentDialog Line 1050  class Classifier(NonModalNonParentDialog
1050          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1051    
1052      def _OnGenClass(self, event):      def _OnGenClass(self, event):
1053            """Open up a dialog for the user to generate classifications."""
1054    
1055          self.genDlg = ClassGenDialog(self, self.layer,          self.genDlg = ClassGenDialog(self, self.layer,
1056                            self.fields.GetString(self.__cur_field))                            self.fields.GetString(self.__cur_field))
# Line 1034  class Classifier(NonModalNonParentDialog Line 1062  class Classifier(NonModalNonParentDialog
1062          self.genDlg.Show()          self.genDlg.Show()
1063    
1064      def _OnGenDialogClose(self, event):      def _OnGenDialogClose(self, event):
1065            """Reenable buttons after the generate classification
1066            dialog is closed.
1067            """
1068          self.genDlg.Destroy()          self.genDlg.Destroy()
1069          self.genDlg = None          self.genDlg = None
1070          self.__EnableButtons(EB_GEN_CLASS)          self.__EnableButtons(EB_GEN_CLASS)
1071    
1072      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1073            """When the user clicks MoveUp, try to move a group up one row."""
1074          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1075    
1076          if len(sel) == 1:          if len(sel) == 1:
# Line 1054  class Classifier(NonModalNonParentDialog Line 1086  class Classifier(NonModalNonParentDialog
1086                  self.classGrid.MakeCellVisible(i - 1, 0)                  self.classGrid.MakeCellVisible(i - 1, 0)
1087    
1088      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1089            """When the user clicks MoveDown, try to move a group down one row."""
1090          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1091    
1092          if len(sel) == 1:          if len(sel) == 1:
# Line 1069  class Classifier(NonModalNonParentDialog Line 1102  class Classifier(NonModalNonParentDialog
1102                  self.classGrid.MakeCellVisible(i + 1, 0)                  self.classGrid.MakeCellVisible(i + 1, 0)
1103    
1104      def _OnTitleChanged(self, event):      def _OnTitleChanged(self, event):
1105            """Update the dialog title when the user changed the layer name."""
1106          obj = event.GetEventObject()          obj = event.GetEventObject()
1107    
1108          self.layer.SetTitle(obj.GetValue())          self.layer.SetTitle(obj.GetValue())
# Line 1077  class Classifier(NonModalNonParentDialog Line 1111  class Classifier(NonModalNonParentDialog
1111          self.__EnableButtons(EB_LAYER_TITLE)          self.__EnableButtons(EB_LAYER_TITLE)
1112    
1113      def __EnableButtons(self, case):      def __EnableButtons(self, case):
1114            """Helper method that enables/disables the appropriate buttons
1115            based on the case provided. Cases are constants beginning with EB_.
1116            """
1117    
1118          list = {wxID_OK                 : True,          list = {wxID_OK                 : True,
1119                  wxID_CANCEL             : True,                  wxID_CANCEL             : True,
# Line 1113  class Classifier(NonModalNonParentDialog Line 1150  class Classifier(NonModalNonParentDialog
1150              if win:              if win:
1151                  win.Enable(enable)                  win.Enable(enable)
1152    
1153  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL_LINEWIDTH = 4002
1154  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1155  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1156  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1157  ID_SELPROP_STROKECLRTRANS = 4006  ID_SELPROP_STROKECLRTRANS = 4006
1158  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1159    ID_SELPROP_SPINCTRL_SIZE = 4008
1160    
1161  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1162        """Dialog that allows the user to select group properties."""
1163    
1164      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1165            """Open the dialog with the initial prop properties and shapeType."""
1166    
1167          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1168                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1169    
# Line 1179  class SelectPropertiesDialog(wxDialog): Line 1220  class SelectPropertiesDialog(wxDialog):
1220              ctrlBox.Add(fillColorBox, 0,              ctrlBox.Add(fillColorBox, 0,
1221                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1222    
1223            # Line width selection
1224          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1225          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1226                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1227          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl_linewidth = wxSpinCtrl(self,
1228                                     min=1, max=10,                                               ID_SELPROP_SPINCTRL_LINEWIDTH,
1229                                     value=str(prop.GetLineWidth()),                                               min=1, max=10,
1230                                     initial=prop.GetLineWidth())                                               value=str(prop.GetLineWidth()),
1231                                                 initial=prop.GetLineWidth())
1232    
1233          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_LINEWIDTH,
1234                         self._OnSpinLineWidth)
         spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)  
1235    
1236            spinBox.Add(self.spinCtrl_linewidth, 0, wxALIGN_LEFT | wxALL, 4)
1237          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1238    
1239            # Size selection
1240            if shapeType == SHAPETYPE_POINT:
1241                spinBox = wxBoxSizer(wxHORIZONTAL)
1242                spinBox.Add(wxStaticText(self, -1, _("Size: ")),
1243                            0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1244                self.spinCtrl_size = wxSpinCtrl(self, ID_SELPROP_SPINCTRL_SIZE,
1245                                                min=1, max=100,
1246                                                value=str(prop.GetSize()),
1247                                                initial=prop.GetSize())
1248    
1249                EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_SIZE, self._OnSpinSize)
1250    
1251                spinBox.Add(self.spinCtrl_size, 0, wxALIGN_LEFT | wxALL, 4)
1252                ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1253    
1254    
1255          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1256          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1257    
# Line 1206  class SelectPropertiesDialog(wxDialog): Line 1266  class SelectPropertiesDialog(wxDialog):
1266          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1267    
1268          button_ok.SetDefault()          button_ok.SetDefault()
1269                                                                                    
1270          #EVT_BUTTON(self, wxID_OK, self._OnOK)          #EVT_BUTTON(self, wxID_OK, self._OnOK)
1271          #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)          #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1272                                                                                    
1273          self.SetAutoLayout(True)          self.SetAutoLayout(True)
1274          self.SetSizer(topBox)          self.SetSizer(topBox)
1275          topBox.Fit(self)          topBox.Fit(self)
# Line 1221  class SelectPropertiesDialog(wxDialog): Line 1281  class SelectPropertiesDialog(wxDialog):
1281      def OnCancel(self, event):      def OnCancel(self, event):
1282          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1283    
1284      def _OnSpin(self, event):      def _OnSpinLineWidth(self, event):
1285          self.prop.SetLineWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl_linewidth.GetValue())
1286            self.previewWin.Refresh()
1287    
1288        def _OnSpinSize(self, event):
1289            self.prop.SetSize(self.spinCtrl_size.GetValue())
1290          self.previewWin.Refresh()          self.previewWin.Refresh()
1291    
1292      def __GetColor(self, cur):      def __GetColor(self, cur):
1293          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1294          if cur is not Transparent:          dialog.SetColor(cur)
             dialog.GetColourData().SetColour(Color2wxColour(cur))  
1295    
1296          ret = None          ret = None
1297          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1298              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1299    
1300          dialog.Destroy()          dialog.Destroy()
1301    
1302          return ret          return ret
1303            
1304      def _OnChangeLineColor(self, event):      def _OnChangeLineColor(self, event):
1305          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1306          if clr is not None:          if clr is not None:
# Line 1247  class SelectPropertiesDialog(wxDialog): Line 1310  class SelectPropertiesDialog(wxDialog):
1310      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1311          self.prop.SetLineColor(Transparent)          self.prop.SetLineColor(Transparent)
1312          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1313            
1314      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1315          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1316          if clr is not None:          if clr is not None:
# Line 1263  class SelectPropertiesDialog(wxDialog): Line 1326  class SelectPropertiesDialog(wxDialog):
1326    
1327    
1328  class ClassDataPreviewWindow(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1329        """A custom window that draws group properties using the correct shape."""
1330    
1331      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1332                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1333            """Draws the appropriate shape as specified with shapeType using
1334            prop properities.
1335            """
1336          if parent is not None:          if parent is not None:
1337              wxWindow.__init__(self, parent, id, (0, 0), size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1338              EVT_PAINT(self, self._OnPaint)              EVT_PAINT(self, self._OnPaint)
# Line 1294  class ClassDataPreviewWindow(wxWindow): Line 1361  class ClassDataPreviewWindow(wxWindow):
1361          self.previewer.Draw(dc, rect, self.prop, self.shapeType)          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1362    
1363  class ClassDataPreviewer:  class ClassDataPreviewer:
1364        """Class that actually draws a group property preview."""
1365    
1366      def Draw(self, dc, rect, prop, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1367            """Draw the property.
1368    
1369            returns: (w, h) as adapted extend if the drawing size
1370            exceeded the given rect. This can only be the case
1371            for point symbols. If the symbol fits the given rect,
1372            None is returned.
1373            """
1374    
1375          assert dc is not None          assert dc is not None
1376          assert isinstance(prop, ClassGroupProperties)          assert isinstance(prop, ClassGroupProperties)
# Line 1335  class ClassDataPreviewer: Line 1410  class ClassDataPreviewer:
1410    
1411          elif shapeType == SHAPETYPE_POINT:          elif shapeType == SHAPETYPE_POINT:
1412    
1413              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
1414                            (min(w, h) - prop.GetLineWidth())/2)              circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
1415                new_h = h
1416                new_w = w
1417                if h < circle_size: new_h = circle_size
1418                if w < circle_size: new_w = circle_size
1419                if new_h > h or new_w > w:
1420                    return (new_w, new_h)
1421    
1422          elif shapeType == SHAPETYPE_POLYGON:          elif shapeType == SHAPETYPE_POLYGON:
1423              dc.DrawRectangle(x, y, w, h)              dc.DrawRectangle(x, y, w, h)
1424    
1425            return None
1426    
1427  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1428        """A wrapper class that can be used to draw group properties in a
1429        grid table.
1430        """
1431    
1432      def __init__(self, shapeType):      def __init__(self, shapeType):
1433          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
# Line 1359  class ClassRenderer(wxPyGridCellRenderer Line 1445  class ClassRenderer(wxPyGridCellRenderer
1445                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1446    
1447          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1448              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)              new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
1449                                               self.shapeType)
1450                if new_size is not None:
1451                    (new_w, new_h) = new_size
1452                    grid.SetRowSize(row, new_h)
1453                    grid.SetColSize(col, new_h)
1454                    grid.ForceRefresh()
1455    
1456                    # now that we know the height, redraw everything
1457                    rect.SetHeight(new_h)
1458                    rect.SetWidth(new_w)
1459                    dc.DestroyClippingRegion()
1460                    dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1461                                         rect.GetWidth(), rect.GetHeight())
1462                    dc.SetPen(wxPen(wxLIGHT_GREY))
1463                    dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1464                    dc.DrawRectangle(rect.GetX(), rect.GetY(),
1465                                     rect.GetWidth(), rect.GetHeight())
1466                    self.previewer.Draw(dc, rect, data.GetProperties(),
1467                                        self.shapeType)
1468    
1469          if isSelected:          if isSelected:
1470              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
# Line 1371  class ClassRenderer(wxPyGridCellRenderer Line 1476  class ClassRenderer(wxPyGridCellRenderer
1476          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1477    
1478    
1479  class ClassGroupPropertiesCtrl(wxWindow, wxControl):  class ClassGroupPropertiesCtrl(wxControl):
1480        """A custom window and control that draw a preview of group properties
1481        and can open a dialog to modify the properties if the user double-clicks
1482        it.
1483        """
1484    
1485      def __init__(self, parent, id, props, shapeType,      def __init__(self, parent, id, props, shapeType,
1486                   size = wxDefaultSize, style = 0):                   size = wxDefaultSize, style = 0):
1487            wxControl.__init__(self, parent, id, size = size, style = style)
         wxWindow.__init__(self, parent, id, size = size, style = style)  
1488    
1489          self.parent = parent          self.parent = parent
1490    
# Line 1418  class ClassGroupPropertiesCtrl(wxWindow, Line 1526  class ClassGroupPropertiesCtrl(wxWindow,
1526          self.Refresh()          self.Refresh()
1527    
1528      def AllowEdit(self, allow):      def AllowEdit(self, allow):
1529            """Allow/Disallow double-clicking on the control."""
1530          self.allowEdit = allow          self.allowEdit = allow
1531    
1532      def DoEdit(self):      def DoEdit(self):
1533            """Open the properties selector dialog."""
1534    
1535          if not self.allowEdit: return          if not self.allowEdit: return
1536    
1537          propDlg = SelectPropertiesDialog(self.parent,          propDlg = SelectPropertiesDialog(self.parent,
# Line 1436  class ClassGroupPropertiesCtrl(wxWindow, Line 1547  class ClassGroupPropertiesCtrl(wxWindow,
1547    
1548      def _OnLeftDClick(self, event):      def _OnLeftDClick(self, event):
1549          self.DoEdit()          self.DoEdit()
1550    

Legend:
Removed from v.1447  
changed lines
  Added in v.2688

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26