/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Diff of /trunk/thuban/Thuban/UI/classifier.py

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

revision 630 by jonathan, Wed Apr 9 10:10:06 2003 UTC revision 2376 by jan, Sun Oct 3 21:23:25 2004 UTC
# Line 18  from wxPython.wx import * Line 18  from wxPython.wx import *
18  from wxPython.grid import *  from wxPython.grid import *
19    
20  from Thuban import _  from Thuban import _
21  from Thuban.common import *  from Thuban.UI.common import Color2wxColour, wxColour2Color
 from Thuban.UI.common import *  
22    
23  from Thuban.Model.classification import *  from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
24    from Thuban.Model.range import Range
25    from Thuban.Model.classification import \
26        Classification, ClassGroupDefault, \
27        ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
28        ClassGroupProperties
29    
30  from Thuban.Model.color import Color  from Thuban.Model.color import Transparent
31    
32  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.layer import Layer, RasterLayer
33    from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
34    
35  from Thuban.UI.classgen import ClassGenDialog, ClassGenerator  from Thuban.UI.classgen import ClassGenDialog
36    from Thuban.UI.colordialog import ColorDialog
37    
38  from dialogs import NonModalDialog  from dialogs import NonModalNonParentDialog
39    from messages import MAP_REPLACED
40    
 # widget id's  
 ID_PROPERTY_SELECT = 4010  
41  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
42    
43    
44  # table columns  # table columns
45  COL_SYMBOL = 0  COL_VISIBLE = 0
46  COL_VALUE  = 1  COL_SYMBOL  = 1
47  COL_LABEL  = 2  COL_VALUE   = 2
48    COL_LABEL   = 3
49    NUM_COLS    = 4
50    
51  # indices into the client data lists in Classifier.fields  # indices into the client data lists in Classifier.fields
52  FIELD_CLASS = 0  FIELD_CLASS = 0
# Line 63  class ClassGrid(wxGrid): Line 70  class ClassGrid(wxGrid):
70                   use for display.                   use for display.
71          """          """
72    
73          #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))          wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
         wxGrid.__init__(self, parent, ID_CLASS_TABLE)  
         #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)  
74    
75          self.classifier = classifier          self.classifier = classifier
76    
# Line 77  class ClassGrid(wxGrid): Line 82  class ClassGrid(wxGrid):
82          EVT_GRID_COL_SIZE(self, self._OnCellResize)          EVT_GRID_COL_SIZE(self, self._OnCellResize)
83          EVT_GRID_ROW_SIZE(self, self._OnCellResize)          EVT_GRID_ROW_SIZE(self, self._OnCellResize)
84    
85      def CreateTable(self, clazz, shapeType, group = None):      #def GetCellAttr(self, row, col):
86            #print "GetCellAttr ", row, col
87            #wxGrid.GetCellAttr(self, row, col)
88    
89        def CreateTable(self, clazz, fieldType, shapeType, group = None):
90    
91          assert isinstance(clazz, Classification)          assert isinstance(clazz, Classification)
92    
93          table = self.GetTable()          table = self.GetTable()
94          if table is None:          if table is None:
95              w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()              w = self.GetDefaultColSize() * NUM_COLS \
96              h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()                  + self.GetDefaultRowLabelSize()
97                h = self.GetDefaultRowSize() * 4 \
98                    + self.GetDefaultColLabelSize()
99    
100              self.SetDimensions(-1, -1, w, h)              self.SetDimensions(-1, -1, w, h)
101              self.SetSizeHints(w, h, -1, -1)              self.SetSizeHints(w, h, -1, -1)
102              table = ClassTable(self)              table = ClassTable(self)
# Line 94  class ClassGrid(wxGrid): Line 106  class ClassGrid(wxGrid):
106          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
107          self.ClearSelection()          self.ClearSelection()
108    
109          table.Reset(clazz, shapeType, group)          table.Reset(clazz, fieldType, shapeType, group)
110    
111      def GetCurrentSelection(self):      def GetCurrentSelection(self):
112          """Return the currently highlighted rows as an increasing list          """Return the currently highlighted rows as an increasing list
# Line 106  class ClassGrid(wxGrid): Line 118  class ClassGrid(wxGrid):
118      def GetSelectedRows(self):      def GetSelectedRows(self):
119          return self.GetCurrentSelection()          return self.GetCurrentSelection()
120    
121      def SetCellRenderer(self, row, col):      #def SetCellRenderer(self, row, col, renderer):
122          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
123    
124      #      #
125      # [Set|Get]Table is taken from http://wiki.wxpython.org      # [Set|Get]Table is taken from http://wiki.wxpython.org
# Line 202  class ClassGrid(wxGrid): Line 214  class ClassGrid(wxGrid):
214  #                                  sel = False))  #                                  sel = False))
215    
216      def _OnCellDClick(self, event):      def _OnCellDClick(self, event):
217          """Handle a double on a cell."""          """Handle a double click on a cell."""
218    
219          r = event.GetRow()          r = event.GetRow()
220          c = event.GetCol()          c = event.GetCol()
         if c == COL_SYMBOL:  
             self.classifier.EditGroupProperties(r)  
221    
222            if c == COL_SYMBOL:
223                self.classifier.EditSymbol(r)
224            else:
225                event.Skip()
226    
227      #      #
228      # _OnSelectedRange() and _OnSelectedCell() were borrowed      # _OnSelectedRange() and _OnSelectedCell() were borrowed
# Line 237  class ClassGrid(wxGrid): Line 251  class ClassGrid(wxGrid):
251    
252      def _OnCellResize(self, event):      def _OnCellResize(self, event):
253          self.FitInside()          self.FitInside()
254            event.Skip()
255    
256  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
257      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
258    
259      NUM_COLS = 3      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
   
     __col_labels = [_("Symbol"), _("Value"), _("Label")]  
260    
261    
262      def __init__(self, view = None):      def __init__(self, view = None):
     #def __init__(self, clazz, shapeType, view = None):  
