[GRASS-SVN] r55651 - in grass/trunk: gui/wxpython/gui_core scripts/v.db.join

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Apr 7 02:23:04 PDT 2013


Author: turek
Date: 2013-04-07 02:23:04 -0700 (Sun, 07 Apr 2013)
New Revision: 55651

Modified:
   grass/trunk/gui/wxpython/gui_core/forms.py
   grass/trunk/gui/wxpython/gui_core/gselect.py
   grass/trunk/scripts/v.db.join/v.db.join.py
Log:
gselect: added new widget ListCtrlComboPopup for multiple selection, v.db.join: added scolumns parameter using the widget

Modified: grass/trunk/gui/wxpython/gui_core/forms.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/forms.py	2013-04-06 20:43:23 UTC (rev 55650)
+++ grass/trunk/gui/wxpython/gui_core/forms.py	2013-04-07 09:23:04 UTC (rev 55651)
@@ -276,15 +276,15 @@
                     if map in cparams:
                         if not cparams[map]['dbInfo']:
                             cparams[map]['dbInfo'] = gselect.VectorDBInfo(map)
-                        self.data[win.InsertColumns] = { 'vector' : map, 'layer' : layer,
-                                                         'dbInfo' : cparams[map]['dbInfo'] }
+                        self.data[win.GetParent().InsertColumns] = { 'vector' : map, 'layer' : layer,
+                                                                     'dbInfo' : cparams[map]['dbInfo'] }
                 else: # table
                     if driver and db:
-                        self.data[win.InsertTableColumns] = { 'table' : pTable.get('value'),
-                                                              'driver' : driver,
-                                                              'database' : db }
+                        self.data[win.GetParent().InsertTableColumns] = { 'table' : pTable.get('value'),
+                                                                          'driver' : driver,
+                                                                          'database' : db }
                     elif pTable:
-                        self.data[win.InsertTableColumns] = { 'table'  : pTable.get('value') }
+                        self.data[win.GetParent().InsertTableColumns] = { 'table'  : pTable.get('value') }
             
             elif name == 'SubGroupSelect':
                 self.data[win.Insert] = { 'group' : p.get('value', '')}
@@ -1179,7 +1179,7 @@
                             selection.SetValue(value)
                         
                         formatSelector = True
-                        # A select.Select is a combobox with two children: a textctl and a popupwindow;
+                        # A gselect.Select is a combobox with two children: a textctl and a popupwindow;
                         # we target the textctl here
                         textWin = selection.GetTextCtrl()
                         p['wxId'] = [ textWin.GetId(), ]
