Browse Source

wxGUI/datacatalog: add toolbar and implement two functions - reload whole tree or current mapset only (new feature)

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@68224 15284696-431f-4ddb-bdfa-cd5b030d7da7
Martin Landa 9 years ago
parent
commit
7242c7f598

+ 4 - 0
gui/wxpython/core/treemodel.py

@@ -132,6 +132,10 @@ class TreeModel(object):
         """Removes node."""
         """Removes node."""
         if node.parent:
         if node.parent:
             node.parent.children.remove(node)
             node.parent.children.remove(node)
+        else:
+            # node is root
+            for n in node.children:
+                node.children.remove(n)
 
 
     def SortChildren(self, node):
     def SortChildren(self, node):
         """Sorts children alphabetically based on label."""
         """Sorts children alphabetically based on label."""

+ 18 - 2
gui/wxpython/datacatalog/catalog.py

@@ -21,6 +21,7 @@ from core.gthread import gThread
 from core.debug import Debug
 from core.debug import Debug
 from datacatalog.tree import DataCatalogTree
 from datacatalog.tree import DataCatalogTree
 from core.utils import _
 from core.utils import _
+from datacatalog.toolbars import DataCatalogToolbar
 
 
 from grass.pydispatch.signal import Signal
 from grass.pydispatch.signal import Signal
 
 
@@ -36,7 +37,10 @@ class DataCatalog(wx.Panel):
         self.SetName("DataCatalog")
         self.SetName("DataCatalog")
         
         
         Debug.msg(1, "DataCatalog.__init__()")
         Debug.msg(1, "DataCatalog.__init__()")
-        
+
+        # toolbar
+        self.toolbar = DataCatalogToolbar(parent = self)
+
         # tree with layers
         # tree with layers
         self.tree = DataCatalogTree(self, giface=giface)
         self.tree = DataCatalogTree(self, giface=giface)
         self.thread = gThread()
         self.thread = gThread()
@@ -50,6 +54,9 @@ class DataCatalog(wx.Panel):
         """Do layout"""
         """Do layout"""
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer = wx.BoxSizer(wx.VERTICAL)
 
 
+        sizer.Add(item = self.toolbar, proportion = 0,
+                  flag = wx.EXPAND)          
+
         sizer.Add(item = self.tree.GetControl(), proportion = 1,
         sizer.Add(item = self.tree.GetControl(), proportion = 1,
                   flag = wx.EXPAND)          
                   flag = wx.EXPAND)          
         
         
@@ -61,10 +68,19 @@ class DataCatalog(wx.Panel):
     def LoadItems(self):
     def LoadItems(self):
         if self._loaded:
         if self._loaded:
             return
             return
-        
+
         self.thread.Run(callable=self.tree.InitTreeItems,
         self.thread.Run(callable=self.tree.InitTreeItems,
                         ondone=lambda event: self.LoadItemsDone())
                         ondone=lambda event: self.LoadItemsDone())
 
 
     def LoadItemsDone(self):
     def LoadItemsDone(self):
         self._loaded = True
         self._loaded = True
         self.tree.ExpandCurrentMapset()
         self.tree.ExpandCurrentMapset()
+
+    def OnReloadTree(self, event):
+        """Reload whole tree"""
+        self.tree.ReloadTreeItems()
+        self.tree.ExpandCurrentMapset()
+
+    def OnReloadCurrentMapset(self, event):
+        """Reload current mapset tree only"""
+        self.tree.ReloadCurrentMapset()

+ 19 - 3
gui/wxpython/datacatalog/frame.py

@@ -6,23 +6,25 @@
 Classes:
 Classes:
  - datacatalog::DataCatalogFrame
  - datacatalog::DataCatalogFrame
 
 
-(C) 2014-2015 by Tereza Fiedlerova, and the GRASS Development Team
+(C) 2014-2016 by Tereza Fiedlerova, and the GRASS Development Team
 
 
 This program is free software under the GNU General Public
 This program is free software under the GNU General Public
 License (>=v2). Read the file COPYING that comes with GRASS
 License (>=v2). Read the file COPYING that comes with GRASS
 for details.
 for details.
 
 
-@author Tereza Fiedlerova
+@author Tereza Fiedlerova (original author)
+@author Martin Landa <landa.martin gmail.com> (various improvements)
 """
 """
 
 
 import os
 import os
+import sys
 
 
 import wx
 import wx
 
 
 from core.utils import _
 from core.utils import _
 from core.globalvar import ICONDIR
 from core.globalvar import ICONDIR
 from datacatalog.tree import DataCatalogTree
 from datacatalog.tree import DataCatalogTree
-
+from datacatalog.toolbars import DataCatalogToolbar
 
 
 class DataCatalogFrame(wx.Frame):
 class DataCatalogFrame(wx.Frame):
     """Frame for testing purposes only."""
     """Frame for testing purposes only."""