263          """Constructor.          """Constructor.
264    
265          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
# Line 257  class ClassTable(wxPyGridTableBase): Line 269  class ClassTable(wxPyGridTableBase):
269    
270          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
271    
272          self.SetView(view)          assert len(ClassTable.__col_labels) == NUM_COLS
273    
274          self.clazz = None          self.clazz = None
275            self.__colAttr = {}
276    
277          #self.Reset(clazz, shapeType)          self.SetView(view)
278    
279      def Reset(self, clazz, shapeType, group = None):      def Reset(self, clazz, fieldType, shapeType, group = None):
280          """Reset the table with the given data.          """Reset the table with the given data.
281    
282          This is necessary because wxWindows does not allow a grid's          This is necessary because wxWindows does not allow a grid's
# Line 280  class ClassTable(wxPyGridTableBase): Line 294  class ClassTable(wxPyGridTableBase):
294    
295          self.GetView().BeginBatch()          self.GetView().BeginBatch()
296    
297          self.fieldType = clazz.GetFieldType()          self.fieldType = fieldType
298          self.shapeType = shapeType          self.shapeType = shapeType
299    
300          self.SetClassification(clazz, group)          self.SetClassification(clazz, group)
301          self.__Modified(-1)          self.__Modified(-1)
302    
303            self.__colAttr = {}
304    
305            attr = wxGridCellAttr()
306            attr.SetEditor(wxGridCellBoolEditor())
307            attr.SetRenderer(wxGridCellBoolRenderer())
308            attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER)
309            self.__colAttr[COL_VISIBLE] = attr
310    
311            attr = wxGridCellAttr()
312            attr.SetRenderer(ClassRenderer(self.shapeType))
313            attr.SetReadOnly()
314            self.__colAttr[COL_SYMBOL] = attr
315    
316          self.GetView().EndBatch()          self.GetView().EndBatch()
317          self.GetView().FitInside()          self.GetView().FitInside()
318    
319      def GetClassification(self):      def GetClassification(self):
320            """Return the current classification."""
321          return self.clazz          return self.clazz
322    
323      def SetClassification(self, clazz, group = None):      def SetClassification(self, clazz, group = None):
324            """Fill in the table with the given classification.
325            Select the given group if group is not None.
326            """
327    
328          self.GetView().BeginBatch()          self.GetView().BeginBatch()
329    
330          old_len = self.GetNumberRows()          old_len = self.GetNumberRows()
331    
         #  
         # copy the data out of the classification and into our  
         # array  
         #  
332          row = -1          row = -1
 #       for g in clazz:  
 #           ng = copy.deepcopy(g)  
 #           self.__SetRow(None, ng)  
 #           if g is group:  
 #               row = self.GetNumberRows() - 1  
 #               #print "selecting row..."  
   
   
         #self.clazz = copy.deepcopy(clazz)  
333          self.clazz = clazz          self.clazz = clazz
334    
335          self.__NotifyRowChanges(old_len, self.GetNumberRows())          self.__NotifyRowChanges(old_len, self.GetNumberRows())
336    
337            #
338            # XXX: this is dead code at the moment
339            #
340          if row > -1:          if row > -1:
341              self.GetView().ClearSelection()              self.GetView().ClearSelection()
342              self.GetView().SelectRow(row)              self.GetView().SelectRow(row)
# Line 327  class ClassTable(wxPyGridTableBase): Line 348  class ClassTable(wxPyGridTableBase):
348          self.GetView().FitInside()          self.GetView().FitInside()
349    
350      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
351            """Make sure table updates correctly if the number of
352            rows changes.
353            """
354          #          #
355          # silly message processing for updates to the number of          # silly message processing for updates to the number of
356          # rows and columns          # rows and columns
# Line 345  class ClassTable(wxPyGridTableBase): Line 369  class ClassTable(wxPyGridTableBase):
369              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
370              self.GetView().FitInside()              self.GetView().FitInside()
371    
   
372      def __SetRow(self, row, group):      def __SetRow(self, row, group):
373          """Set a row's data to that of the group.          """Set a row's data to that of the group.
374    
# Line 387  class ClassTable(wxPyGridTableBase): Line 410  class ClassTable(wxPyGridTableBase):
410              if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupMap):       return _("Map")
411    
412          assert False # shouldn't get here          assert False # shouldn't get here
413          return _("")          return ""
414    
415      def GetNumberRows(self):      def GetNumberRows(self):
416          """Return the number of rows."""          """Return the number of rows."""
# Line 398  class ClassTable(wxPyGridTableBase): Line 421  class ClassTable(wxPyGridTableBase):
421    
422      def GetNumberCols(self):      def GetNumberCols(self):
423          """Return the number of columns."""          """Return the number of columns."""
424          return self.NUM_COLS          return NUM_COLS
425    
426      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
427          """Determine if a cell is empty. This is always false."""          """Determine if a cell is empty. This is always false."""
# Line 416  class ClassTable(wxPyGridTableBase): Line 439  class ClassTable(wxPyGridTableBase):
439          """          """
440    
441          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
442          self.__Modified()  
         
443      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
444          """Return the object that is used to represent the given          """Return the object that is used to represent the given
445             cell coordinates. This may not be a string.             cell coordinates. This may not be a string.
446    
447          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
448          """          """
449    
# Line 431  class ClassTable(wxPyGridTableBase): Line 453  class ClassTable(wxPyGridTableBase):
453              group = self.clazz.GetGroup(row - 1)              group = self.clazz.GetGroup(row - 1)
454    
455    
456            if col == COL_VISIBLE:
457                return group.IsVisible()
458    
459          if col == COL_SYMBOL:          if col == COL_SYMBOL:
460              return group.GetProperties()              return group.GetProperties()
461    
# Line 445  class ClassTable(wxPyGridTableBase): Line 470  class ClassTable(wxPyGridTableBase):
470          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
471              return group.GetValue()              return group.GetValue()
472          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
473              return _("%s - %s") % (group.GetMin(), group.GetMax())              return group.GetRange()
474    
475          assert(False) # shouldn't get here          assert False # shouldn't get here
476          return None          return None
477    
478      def __ParseInput(self, value):      def __ParseInput(self, value):
479          """Try to determine what kind of input value is          """Try to determine what kind of input value is
480             (string, number, or range)             (string, number, or range)
481    
482          Returns a tuple of length one if there is a single          Returns a tuple (type, data) where type is 0 if data is
483          value, or of length two if it is a range.          a singleton value, or 1 if is a range
484          """          """
485    
486          type = self.fieldType          type = self.fieldType
487    
488          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
489              return (value,)              return (0, value)
490          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
   
491              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
492                    # the float call allows the user to enter 1.0 for 1
493                  conv = lambda p: int(float(p))                  conv = lambda p: int(float(p))
494              else:              else:
495                  conv = lambda p: p                  conv = float
496    
497              #              #
498              # first try to take the input as a single number              # first try to take the input as a single number
499              # if there's an exception try to break it into              # if there's an exception try to break it into
500              # a range seperated by a '-'. take care to ignore              # a range. if there is an exception here, let it
501              # a leading '-' as that could be for a negative number.              # pass up to the calling function.
             # then try to parse the individual parts. if there  
             # is an exception here, let it pass up to the calling  
             # function.  
