/[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 2386 by jan, Thu Oct 7 14:43:45 2004 UTC revision 2688 by frank, Fri Jun 30 12:27:20 2006 UTC
# Line 1  Line 1 
1  # Copyright (c) 2003-2004 by Intevation GmbH  # Copyright (c) 2003-2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]> (2003-2004)  # Jan-Oliver Wagner <[email protected]> (2003-2004)
4  # Martin Schulze <[email protected]> (2004)  # Martin Schulze <[email protected]> (2004)
5  # Frank Koormann <[email protected]> (2003)  # Frank Koormann <[email protected]> (2003, 2006)
6  # Bernhard Herzog <[email protected]> (2003)  # Bernhard Herzog <[email protected]> (2003)
7  # Jonathan Coles <[email protected]> (2003)  # Jonathan Coles <[email protected]> (2003)
8  #  #
# Line 16  __version__ = "$Revision$" Line 16  __version__ = "$Revision$"
16  # $Id$  # $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 30  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  from Thuban.Model.data import 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  from Thuban.UI.colordialog import ColorDialog
44    
45  from dialogs import NonModalNonParentDialog  from Thuban.UI.layerproperties import LayerProperties
46  from messages import MAP_REPLACED  from messages import MAP_REPLACED
47    
 ID_CLASS_TABLE = 40011  
   
48    
49  # table columns  # table columns
50  COL_VISIBLE = 0  COL_VISIBLE = 0
# Line 75  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 87  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 161  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 259  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 412  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 475  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 486  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 576  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 684  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"),
# Line 697  class Classifier(NonModalNonParentDialog Line 766  class Classifier(NonModalNonParentDialog
766          group in the classification table.          group in the classification table.
767          """          """
768    
769          NonModalNonParentDialog.__init__(self, parent, name, "")          LayerProperties.__init__(self, parent, name, layer)
   
         self.__SetTitle(layer.Title())  
   
         self.parent.Subscribe(MAP_REPLACED, self.map_replaced)  
         self.layer = layer  
         self.map = parent.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)
778          # Create the controls  
779          #      def dialog_layout(self, panel, panelBox):
780    
781          panel = wxPanel(self, -1)          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.GetClassificationColumn()              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 773  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 827  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 838  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 874  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          """Unsubscribe from all messages."""          """Unsubscribe from all messages."""
883          self.parent.Unsubscribe(MAP_REPLACED, self.map_replaced)          LayerProperties.unsubscribe_messages(self)
         self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)  
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):  
         """Subscribed to MAP_LAYERS_REMOVED. If this layer was removed,  
         Close self.  
         """  
         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.          """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
   
889          Close self.          Close self.
890          """          """
891          self.Close()          self.Close()
892    
     def map_replaced(self, *args):  
         """Subscribed to the mainwindow's MAP_REPLACED message. Close self."""  
         self.Close()  
   
893      def EditSymbol(self, row):      def EditSymbol(self, row):
894          """Open up a dialog where the user can select the properties          """Open up a dialog where the user can select the properties
895          for a group.          for a group.
# Line 1025  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 1045  class Classifier(NonModalNonParentDialog Line 1031  class Classifier(NonModalNonParentDialog
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.SetClassificationColumn(self.originalClassField)              self.layer.SetClassificationColumn(self.originalClassField)
1042              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
1043    
# Line 1502  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      """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      and can open a dialog to modify the properties if the user double-clicks
1482      it.      it.
# Line 1510  class ClassGroupPropertiesCtrl(wxWindow, Line 1484  class ClassGroupPropertiesCtrl(wxWindow,
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 1575  class ClassGroupPropertiesCtrl(wxWindow, Line 1548  class ClassGroupPropertiesCtrl(wxWindow,
1548      def _OnLeftDClick(self, event):      def _OnLeftDClick(self, event):
1549          self.DoEdit()          self.DoEdit()
1550    
 from Thuban.UI.mainwindow import layer_properties_dialogs  
 layer_properties_dialogs.add(Layer, Classifier)  
 layer_properties_dialogs.add(RasterLayer, Classifier)  

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26