@@ -35,6 +37,11 @@ class DataCatalogFrame(wx.Frame):
         self._giface = giface
         self._giface = giface
         self.panel = wx.Panel(self)
         self.panel = wx.Panel(self)
 
 
+        self.toolbar = DataCatalogToolbar(parent = self)
+        # workaround for http://trac.wxwidgets.org/ticket/13888
+        if sys.platform != 'darwin':
+            self.SetToolBar(self.toolbar)
+
         # tree
         # tree
         self.tree = DataCatalogTree(parent=self.panel, giface=self._giface)
         self.tree = DataCatalogTree(parent=self.panel, giface=self._giface)
         self.tree.InitTreeItems()
         self.tree.InitTreeItems()
@@ -74,3 +81,12 @@ class DataCatalogFrame(wx.Frame):
             self.Destroy()
             self.Destroy()
 
 
         event.Skip()
         event.Skip()
+
+    def OnReloadTree(self, event):
+        """Reload whole tree"""
+        self.tree.ReloadTreeItems()
+        self.tree.ExpandCurrentMapset()
+
+    def OnReloadCurrentMapset(self, event):
+        """Reload current mapset tree only"""
+        self.tree.ReloadCurrentMapset()

+ 48 - 0
gui/wxpython/datacatalog/toolbars.py

@@ -0,0 +1,48 @@
+"""
+@package datacatalog.toolbars
+
+@brief Data Catalog toolbars
+
+Classes:
+ - toolbars::DataCatalogToolbar(BaseToolbar)
+
+(C) 2016 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon import MetaIcon
+from core.utils import _
+
+icons = {
+    'reloadTree': MetaIcon(img='redraw', label=_("Reload GRASS locations")),
+    'reloadMapset': MetaIcon(img='reload', label=_("Reload current GRASS mapset only"))
+    }
+
+class DataCatalogToolbar(BaseToolbar):
+    """Main data catalog toolbar
+    """
+    def __init__(self, parent):
+        """Main toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+
+        self.InitToolbar(self._toolbarData())
+
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """Returns toolbar data (name, icon, handler)"""
+        # BaseIcons are a set of often used icons. It is possible
+        # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
+        return self._getToolbarData((("reloadTree", icons["reloadTree"],
+                                      self.parent.OnReloadTree),
+                                     ("reloadMapset", icons["reloadMapset"],
+                                      self.parent.OnReloadCurrentMapset)
+                                     ))

+ 69 - 20
gui/wxpython/datacatalog/tree.py

@@ -50,7 +50,7 @@ def getEnvironment(gisdbase, location, mapset):
     return tmp_gisrc_file, env
     return tmp_gisrc_file, env
 
 
 
 
-def getLocationTree(gisdbase, location, queue):
+def getLocationTree(gisdbase, location, queue, mapsets=[]):
     """Creates dictionary with mapsets, elements, layers for given location.
     """Creates dictionary with mapsets, elements, layers for given location.
     Returns tuple with the dictionary and error (or None)"""
     Returns tuple with the dictionary and error (or None)"""
     tmp_gisrc_file, env = getEnvironment(gisdbase, location, 'PERMANENT')
     tmp_gisrc_file, env = getEnvironment(gisdbase, location, 'PERMANENT')
@@ -59,7 +59,8 @@ def getLocationTree(gisdbase, location, queue):
     maps_dict = {}
     maps_dict = {}
     elements = ['raster', 'raster_3d', 'vector']
     elements = ['raster', 'raster_3d', 'vector']
     try:
     try:
-        mapsets = gscript.read_command('g.mapsets', flags='l', quiet=True, env=env).strip()
+        if not mapsets:
+            mapsets = gscript.read_command('g.mapsets', flags='l', quiet=True, env=env).strip()
     except CalledModuleError:
     except CalledModuleError:
         queue.put((maps_dict, _("Failed to read mapsets from location <{l}>.").format(l=location)))
         queue.put((maps_dict, _("Failed to read mapsets from location <{l}>.").format(l=location)))
         gscript.try_remove(tmp_gisrc_file)
         gscript.try_remove(tmp_gisrc_file)
@@ -175,16 +176,11 @@ class LocationMapTree(TreeView):
                     proc_list[i].join()
                     proc_list[i].join()
                     if error:
                     if error:
                         errors.append(error)
                         errors.append(error)
+
                     for key in sorted(maps.keys()):
                     for key in sorted(maps.keys()):
                         mapset_node = self._model.AppendNode(parent=location_nodes[i], label=key,
                         mapset_node = self._model.AppendNode(parent=location_nodes[i], label=key,
                                                              data=dict(type='mapset', name=key))
                                                              data=dict(type='mapset', name=key))