502              #              #
503              try:              try:
504                  return (conv(Str2Num(value)),)                  return (0, conv(value))
505              except ValueError:              except ValueError:
506                  i = value.find('-')                  return (1, Range(value))
                 if i == 0:  
                     i = value.find('-', 1)  
   
                 return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))  
507    
508          assert False  # shouldn't get here          assert False  # shouldn't get here
509          return (0,)          return (0,None)
               
510    
511      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
512          """Set the cell specified by 'row' and 'col' to 'value'.          """Set the cell specified by 'row' and 'col' to 'value'.
# Line 503  class ClassTable(wxPyGridTableBase): Line 520  class ClassTable(wxPyGridTableBase):
520          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
521          """          """
522    
523          assert col >= 0 and col < self.GetNumberCols()          assert 0 <= col < self.GetNumberCols()
524          assert row >= 0 and row < self.GetNumberRows()          assert 0 <= row < self.GetNumberRows()
525    
526          if row == 0:          if row == 0:
527              group = self.clazz.GetDefaultGroup()              group = self.clazz.GetDefaultGroup()
# Line 513  class ClassTable(wxPyGridTableBase): Line 530  class ClassTable(wxPyGridTableBase):
530    
531          mod = True # assume the data will change          mod = True # assume the data will change
532    
533          if col == COL_SYMBOL:          if col == COL_VISIBLE:
534                group.SetVisible(value)
535            elif col == COL_SYMBOL:
536              group.SetProperties(value)              group.SetProperties(value)
537          elif col == COL_LABEL:          elif col == COL_LABEL:
538              group.SetLabel(value)              group.SetLabel(value)
# Line 541  class ClassTable(wxPyGridTableBase): Line 560  class ClassTable(wxPyGridTableBase):
560                      # changing the underlying group type if the                      # changing the underlying group type if the
561                      # group was a singleton and a range was entered                      # group was a singleton and a range was entered
562                      #                      #
563                      if len(dataInfo) == 1:                      if dataInfo[0] == 0:
564                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
565                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(props = props)
566                              changed = True                              changed = True
567                          ngroup.SetValue(dataInfo[0])                          ngroup.SetValue(dataInfo[1])
568                      elif len(dataInfo) == 2:                      elif dataInfo[0] == 1:
569                          if not isinstance(group, ClassGroupRange):                          if not isinstance(group, ClassGroupRange):
570                              ngroup = ClassGroupRange(prop = props)                              ngroup = ClassGroupRange(props = props)
571                              changed = True                              changed = True
572                          ngroup.SetRange(dataInfo[0], dataInfo[1])                          ngroup.SetRange(dataInfo[1])
573                      else:                      else:
574                          assert False                          assert False
575                          pass                          pass
# Line 569  class ClassTable(wxPyGridTableBase): Line 588  class ClassTable(wxPyGridTableBase):
588      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
589          """Returns the cell attributes"""          """Returns the cell attributes"""
590    
591          attr = wxGridCellAttr()          return self.__colAttr.get(col, wxGridCellAttr()).Clone()
         #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)  
   
         if col == COL_SYMBOL:  
             # we need to create a new renderer each time, because  
             # SetRenderer takes control of the parameter  
             attr.SetRenderer(ClassRenderer(self.shapeType))  
             attr.SetReadOnly()  
   
         return attr  
592    
593      def GetClassGroup(self, row):      def GetClassGroup(self, row):
594          """Return the ClassGroup object representing row 'row'."""          """Return the ClassGroup object representing row 'row'."""
# Line 590  class ClassTable(wxPyGridTableBase): Line 600  class ClassTable(wxPyGridTableBase):
600              return self.clazz.GetGroup(row - 1)              return self.clazz.GetGroup(row - 1)
601    
602      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
603            """Set the given row to properties of group."""
604          self.__SetRow(row, group)          self.__SetRow(row, group)
605          self.GetView().Refresh()          self.GetView().Refresh()
606    
# Line 644  class ClassTable(wxPyGridTableBase): Line 655  class ClassTable(wxPyGridTableBase):
655              self.__NotifyRowChanges(old_len, self.GetNumberRows())              self.__NotifyRowChanges(old_len, self.GetNumberRows())
656    
657    
658  ID_CLASSIFY_OK = 4001  ID_PROPERTY_REVERT = 4002
659  ID_CLASSIFY_CANCEL = 4002  ID_PROPERTY_ADD = 4003
660  ID_CLASSIFY_ADD = 4003  ID_PROPERTY_GENCLASS = 4004
661  ID_CLASSIFY_GENCLASS = 4004  ID_PROPERTY_REMOVE = 4005
662  ID_CLASSIFY_REMOVE = 4005  ID_PROPERTY_MOVEUP = 4006
663  ID_CLASSIFY_MOVEUP = 4006  ID_PROPERTY_MOVEDOWN = 4007
664  ID_CLASSIFY_MOVEDOWN = 4007  ID_PROPERTY_TRY = 4008
665  ID_CLASSIFY_APPLY = 4008  ID_PROPERTY_EDITSYM = 4009
666  ID_CLASSIFY_EDITPROPS = 4009  ID_PROPERTY_SELECT = 4011
667  ID_CLASSIFY_CLOSE = 4010  ID_PROPERTY_TITLE = 4012
668    ID_PROPERTY_FIELDTEXT = 4013
669    
670  BTN_ADD = 0  BTN_ADD = 0
671  BTN_EDIT = 1  BTN_EDIT = 1
# Line 662  BTN_UP = 3 Line 674  BTN_UP = 3
674  BTN_DOWN = 4  BTN_DOWN = 4
675  BTN_RM = 5  BTN_RM = 5
676    
677  class Classifier(NonModalDialog):  EB_LAYER_TITLE = 0
678    EB_SELECT_FIELD = 1
679    EB_GEN_CLASS = 2
680    
681    class Classifier(NonModalNonParentDialog):
682    
683      type2string = {None:             _("None"),      type2string = {None:             _("None"),
684                     FIELDTYPE_STRING: _("Text"),                     FIELDTYPE_STRING: _("Text"),
# Line 670  class Classifier(NonModalDialog): Line 686  class Classifier(NonModalDialog):
686                     FIELDTYPE_DOUBLE: _("Decimal")}                     FIELDTYPE_DOUBLE: _("Decimal")}
687    
688      def __init__(self, parent, name, layer, group = None):      def __init__(self, parent, name, layer, group = None):
689          NonModalDialog.__init__(self, parent, name,          """Create a Properties/Classification dialog for a layer.
690                                  _("Classifier: %s") % layer.Title())          The layer is part of map. If group is not None, select that
691            group in the classification table.
692            """
693    
694            NonModalNonParentDialog.__init__(self, parent, name, "")
695    
696          panel = wxPanel(self, -1, size=(100, 100))          self.__SetTitle(layer.Title())
697    
698            self.parent.Subscribe(MAP_REPLACED, self.map_replaced)
699          self.layer = layer          self.layer = layer
700            self.map = parent.Map()
701    
702          self.originalClass = self.layer.GetClassification()          self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
703          field = self.originalClass.GetField()          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
704          fieldType = self.originalClass.GetFieldType()                               self.layer_shapestore_replaced)
705    
706          self.genDlg = None          self.genDlg = None
707    
708          topBox = wxBoxSizer(wxVERTICAL)          ############################
709          panelBox = wxBoxSizer(wxVERTICAL)          # Create the controls
   
         #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),  
             #0, wxALIGN_LEFT | wxALL, 4)  
         panelBox.Add(wxStaticText(panel, -1,  
                                 _("Layer Type: %s") % layer.ShapeType()),  
             0, wxALIGN_LEFT | wxALL, 4)  
   
   
         #  
         # make field combo box  