@@ -1317,9 +1317,16 @@
                         elif prompt == 'dbcolumn':
                             win = gselect.ColumnSelect(parent = which_panel,
                                                        value = value,
-                                                       param = p)
-                            win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
-                            win.Bind(wx.EVT_TEXT,     self.OnSetValue)
+                                                       param = p,
+                                                       multiple =  p.get('multiple', False))
+                        
+                            # A gselect.ColumnSelect is a combobox with two children: a textctl and a popupwindow;
+                            # we target the textctl here
+                            textWin = win.GetTextCtrl()
+                            p['wxId'] = [ textWin.GetId(), ]
+                            
+                            textWin.Bind(wx.EVT_TEXT, self.OnSetValue)
+                            win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
 
                         elif prompt == 'location':
                             win = gselect.LocationSelect(parent = which_panel,

Modified: grass/trunk/gui/wxpython/gui_core/gselect.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/gselect.py	2013-04-06 20:43:23 UTC (rev 55650)
+++ grass/trunk/gui/wxpython/gui_core/gselect.py	2013-04-07 09:23:04 UTC (rev 55651)
@@ -6,6 +6,7 @@
 Classes:
  - gselect::Select
  - gselect::VectorSelect
+ - gselect::ListCtrlComboPopup
  - gselect::TreeCrtlComboPopup
  - gselect::VectorDBInfo
  - gselect::LayerSelect
@@ -25,7 +26,7 @@
  - gselect::CoordinatesSelect
  - gselect::SignatureSelect
 
-(C) 2007-2012 by the GRASS Development Team 
+(C) 2007-2013 by the GRASS Development Team 
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -33,7 +34,7 @@
 @author Michael Barton
 @author Martin Landa <landa.martin gmail.com>
 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
- at author Stepan Turek <stepan.turek seznam.cz> (CoordinatesSelect)
+ at author Stepan Turek <stepan.turek seznam.cz> (CoordinatesSelect, ListCtrlComboPopup)
 """
 
 import os
@@ -153,25 +154,16 @@
         
         return True
 
-class TreeCtrlComboPopup(wx.combo.ComboPopup):
-    """!Create a tree ComboBox for selecting maps and other GIS elements
-    in accessible mapsets within the current location
-    """
+class ListCtrlComboPopup(wx.combo.ComboPopup):
+    """!Create a list ComboBox using TreeCtrl with hidden root.
+    """    
     # overridden ComboPopup methods
     def Init(self):
         self.value = [] # for multiple is False -> len(self.value) in [0,1]
         self.curitem = None
         self.multiple = False
-        self.nmaps = 1
-        self.type = None
-        self.mapsets = None
         self.updateOnPopup = True
-        self.onPopup = None
-        self.fullyQualified = True
-        self.extraItems = dict()
-        
-        self.SetFilter(None)
-        
+
     def Create(self, parent):
         self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
                                    |wx.TR_HAS_BUTTONS
@@ -181,24 +173,24 @@
                                    |wx.TR_FULL_ROW_HIGHLIGHT)
         self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
         self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
-        self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.mapsetExpanded)
-        self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.mapsetCollapsed)
-        self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.mapsetActivated)
-        self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.mapsetSelected)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnExpanded)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnCollapsed)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated)
+        self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelected)
         self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
         
     # the following dummy handler are needed to keep tree events from propagating up to
     # the parent GIS Manager layer tree
-    def mapsetExpanded(self, event):
+    def OnExpanded(self, event):
         pass
 
-    def mapsetCollapsed(self, event):
+    def OnCollapsed(self, event):
         pass
 
-    def mapsetActivated(self, event):
+    def OnActivated(self, event):
         pass
 
-    def mapsetSelected(self, event):
+    def OnSelected(self, event):
         pass
     # end of dummy events
 
@@ -206,8 +198,140 @@
         return self.seltree
 
     def GetStringValue(self):
-        """!Get value as a string separated by commas"""
+        """!Get value as a string separated by commas
+        """
         return ','.join(self.value)
+
+    def SetStringValue(self, value):
+        # this assumes that item strings are unique...
+        root = self.seltree.GetRootItem()
+        if not root:
+            return
+        winValue = self.GetCombo().GetValue().strip(',')
+        self.value = []
+        if winValue:
+            self.value = winValue.split(',')
+
+    def OnPopup(self, force = False):
+        """!Limited only for first selected
+        """
+        if not force and not self.updateOnPopup:
+            return
+
+        # selects map starting according to written text
+        inputText = self.GetCombo().GetValue().strip()
+        if inputText:
+            root = self.seltree.GetRootItem()
+            match = self.FindItem(root, inputText, startLetters = True)
+            self.seltree.EnsureVisible(match)
+            self.seltree.SelectItem(match)
+
+    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
+        """!Reads UserSettings to get height (which was 200 in old implementation).
+        """
+        height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
+        return wx.Size(minWidth, min(height, maxHeight))
+
+    def FindItem(self, parentItem, text, startLetters = False):
+        """!Finds item with given name or starting with given text
+        """
+        startletters = startLetters
+        item, cookie = self.seltree.GetFirstChild(parentItem)
+        while wx.TreeItemId.IsOk(item):
+            if self.seltree.GetItemText(item) == text:
+                return item
+            if self.seltree.ItemHasChildren(item):
+                item = self.FindItem(item, text, startLetters = startletters)
+                if wx.TreeItemId.IsOk(item):
+                    return item
+            elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
+                return item
+            item, cookie = self.seltree.GetNextChild(parentItem, cookie)
+        return wx.TreeItemId()
+
+    def AddItem(self, value):
+        root = self.seltree.GetRootItem()
+        if not root:
+            root = self.seltree.AddRoot("<hidden root>")
+        self.seltree.AppendItem(root, text = value)
+
+    def OnKeyUp(self, event):
+        """!Enable to select items using keyboard
+        """
+        item = self.seltree.GetSelection()
+        if event.GetKeyCode() == wx.WXK_DOWN:
+            self.seltree.SelectItem(self.seltree.GetNextVisible(item))
+            
+        elif event.GetKeyCode() == wx.WXK_UP: 
+            itemPrev = self.seltree.GetPrevSibling(item)
+            self.seltree.SelectItem(itemPrev)
+
+        elif event.GetKeyCode() == wx.WXK_ESCAPE:
+            self.Dismiss()
+
+        elif event.GetKeyCode() == wx.WXK_RETURN:
+            self.seltree.SelectItem(item)
+            self.curitem = item
+            item_str = self.seltree.GetItemText(self.curitem)
+            if self.multiple:
+                if item_str not in self.value: 
+                    self.value.append(item_str)
+            else:   
+                self.value = [item_str]
+            self.Dismiss()
+
+    def OnMotion(self, evt):
+        """!Have the selection follow the mouse, like in a real combobox
+        """
+        item, flags = self.seltree.HitTest(evt.GetPosition())
+        if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
+            self.seltree.SelectItem(item)
+            self.curitem = item
+        evt.Skip()
+
+    def OnLeftDown(self, evt):
+        """!Do the combobox selection
+        """
+        if self.curitem is None:
+            return
+
+        item_str = self.seltree.GetItemText(self.curitem)
+        if self.multiple:
+            if item_str not in self.value: 
+                self.value.append(item_str)
+        else:
+            self.value = [item_str]
+        self.Dismiss()
+        
+        evt.Skip()
+
+    def SetData(self, **kargs):
+        """!Set object properties"""
+        if 'multiple' in kargs:
+            self.multiple = kargs['multiple']
+        if 'onPopup' in kargs:
+            self.onPopup = kargs['onPopup']
+
+    def DeleteAllItems(self):
+        """!Delete all items in popup"""
+        self.seltree.DeleteAllItems()
+
+class TreeCtrlComboPopup(ListCtrlComboPopup):
+    """!Create a tree ComboBox for selecting maps and other GIS elements
+    in accessible mapsets within the current location
+    """
+    # overridden ComboPopup methods
+    def Init(self):
+
+        ListCtrlComboPopup.Init(self)
+        self.nmaps = 1
+        self.type = None
+        self.mapsets = None
+        self.onPopup = None
+        self.fullyQualified = True
+        self.extraItems = dict()
+        
+        self.SetFilter(None)
     
     def SetFilter(self, filter):
         """!Set filter for GIS elements, see e.g. VectorSelect"""
@@ -225,14 +349,7 @@
  
         self.GetElementList(selected, exclude)
         
-        # selects map starting according to written text
-        inputText = self.GetCombo().GetValue().strip()
-        if inputText:
-            root = self.seltree.GetRootItem()
-            match = self.FindItem(root, inputText, startLetters = True)
-            self.seltree.EnsureVisible(match)
-            self.seltree.SelectItem(match)
-            
+        ListCtrlComboPopup.OnPopup(self, force)
       
     def GetElementList(self, elements = None, exclude = False):
         """!Get filtered list of GIS elements in accessible mapsets
@@ -253,23 +370,7 @@
                 self.seltree.SelectItem(item)
             except:
                 pass
-            
-    def SetStringValue(self, value):
-        # this assumes that item strings are unique...
-        root = self.seltree.GetRootItem()
-        if not root:
-            return
-        winValue = self.GetCombo().GetValue().strip(',')
-        self.value = []
-        if winValue:
-            self.value = winValue.split(',')
-        
-    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
-        """!Reads UserSettings to get height (which was 200 in old implementation).
-        """
-        height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
-        return wx.Size(minWidth, min(height, maxHeight))
-
+                    
     def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
         """!Get list of GIS elements in accessible mapsets and display as tree
         with all relevant elements displayed beneath each mapset branch
@@ -444,22 +545,6 @@
                         self.AddItem(elem, mapset = mapset, node = False, parent = node)
                 else:
                     self.AddItem(elem, mapset = mapset, node = False, parent = node)
-
-    def FindItem(self, parentItem, text, startLetters = False):
-        """!Finds item with given name or starting with given text"""
-        startletters = startLetters
-        item, cookie = self.seltree.GetFirstChild(parentItem)
-        while wx.TreeItemId.IsOk(item):
-            if self.seltree.GetItemText(item) == text:
-                return item
-            if self.seltree.ItemHasChildren(item):
-                item = self.FindItem(item, text, startLetters = startletters)
-                if wx.TreeItemId.IsOk(item):
-                    return item
-            elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
-                return item
-            item, cookie = self.seltree.GetNextChild(parentItem, cookie)
-        return wx.TreeItemId()
     
     def AddItem(self, value, mapset = None, node = True, parent = None):
         if not parent:
@@ -537,16 +622,7 @@
                         self.value = [fullName]
             
             self.Dismiss()
-        
-    def OnMotion(self, evt):
-        """!Have the selection follow the mouse, like in a real combobox
-        """
-        item, flags = self.seltree.HitTest(evt.GetPosition())
-        if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
-            self.seltree.SelectItem(item)
-            self.curitem = item
-        evt.Skip()
-
+    
     def OnLeftDown(self, evt):
         """!Do the combobox selection
         """
@@ -579,20 +655,17 @@
 
     def SetData(self, **kargs):
         """!Set object properties"""
+        ListCtrlComboPopup.SetData(self, **kargs)
         if 'type' in kargs:
             self.type = kargs['type']
             if self.type in ('stds', 'strds', 'str3ds', 'stvds'):
                 tgis.init()
         if 'mapsets' in kargs:
             self.mapsets = kargs['mapsets']
-        if 'multiple' in kargs:
-            self.multiple = kargs['multiple']
         if 'nmaps' in kargs:
             self.nmaps = kargs['nmaps']
         if 'updateOnPopup' in kargs:
             self.updateOnPopup = kargs['updateOnPopup']
-        if 'onPopup' in kargs:
-            self.onPopup = kargs['onPopup']
         if 'fullyQualified' in kargs:
             self.fullyQualified = kargs['fullyQualified']
         if 'extraItems' in kargs:
@@ -602,7 +675,7 @@
         """!Get element type
         """
         return self.type
-    
+
 class VectorDBInfo:
     """!Class providing information about attribute tables
     linked to a vector map"""
@@ -836,8 +909,8 @@
         
         self.SetItems(items)
         self.SetValue('')
-        
-class ColumnSelect(wx.ComboBox):
+
+class ColumnSelect(wx.combo.ComboCtrl):
     """!Creates combo box for selecting columns in the attribute table
     for a vector map.
 
@@ -847,21 +920,36 @@
     @param size window size
     @param vector vector map name
     @param layer layer number
+    @param multiple - True if it is possible to add multiple columns
     @param param parameters list (see menuform.py)
     @param **kwags wx.ComboBox parameters
     """
     def __init__(self, parent, id = wx.ID_ANY, value = '', 
                  size = globalvar.DIALOG_COMBOBOX_SIZE,
-                 vector = None, layer = 1, param = None, **kwargs):
+                 vector = None, layer = 1, multiple = False, 
+                 param = None, **kwargs):
         self.defaultValue = value
         self.param = param
         
-        super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
-        self.SetName("ColumnSelect")
-        
+        wx.combo.ComboCtrl.__init__(self, parent, id, size = size, **kwargs)
+        self.GetChildren()[0].SetName("ColumnSelect")
+        self.GetChildren()[0].type = type
+
+        self.tcp = ListCtrlComboPopup()
+        self.SetPopupControl(self.tcp)
+        self.tcp.SetData(multiple = multiple)
+
         if vector:
             self.InsertColumns(vector, layer)
-    
+        self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+     
+    def OnKeyUp(self, event):
+        """!Shows popupwindow if down arrow key is released"""
+        if event.GetKeyCode() == wx.WXK_DOWN and not self.IsPopupShown():
+            self.ShowPopup() 
+        else:
+            event.Skip()
+
     def InsertColumns(self, vector, layer, excludeKey = False, excludeCols = None, type = None, dbInfo = None):
         """!Insert columns for a vector attribute table into the columns combobox
 
@@ -896,8 +984,12 @@
                             pass
         except (KeyError, ValueError):
             columns = list()
-            
-        self.SetItems(columns)
+
+        # update list
+        self.tcp.DeleteAllItems()
+        for col in columns:
+            self.tcp.AddItem(col)
+
         self.SetValue(self.defaultValue)
         
         if self.param:
@@ -923,9 +1015,12 @@
         if ret:
             columns = ret.splitlines()
         
-        self.SetItems(columns)
+        # update list
+        self.tcp.DeleteAllItems()
         self.SetValue(self.defaultValue)
         
+        for col in columns:
+            self.tcp.AddItem(col)
         if self.param:
             value = self.param.get('value', '')
             if value != '' and value in columns:

Modified: grass/trunk/scripts/v.db.join/v.db.join.py
===================================================================
--- grass/trunk/scripts/v.db.join/v.db.join.py	2013-04-06 20:43:23 UTC (rev 55650)
+++ grass/trunk/scripts/v.db.join/v.db.join.py	2013-04-07 09:23:04 UTC (rev 55651)
@@ -40,7 +40,7 @@
 #% key: otable
 #% description: Other table name
 #% required: yes
-#% guidependency: ocolumn
+#% guidependency: ocolumn,scolumns
 #%end
 
 #%option G_OPT_DB_COLUMN
@@ -49,6 +49,13 @@
 #% required: yes
 #%end
 
+#%option G_OPT_DB_COLUMN
+#% key: scolumns
+#% multiple: yes
+#% required: no
+#% description: Subset of columns from the other table
+#%end
+
 import sys
 import os
 import string
@@ -60,12 +67,16 @@
     column = options['column']
     otable = options['otable']
     ocolumn = options['ocolumn']
-
+    if options['scolumns']:
+        scolumns = options['scolumns'].split(',')
+    else:
+        scolumns = None
+    
     f = grass.vector_layer_db(map, layer)
 
     maptable = f['table']
     database = f['database']
-    driver = f['driver']
+    driver   = f['driver']
 
     if driver == 'dbf':
 	grass.fatal(_("JOIN is not supported for tables stored in DBF format"))
@@ -73,17 +84,41 @@
     if not maptable:
 	grass.fatal(_("There is no table connected to this map. Unable to join any column."))
 
+    # check if column is in map table
     if not grass.vector_columns(map, layer).has_key(column):
-	grass.fatal(_("Column <%s> not found in table <%s> at layer <%s>") % (column, map, layer))
+	grass.fatal(_("Column <%s> not found in table <%s>") % (column, maptable))
 
+    # describe other table
     all_cols_ot = grass.db_describe(otable, driver = driver, database = database)['cols']
+
+    # check if ocolumn is on other table
+    if ocolumn not in [ocol[0] for ocol in all_cols_ot]:
+	grass.fatal(_("Column <%s> not found in table <%s>") % (ocolumn, otable))
+
+    # determine columns subset from other table
+    if not scolumns:
+        # select all columns from other table
+        cols_to_add = all_cols_ot
+    else:
+        cols_to_add = []
+    	# check if scolumns exists in the other table
+    	for scol in scolumns:
+            found = False
+            for col_ot in all_cols_ot:
+                if scol == col_ot[0]:
+                    found = True
+                    cols_to_add.append(col_ot)
+                    break
+            if not found:
+                grass.warning(_("Column <%s> not found in table <%s>.") % (scol, otable))
+    
     all_cols_tt = grass.vector_columns(map, int(layer)).keys()
 
     select = "SELECT $colname FROM $otable WHERE $otable.$ocolumn=$table.$column"
     template = string.Template("UPDATE $table SET $colname=(%s);" % select)
 
-    for col in all_cols_ot:
-	# Skip the vector column which is used for join
+    for col in cols_to_add:
+	# skip the vector column which is used for join
 	colname = col[0]
 	if colname == column:
 	    continue
@@ -95,7 +130,7 @@
 
 	colspec = "%s %s" % (colname, coltype)
 
-	# Add only the new column to the table
+	# add only the new column to the table
 	if colname not in all_cols_tt:
 	    if grass.run_command('v.db.addcolumn', map = map, columns = colspec, layer = layer) != 0:
 	        grass.fatal(_("Error creating column <%s>") % colname)
@@ -103,14 +138,15 @@
 	stmt = template.substitute(table = maptable, column = column,
 				   otable = otable, ocolumn = ocolumn,
 				   colname = colname)
-
+        grass.debug(stmt, 1)
         grass.verbose(_("Updating column <%s> of vector map <%s>...") % (colname, map))
 	if grass.write_command('db.execute', stdin = stmt, input = '-', database = database, driver = driver) != 0:
 	    grass.fatal(_("Error filling column <%s>") % colname)
 
-    # write cmd history:
+    # write cmd history
     grass.vector_history(map)
 
+    return 0
 if __name__ == "__main__":
     options, flags = grass.parser()
-    main()
+    sys.exit(main())



More information about the grass-commit mailing list