-                        for elem in maps[key]:
-                            if maps[key][elem]:
-                                element_node = self._model.AppendNode(parent=mapset_node, label=elem,
-                                                                      data=dict(type='element', name=elem))
-                                for layer in maps[key][elem]:
-                                    self._model.AppendNode(parent=element_node, label=layer,
-                                                           data=dict(type=elem, name=layer))
+                        self._populateMapsetItem(mapset_node, maps[key])
 
 
                 proc_count = 0
                 proc_count = 0
                 proc_list = []
                 proc_list = []
@@ -198,9 +194,55 @@ class LocationMapTree(TreeView):
         self.RefreshItems()
         self.RefreshItems()
 
 
     def InitTreeItems(self):
     def InitTreeItems(self):
-        """Create popup menu for layers"""
+        """Load locations, mapsets and layers in the tree."""
         raise NotImplementedError()
         raise NotImplementedError()
 
 
+    def ReloadTreeItems(self):
+        """Reload locations, mapsets and layers in the tree."""
+        self._model.RemoveNode(self._model.root)
+        self.RefreshNode(self._model.root)
+        self.InitTreeItems()
+
+    def ReloadCurrentMapset(self):
+        """Reload current mapset tree only."""
+        def get_first_child(node):
+            try:
+                child = mapsetItem.children[0]
+            except IndexError:
+                child = None
+            return child
+
+        locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
+        if not locationItem or not mapsetItem:
+            return
+
+        if mapsetItem.children:
+            node = get_first_child(mapsetItem)
+            while node:
+                self._model.RemoveNode(node)
+                node = get_first_child(mapsetItem)
+
+        q = Queue()
+        p = Process(target=getLocationTree,
+                    args=(self.gisdbase, locationItem.data['name'], q, mapsetItem.data['name']))
+        p.start()
+        maps, error = q.get()
+        if error:
+            raise CalledModuleError(e)
+
+        self._populateMapsetItem(mapsetItem, maps[mapsetItem.data['name']])
+        self.RefreshNode(mapsetItem)
+        self.RefreshItems()
+
+    def _populateMapsetItem(self, mapset_node, data):
+        for elem in data:
+            if data[elem]:
+                element_node = self._model.AppendNode(parent=mapset_node, label=elem,
+                                                      data=dict(type='element', name=elem))
+                for layer in data[elem]:
+                    self._model.AppendNode(parent=element_node, label=layer,
+                                           data=dict(type=elem, name=layer))
+
     def _popupMenuLayer(self):
     def _popupMenuLayer(self):
         """Create popup menu for layers"""
         """Create popup menu for layers"""
         raise NotImplementedError()
         raise NotImplementedError()
@@ -279,20 +321,27 @@ class LocationMapTree(TreeView):
         else:
         else:
             Debug.msg(1, "Location <%s> not found" % location)
             Debug.msg(1, "Location <%s> not found" % location)
 
 
-    def ExpandCurrentMapset(self):
-        """Expand current mapset"""
+    def GetCurrentLocationMapsetNode(self):
+        """Get current mapset node"""
         gisenv = gscript.gisenv()
         gisenv = gscript.gisenv()
         location = gisenv['LOCATION_NAME']
         location = gisenv['LOCATION_NAME']
         mapset = gisenv['MAPSET']
         mapset = gisenv['MAPSET']
         locationItem = self._model.SearchNodes(name=location, type='location')
         locationItem = self._model.SearchNodes(name=location, type='location')
-        mapsetItem = None
-        if locationItem:
-            mapsetItem = self._model.SearchNodes(parent=locationItem[0], name=mapset, type='mapset')
+        if not locationItem:
+            return None, None
+
+        mapsetItem = self._model.SearchNodes(parent=locationItem[0], name=mapset, type='mapset')
+        if not mapsetItem:
+            return locationItem[0], None
+
+        return locationItem[0], mapsetItem[0]
+            
+    def ExpandCurrentMapset(self):
+        """Expand current mapset"""
+        locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
         if mapsetItem:
         if mapsetItem:
-            self.Select(mapsetItem[0], select=True)
-            self.ExpandNode(mapsetItem[0], recursive=True)
-        else:
-            Debug.msg(1, "Mapset <%s> not found" % mapset)
+            self.Select(mapsetItem, select=True)
+            self.ExpandNode(mapsetItem, recursive=True)
 
 
 class DataCatalogTree(LocationMapTree):
 class DataCatalogTree(LocationMapTree):
     def __init__(self, parent, giface=None):
     def __init__(self, parent, giface=None):
@@ -336,7 +385,7 @@ class DataCatalogTree(LocationMapTree):
     def InitTreeItems(self):
     def InitTreeItems(self):
         """Add locations, mapsets and layers to the tree."""
         """Add locations, mapsets and layers to the tree."""
         self._initTreeItems()
         self._initTreeItems()
-
+        
     def OnCopy(self, event):
     def OnCopy(self, event):
         """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
         """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
         self.copy_layer = self.selected_layer
         self.copy_layer = self.selected_layer