710          #          #
         self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",  
                                      style = wxCB_READONLY)  
   
         self.num_cols = layer.table.field_count()  
         # just assume the first field in case one hasn't been  
         # specified in the file.  
         self.__cur_field = 0  
   
         self.fields.Append("<None>")  
         self.fields.SetClientData(0, None)  
   
         for i in range(self.num_cols):  
             type, name, len, decc = layer.table.field_info(i)  
             self.fields.Append(name)  
   
             if name == field:  
                 self.__cur_field = i + 1  
                 self.fields.SetClientData(i + 1,  
                                           copy.deepcopy(self.originalClass))  
             else:  
                 self.fields.SetClientData(i + 1, None)  
711    
712            panel = wxPanel(self, -1)
713    
714          ###########          text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
   
715          self.fieldTypeText = wxStaticText(panel, -1, "")          self.fieldTypeText = wxStaticText(panel, -1, "")
         panelBox.Add(self.fieldTypeText, 0,  
                      wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)  
716    
717          propertyBox = wxBoxSizer(wxHORIZONTAL)          if layer.HasClassification():
718          propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),              self.originalClass = self.layer.GetClassification()
719              0, wxALIGN_LEFT | wxALL, 4)              self.originalClassField = self.layer.GetClassificationColumn()
720          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)              field = self.originalClassField
721          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)              fieldType = self.layer.GetFieldType(field)
722    
723          panelBox.Add(propertyBox, 0, wxGROW, 4)              table = layer.ShapeStore().Table()
724                #
725                # make field choice box
726                #
727                self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
728    
729                self.num_cols = table.NumColumns()
730                # just assume the first field in case one hasn't been
731                # specified in the file.
732                self.__cur_field = 0
733    
734          #              self.fields.Append("<None>")
         # Control Box  
         #  
         controlBox = wxBoxSizer(wxHORIZONTAL)  
735    
736                if fieldType is None:
737                    self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
738                else:
739                    self.fields.SetClientData(0, None)
740    
741          ###########              for i in range(self.num_cols):
742          #                  name = table.Column(i).name
743          # Control buttons:                  self.fields.Append(name)
744          #  
745          self.controlButtons = []                  if name == field:
746                        self.__cur_field = i + 1
747                        self.fields.SetClientData(i + 1,
748                                                  copy.deepcopy(self.originalClass))
749                    else:
750                        self.fields.SetClientData(i + 1, None)
751    
752          controlButtonBox = wxBoxSizer(wxVERTICAL)              button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
753                    _("Generate Class"))
754                button_add = wxButton(panel, ID_PROPERTY_ADD,
755                    _("Add"))
756                button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
757                    _("Move Up"))
758                button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
759                    _("Move Down"))
760                button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
761                    _("Edit Symbol"))
762                button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
763                    _("Remove"))
764    
765                self.classGrid = ClassGrid(panel, self)
766    
767                # calling __SelectField after creating the classGrid fills in the
768                # grid with the correct information
769                self.fields.SetSelection(self.__cur_field)
770                self.__SelectField(self.__cur_field, group = group)
771    
772            button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))
773            button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))
774            button_ok = wxButton(self, wxID_OK, _("OK"))
775            button_close = wxButton(self, wxID_CANCEL, _("Close"))
776            button_ok.SetDefault()
777    
778          button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))          ############################
779          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          # Layout the controls
780          self.controlButtons.append(button)          #
781    
782          button = wxButton(panel, ID_CLASSIFY_EDITPROPS, _("Edit Properties"))          topBox = wxBoxSizer(wxVERTICAL)
783          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          panelBox = wxBoxSizer(wxVERTICAL)
         self.controlButtons.append(button)  
784    
785          button = wxButton(panel, ID_CLASSIFY_GENCLASS, _("Generate Class"))          sizer = wxBoxSizer(wxHORIZONTAL)
786          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          sizer.Add(wxStaticText(panel, -1, _("Title: ")),
787          self.controlButtons.append(button)              0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
788            sizer.Add(text_title, 1, wxGROW, 0)
789    
790          button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))          panelBox.Add(sizer, 0, wxGROW, 4)
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
791    
792          button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))          if isinstance(layer, RasterLayer):
793          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)              type = "Image"
794          self.controlButtons.append(button)          else:
795                type = layer.ShapeType()
796    
797          controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)          panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
798                0, wxALIGN_LEFT | wxALL, 4)
799    
800          button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))          if layer.HasClassification():
         controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)  
         self.controlButtons.append(button)  
801    
802                classBox = wxStaticBoxSizer(
803                            wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
804    
         ###########  
         #  
         # Classification data table  
         #  
805    
806          self.classGrid = ClassGrid(panel, self)              sizer = wxBoxSizer(wxHORIZONTAL)
807          #self.__SetGridTable(self.__cur_field, group)              sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
808          #self.fields.SetSelection(self.__cur_field)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
809                sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
810    
811          # calling __SelectField after creating the classGrid fills in the              classBox.Add(sizer, 0, wxGROW, 4)
         # grid with the correct information  
         self.fields.SetSelection(self.__cur_field)  
         self.__SelectField(self.__cur_field, group = group)  
812    
813          #self.classGrid.SelectGroup(group)              classBox.Add(self.fieldTypeText, 0,
814                            wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
815    
816          controlBox.Add(self.classGrid, 1, wxGROW, 0)              controlBox = wxBoxSizer(wxHORIZONTAL)
817                controlButtonBox = wxBoxSizer(wxVERTICAL)
818    
819                controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
820                controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
821                controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
822                controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
823                controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
824                controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
825                controlButtonBox.Add(button_remove, 0,
826                                     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
827    
828                controlBox.Add(self.classGrid, 1, wxGROW, 0)
829                controlBox.Add(controlButtonBox, 0, wxGROW, 10)
830    
831          controlBox.Add(controlButtonBox, 0, wxGROW, 10)              classBox.Add(controlBox, 1, wxGROW, 10)
832          panelBox.Add(controlBox, 1, wxGROW, 10)              panelBox.Add(classBox, 1, wxGROW, 0)
833    
         EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)  
         EVT_BUTTON(self, ID_CLASSIFY_EDITPROPS, self._OnEditGroupProperties)  
         EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)  
         EVT_BUTTON(self, ID_CLASSIFY_GENCLASS, self._OnGenClass)  
         EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)  
         EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)  
   
         ###########  
834    
835          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
836          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)
837                        0, wxALL, 4)          buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)
838          buttonBox.Add(60, 20, 0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
839          buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),          buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)
                       0, wxALL, 4)  
         buttonBox.Add(60, 20, 0, wxALL, 4)  
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_CLOSE, _("Close")),  
                       0, wxALL, 4)  
         buttonBox.Add(60, 20, 0, wxALL, 4)  
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_CANCEL, _("Cancel")),  
                       0, wxALL, 4)  
         panelBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)  
   
         EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)  
         EVT_BUTTON(self, ID_CLASSIFY_APPLY, self._OnApply)  
         EVT_BUTTON(self, ID_CLASSIFY_CLOSE, self._OnCloseBtn)  
         EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)  
   
         ###########  
   
840    
841          panel.SetAutoLayout(True)          panel.SetAutoLayout(True)
842          panel.SetSizer(panelBox)          panel.SetSizer(panelBox)
843            panelBox.Fit(panel)
844          panelBox.SetSizeHints(panel)          panelBox.SetSizeHints(panel)
845    
846          topBox.Add(panel, 1, wxGROW, 0)          topBox.Add(panel, 1, wxGROW | wxALL, 4)
847          panelBox.SetSizeHints(self)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
848    
849          self.SetAutoLayout(True)          self.SetAutoLayout(True)
850          self.SetSizer(topBox)          self.SetSizer(topBox)
851            topBox.Fit(self)
852            topBox.SetSizeHints(self)
853            self.Layout()
854    
855            ###########
856    
857            EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
858            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
859            EVT_BUTTON(self, wxID_OK, self._OnOK)
860            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
861            EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
862            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
863    
864            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
865            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
866            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
867            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
868            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
869            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
870    
         #self.Fit()  
871          ######################          ######################
872    
873            text_title.SetFocus()
874          self.haveApplied = False          self.haveApplied = False
875    
876      def EditGroupProperties(self, row):      def unsubscribe_messages(self):
877            """Unsubscribe from all messages."""
878            self.parent.Unsubscribe(MAP_REPLACED, self.map_replaced)
879            self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
880            self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
881                                   self.layer_shapestore_replaced)
882    
883        def map_layers_removed(self, map):
884            """Subscribed to MAP_LAYERS_REMOVED. If this layer was removed,
885            Close self.
886            """
887            if self.layer not in self.map.Layers():
888                self.Close()
889    
890        def layer_shapestore_replaced(self, *args):
891            """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
892    
893            Close self.
894            """
895            self.Close()
896    
897        def map_replaced(self, *args):
898            """Subscribed to the mainwindow's MAP_REPLACED message. Close self."""
899            self.Close()
900    
901        def EditSymbol(self, row):
902            """Open up a dialog where the user can select the properties
903            for a group.
904            """
905          table = self.classGrid.GetTable()          table = self.classGrid.GetTable()
906          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
907    
908          # get a new ClassGroupProperties object and copy the          # get a new ClassGroupProperties object and copy the
909          # values over to our current object          # values over to our current object
910          propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())          propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
911    
912          self.Enable(False)          self.Enable(False)
913          if propDlg.ShowModal() == wxID_OK:          if propDlg.ShowModal() == wxID_OK:
# Line 857  class Classifier(NonModalDialog): Line 915  class Classifier(NonModalDialog):
915              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
916          self.Enable(True)          self.Enable(True)
917          propDlg.Destroy()          propDlg.Destroy()
918            
919      def _SetClassification(self, clazz):      def _SetClassification(self, clazz):
920                    """Called from the ClassGen dialog when a new classification has
921            been created and should be set in the table.
922            """
923            # FIXME: This could be implemented using a message
924    
925          self.fields.SetClientData(self.__cur_field, clazz)          self.fields.SetClientData(self.__cur_field, clazz)
926          self.classGrid.GetTable().SetClassification(clazz)          self.classGrid.GetTable().SetClassification(clazz)
927    
928      def __BuildClassification(self, fieldIndex, copyClass = False):      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
929            """Pack the classification setting into a Classification object.
930            Returns (Classification, fieldName) where fieldName is the selected
931            field in the table that the classification should be used with.
932            """
933    
934  #       numRows = self.classGrid.GetNumberRows()  #       numRows = self.classGrid.GetNumberRows()
935  #       assert numRows > 0  # there should always be a default row  #       assert numRows > 0  # there should always be a default row
936    
 #       clazz = Classification()  
937          if fieldIndex == 0:          if fieldIndex == 0:
938              fieldName = None              fieldName = None
939              fieldType = None              fieldType = None
# Line 876  class Classifier(NonModalDialog): Line 941  class Classifier(NonModalDialog):
941              fieldName = self.fields.GetString(fieldIndex)              fieldName = self.fields.GetString(fieldIndex)
942              fieldType = self.layer.GetFieldType(fieldName)              fieldType = self.layer.GetFieldType(fieldName)
943    
944          clazz = self.classGrid.GetTable().GetClassification()          clazz = self.fields.GetClientData(fieldIndex)
945            if clazz is None or self.classGrid.GetTable().IsModified() or force:
946          if copyClass:              clazz = self.classGrid.GetTable().GetClassification()
947              clazz = copy.deepcopy(clazz)              if copyClass:
948                    clazz = copy.deepcopy(clazz)
         clazz.SetField(fieldName)  
         clazz.SetFieldType(fieldType)  
   
   
 #       table = self.classGrid.GetTable()  
 #       clazz.SetDefaultGroup(table.GetClassGroup(0))  
   
 #       for i in range(1, numRows):  
 #           clazz.AppendGroup(table.GetClassGroup(i))  
949    
950          return clazz          return clazz, fieldName
951    
952      def __SetGridTable(self, fieldIndex, group = None):      def __SetGridTable(self, fieldIndex, group = None):
953            """Set the table with the classification associated with the
954            selected field at fieldIndex. Select the specified group
955            if group is not None.
956            """
957    
958          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
959    
# Line 904  class Classifier(NonModalDialog): Line 964  class Classifier(NonModalDialog):
964                      self.layer.GetClassification().                      self.layer.GetClassification().
965                                 GetDefaultGroup().GetProperties()))                                 GetDefaultGroup().GetProperties()))
966    
967              fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
968              fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
             clazz.SetFieldType(fieldType)  
969                                    
970          self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)          self.classGrid.CreateTable(clazz, fieldType,
971                                       self.layer.ShapeType(), group)
972    
973      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
974            """Set the field type string using the data type of the field
975            at fieldIndex.
976            """
977          fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
978          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
979    
# Line 918  class Classifier(NonModalDialog): Line 981  class Classifier(NonModalDialog):
981    
982          text = Classifier.type2string[fieldType]          text = Classifier.type2string[fieldType]
983    
984          self.fieldTypeText.SetLabel(_("Field Type: %s") % text)          self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
985    
986      def __SelectField(self, newIndex, oldIndex = -1, group = None):      def __SelectField(self, newIndex, oldIndex = -1, group = None):
987          """This method assumes that the current selection for the          """This method assumes that the current selection for the
# Line 928  class Classifier(NonModalDialog): Line 991  class Classifier(NonModalDialog):
991          assert oldIndex >= -1          assert oldIndex >= -1
992    
993          if oldIndex != -1:          if oldIndex != -1:
994              clazz = self.__BuildClassification(oldIndex)              clazz, name = self.__BuildClassification(oldIndex, force = True)
995              self.fields.SetClientData(oldIndex, clazz)              self.fields.SetClientData(oldIndex, clazz)
996    
997          self.__SetGridTable(newIndex, group)          self.__SetGridTable(newIndex, group)
998    
999          enabled = newIndex != 0          self.__EnableButtons(EB_SELECT_FIELD)
   
         for b in self.controlButtons:  
             b.Enable(enabled)  
1000    
1001          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
1002    
1003        def __SetTitle(self, title):
1004            """Set the title of the dialog."""
1005            if title != "":
1006                title = ": " + title
1007    
1008            self.SetTitle(_("Layer Properties") + title)
1009    
1010      def _OnEditGroupProperties(self, event):      def _OnEditSymbol(self, event):
1011            """Open up a dialog for the user to select group properties."""
1012          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1013    
1014          if len(sel) == 1:          if len(sel) == 1:
1015              self.EditGroupProperties(sel[0])              self.EditSymbol(sel[0])
1016    
1017      def _OnFieldSelect(self, event):      def _OnFieldSelect(self, event):
1018          index = self.fields.GetSelection()          index = self.fields.GetSelection()
1019          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
1020          self.__cur_field = index          self.__cur_field = index
1021    
1022      def _OnApply(self, event):      def _OnTry(self, event):
1023          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
1024             it to the layer.             it to the layer.
1025          """          """
1026    
1027          clazz = self.fields.GetClientData(self.__cur_field)          if self.layer.HasClassification():
1028                clazz = self.fields.GetClientData(self.__cur_field)
1029    
1030          #              #
1031          # only build the classification if there wasn't one to              # only build the classification if there wasn't one to
1032          # to begin with or it has been modified              # to begin with or it has been modified
1033          #              #
1034          if clazz is None or self.classGrid.GetTable().IsModified():              self.classGrid.SaveEditControlValue()
1035              clazz = self.__BuildClassification(self.__cur_field, True)              clazz, name = self.__BuildClassification(self.__cur_field, True)
1036    
1037          self.layer.SetClassification(clazz)              self.layer.SetClassificationColumn(name)
1038                self.layer.SetClassification(clazz)
1039    
1040          self.haveApplied = True          self.haveApplied = True
1041    
1042      def _OnOK(self, event):      def _OnOK(self, event):
1043          self._OnApply(event)          self._OnTry(event)
1044          self.Close()          self.Close()
1045    
1046        def OnClose(self, event):
1047            self.unsubscribe_messages()
1048            NonModalNonParentDialog.OnClose(self, event)
1049    
1050      def _OnCloseBtn(self, event):      def _OnCloseBtn(self, event):
1051          """Close is similar to Cancel except that any changes that were          """Close is similar to Cancel except that any changes that were
1052          made and applied remain applied, but the currently displayed          made and applied remain applied, but the currently displayed
# Line 982  class Classifier(NonModalDialog): Line 1055  class Classifier(NonModalDialog):
1055    
1056          self.Close()          self.Close()
1057    
1058      def _OnCancel(self, event):      def _OnRevert(self, event):
1059          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
1060          if self.haveApplied:          if self.haveApplied:
1061                self.layer.SetClassificationColumn(self.originalClassField)
1062              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
1063    
1064          self.Close()          #self.Close()
1065    
1066      def _OnAdd(self, event):      def _OnAdd(self, event):
1067          self.classGrid.AppendRows()          self.classGrid.AppendRows()
# Line 996  class Classifier(NonModalDialog): Line 1070  class Classifier(NonModalDialog):
1070          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1071    
1072      def _OnGenClass(self, event):      def _OnGenClass(self, event):
1073            """Open up a dialog for the user to generate classifications."""
1074    
         #if self.genDlg is None:  
1075          self.genDlg = ClassGenDialog(self, self.layer,          self.genDlg = ClassGenDialog(self, self.layer,
1076                            self.fields.GetString(self.__cur_field))                            self.fields.GetString(self.__cur_field))
1077    
1078          EVT_CLOSE(self.genDlg, self._OnGenDialogClose)          EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1079    
1080          self.fields.Enable(False)          self.__EnableButtons(EB_GEN_CLASS)
         self.controlButtons[BTN_EDIT].Enable(False)  
         self.controlButtons[BTN_GEN].Enable(False)  
1081    
1082          self.genDlg.Show()          self.genDlg.Show()
         #if self.genDlg.ShowModal() == wxID_OK:  
         #    clazz = self.genDlg.GetClassification()  
         #    self.fields.SetClientData(self.__cur_field, clazz)  
         #    self.classGrid.GetTable().SetClassification(clazz)  
         #self.Enable(True)  
         #self.genDlg.Destroy()  
1083    
1084      def _OnGenDialogClose(self, event):      def _OnGenDialogClose(self, event):
1085            """Reenable buttons after the generate classification
1086            dialog is closed.
1087            """
1088          self.genDlg.Destroy()          self.genDlg.Destroy()
1089            self.genDlg = None
1090          self.fields.Enable(True)          self.__EnableButtons(EB_GEN_CLASS)
         self.controlButtons[BTN_EDIT].Enable(True)  
         self.controlButtons[BTN_GEN].Enable(True)  
1091    
1092      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1093            """When the user clicks MoveUp, try to move a group up one row."""
1094          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1095    
1096          if len(sel) == 1:          if len(sel) == 1:
# Line 1038  class Classifier(NonModalDialog): Line 1106  class Classifier(NonModalDialog):
1106                  self.classGrid.MakeCellVisible(i - 1, 0)                  self.classGrid.MakeCellVisible(i - 1, 0)
1107    
1108      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1109            """When the user clicks MoveDown, try to move a group down one row."""
1110          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1111    
1112          if len(sel) == 1:          if len(sel) == 1:
# Line 1052  class Classifier(NonModalDialog): Line 1121  class Classifier(NonModalDialog):
1121                  self.classGrid.SelectRow(i + 1)                  self.classGrid.SelectRow(i + 1)
1122                  self.classGrid.MakeCellVisible(i + 1, 0)                  self.classGrid.MakeCellVisible(i + 1, 0)
1123    
1124        def _OnTitleChanged(self, event):
1125            """Update the dialog title when the user changed the layer name."""
1126            obj = event.GetEventObject()
1127    
1128            self.layer.SetTitle(obj.GetValue())
1129            self.__SetTitle(self.layer.Title())
1130    
1131            self.__EnableButtons(EB_LAYER_TITLE)
1132    
1133        def __EnableButtons(self, case):
1134            """Helper method that enables/disables the appropriate buttons
1135            based on the case provided. Cases are constants beginning with EB_.
1136            """
1137    
1138            list = {wxID_OK                 : True,
1139                    wxID_CANCEL             : True,
1140                    ID_PROPERTY_ADD         : True,
1141                    ID_PROPERTY_MOVEUP      : True,
1142                    ID_PROPERTY_MOVEDOWN    : True,
1143                    ID_PROPERTY_REMOVE      : True,
1144                    ID_PROPERTY_SELECT      : True,
1145                    ID_PROPERTY_FIELDTEXT   : True,
1146                    ID_PROPERTY_GENCLASS    : True,
1147                    ID_PROPERTY_EDITSYM     : True}
1148    
1149            if case == EB_LAYER_TITLE:  
1150                if self.layer.Title() == "":
1151                    list[wxID_OK] = False
1152                    list[wxID_CANCEL] = False
1153    
1154            elif case == EB_SELECT_FIELD:
1155                if self.fields.GetSelection() == 0:
1156                    list[ID_PROPERTY_GENCLASS] = False
1157                    list[ID_PROPERTY_ADD] = False
1158                    list[ID_PROPERTY_MOVEUP] = False
1159                    list[ID_PROPERTY_MOVEDOWN] = False
1160                    list[ID_PROPERTY_REMOVE] = False
1161    
1162            elif case == EB_GEN_CLASS:
1163                if self.genDlg is not None:
1164                    list[ID_PROPERTY_SELECT] = False
1165                    list[ID_PROPERTY_FIELDTEXT] = False
1166                    list[ID_PROPERTY_GENCLASS] = False
1167    
1168            for id, enable in list.items():
1169                win = self.FindWindowById(id)
1170                if win:
1171                    win.Enable(enable)
1172    
 ID_SELPROP_OK = 4001  
 ID_SELPROP_CANCEL = 4002  
1173  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL = 4002
1174  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1175  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
# Line 1063  ID_SELPROP_STROKECLRTRANS = 4006 Line 1178  ID_SELPROP_STROKECLRTRANS = 4006
1178  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1179    
1180  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1181        """Dialog that allows the user to select group properties."""
1182    
1183      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1184            """Open the dialog with the initial prop properties and shapeType."""
1185    
1186          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1187                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1188    
# Line 1093  class SelectPropertiesDialog(wxDialog): Line 1211  class SelectPropertiesDialog(wxDialog):
1211          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1212    
1213          lineColorBox = wxBoxSizer(wxHORIZONTAL)          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1214          lineColorBox.Add(          button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1215              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),          button.SetFocus()
1216              1, wxALL | wxGROW, 4)          lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1217          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1218    
1219          lineColorBox.Add(          lineColorBox.Add(
# Line 1141  class SelectPropertiesDialog(wxDialog): Line 1259  class SelectPropertiesDialog(wxDialog):
1259          # Control buttons:          # Control buttons:
1260          #          #
1261          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1262          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),          button_ok = wxButton(self, wxID_OK, _("OK"))
1263                        0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1264          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1265                        0, wxALL, 4)                        0, wxRIGHT|wxEXPAND, 10)
1266          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1267                                                                                    
1268          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)          button_ok.SetDefault()
1269          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)  
1270                                                                                            #EVT_BUTTON(self, wxID_OK, self._OnOK)
1271            #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)
1276          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1277    
1278      def _OnOK(self, event):      def OnOK(self, event):
1279          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1280    
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 _OnSpin(self, event):
# Line 1166  class SelectPropertiesDialog(wxDialog): Line 1286  class SelectPropertiesDialog(wxDialog):
1286          self.previewWin.Refresh()          self.previewWin.Refresh()
1287    
1288      def __GetColor(self, cur):      def __GetColor(self, cur):
1289          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1290          if cur is not Color.Transparent:          dialog.SetColor(cur)
             dialog.GetColourData().SetColour(Color2wxColour(cur))  
1291    
1292          ret = None          ret = None
1293          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1294              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1295    
1296          dialog.Destroy()          dialog.Destroy()
1297    
1298          return ret          return ret
1299            
1300      def _OnChangeLineColor(self, event):      def _OnChangeLineColor(self, event):
1301          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1302          if clr is not None:          if clr is not None:
# Line 1185  class SelectPropertiesDialog(wxDialog): Line 1304  class SelectPropertiesDialog(wxDialog):
1304          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1305    
1306      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1307          self.prop.SetLineColor(Color.Transparent)          self.prop.SetLineColor(Transparent)
1308          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1309            
1310      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1311          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1312          if clr is not None:          if clr is not None:
# Line 1195  class SelectPropertiesDialog(wxDialog): Line 1314  class SelectPropertiesDialog(wxDialog):
1314          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1315    
1316      def _OnChangeFillColorTrans(self, event):      def _OnChangeFillColorTrans(self, event):
1317          self.prop.SetFill(Color.Transparent)          self.prop.SetFill(Transparent)
1318          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1319    
1320      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
# Line 1203  class SelectPropertiesDialog(wxDialog): Line 1322  class SelectPropertiesDialog(wxDialog):
1322    
1323    
1324  class ClassDataPreviewWindow(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1325        """A custom window that draws group properties using the correct shape."""
1326    
1327      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1328                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1329            """Draws the appropriate shape as specified with shapeType using
1330            prop properities.
1331            """
1332          if parent is not None:          if parent is not None:
1333              wxWindow.__init__(self, parent, id, (0, 0), size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1334              EVT_PAINT(self, self._OnPaint)              EVT_PAINT(self, self._OnPaint)
# Line 1234  class ClassDataPreviewWindow(wxWindow): Line 1357  class ClassDataPreviewWindow(wxWindow):
1357          self.previewer.Draw(dc, rect, self.prop, self.shapeType)          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1358    
1359  class ClassDataPreviewer:  class ClassDataPreviewer:
1360        """Class that actually draws a group property preview."""
1361    
1362      def Draw(self, dc, rect, prop, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1363            """Draw the property.
1364    
1365            returns: (w, h) as adapted extend if the drawing size
1366            exceeded the given rect. This can only be the case
1367            for point symbols. If the symbol fits the given rect,
1368            None is returned.
1369            """
1370    
1371          assert dc is not None          assert dc is not None
1372          assert isinstance(prop, ClassGroupProperties)          assert isinstance(prop, ClassGroupProperties)
# Line 1251  class ClassDataPreviewer: Line 1382  class ClassDataPreviewer:
1382              h = rect.GetHeight()              h = rect.GetHeight()
1383    
1384          stroke = prop.GetLineColor()          stroke = prop.GetLineColor()
1385          if stroke is Color.Transparent:          if stroke is Transparent:
1386              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1387          else:          else:
1388              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
# Line 1259  class ClassDataPreviewer: Line 1390  class ClassDataPreviewer:
1390                          wxSOLID)                          wxSOLID)
1391    
1392          stroke = prop.GetFill()          stroke = prop.GetFill()
1393          if stroke is Color.Transparent:          if stroke is Transparent:
1394              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1395          else:          else:
1396              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 1275  class ClassDataPreviewer: Line 1406  class ClassDataPreviewer:
1406    
1407          elif shapeType == SHAPETYPE_POINT:          elif shapeType == SHAPETYPE_POINT:
1408    
1409              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
1410                            (min(w, h) - prop.GetLineWidth())/2)              circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
1411                new_h = h
1412                new_w = w
1413                if h < circle_size: new_h = circle_size
1414                if w < circle_size: new_w = circle_size
1415                if new_h > h or new_w > w:
1416                    return (new_w, new_h)
1417    
1418          elif shapeType == SHAPETYPE_POLYGON:          elif shapeType == SHAPETYPE_POLYGON:
1419              dc.DrawRectangle(x, y, w, h)              dc.DrawRectangle(x, y, w, h)
1420    
1421            return None
1422    
1423  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1424        """A wrapper class that can be used to draw group properties in a
1425        grid table.
1426        """
1427    
1428      def __init__(self, shapeType):      def __init__(self, shapeType):
1429          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
# Line 1299  class ClassRenderer(wxPyGridCellRenderer Line 1441  class ClassRenderer(wxPyGridCellRenderer
1441                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1442    
1443          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1444              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)              new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
1445                                               self.shapeType)
1446                if new_size is not None:
1447                    (new_w, new_h) = new_size
1448                    grid.SetRowSize(row, new_h)
1449                    grid.SetColSize(col, new_h)
1450                    grid.ForceRefresh()
1451    
1452                    # now that we know the height, redraw everything
1453                    rect.SetHeight(new_h)
1454                    rect.SetWidth(new_w)
1455                    dc.DestroyClippingRegion()
1456                    dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1457                                         rect.GetWidth(), rect.GetHeight())
1458                    dc.SetPen(wxPen(wxLIGHT_GREY))
1459                    dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1460                    dc.DrawRectangle(rect.GetX(), rect.GetY(),
1461                                     rect.GetWidth(), rect.GetHeight())
1462                    self.previewer.Draw(dc, rect, data.GetProperties(),
1463                                        self.shapeType)
1464    
1465          if isSelected:          if isSelected:
1466              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
# Line 1312  class ClassRenderer(wxPyGridCellRenderer Line 1473  class ClassRenderer(wxPyGridCellRenderer
1473    
1474    
1475  class ClassGroupPropertiesCtrl(wxWindow, wxControl):  class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1476        """A custom window and control that draw a preview of group properties
1477        and can open a dialog to modify the properties if the user double-clicks
1478        it.
1479        """
1480    
1481      def __init__(self, parent, id, props, shapeType,      def __init__(self, parent, id, props, shapeType,
1482                   size = wxDefaultSize, style = 0):                   size = wxDefaultSize, style = 0):
1483    
1484          wxWindow.__init__(self, parent, id, size = size, style = style)          wxWindow.__init__(self, parent, id, size = size, style = style)
1485    
1486            self.parent = parent
1487    
1488          self.SetProperties(props)          self.SetProperties(props)
1489          self.SetShapeType(shapeType)          self.SetShapeType(shapeType)
1490          self.AllowEdit(True)          self.AllowEdit(True)
# Line 1356  class ClassGroupPropertiesCtrl(wxWindow, Line 1523  class ClassGroupPropertiesCtrl(wxWindow,
1523          self.Refresh()          self.Refresh()
1524    
1525      def AllowEdit(self, allow):      def AllowEdit(self, allow):
1526            """Allow/Disallow double-clicking on the control."""
1527          self.allowEdit = allow          self.allowEdit = allow
1528    
1529      def DoEdit(self):      def DoEdit(self):
1530            """Open the properties selector dialog."""
1531    
1532          if not self.allowEdit: return          if not self.allowEdit: return
1533    
1534          propDlg = SelectPropertiesDialog(NULL,          propDlg = SelectPropertiesDialog(self.parent,
1535                                           self.GetProperties(),                                           self.GetProperties(),
1536                                           self.GetShapeType())                                           self.GetShapeType())
1537    
# Line 1374  class ClassGroupPropertiesCtrl(wxWindow, Line 1544  class ClassGroupPropertiesCtrl(wxWindow,
1544    
1545      def _OnLeftDClick(self, event):      def _OnLeftDClick(self, event):
1546          self.DoEdit()          self.DoEdit()
1547    
1548    from Thuban.UI.mainwindow import layer_properties_dialogs
1549    layer_properties_dialogs.add(Layer, Classifier)
1550    layer_properties_dialogs.add(RasterLayer, Classifier)

Legend:
Removed from v.630  
changed lines
  Added in v.2376

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26