Jelajahi Sumber

wxGUI/datacatalog: Store list of databases in settings (#858)

Functions for removing and deleting grassdb added. Grassdb path are now stored into UserSettings. Function InsertGrassDb edited to add only grassdb which does not currently exist in the data catalogue.


Co-authored-by: Anna Petrasova <kratochanna@gmail.com>
Linda Kladivova 4 tahun lalu
induk
melakukan
0d8ea927c9

+ 9 - 0
gui/wxpython/core/settings.py

@@ -113,6 +113,15 @@ class Settings:
                     },
                 },
             },
+            #
+            # datacatalog
+            #
+            'datacatalog': {
+                # grassdb string
+                'grassdbs': {
+                    'listAsString': ""
+                },
+            },
             'manager': {
                 # show opacity level widget
                 'changeOpacityLevel': {

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

@@ -93,13 +93,12 @@ class DataCatalog(wx.Panel):
         self.tree.ReloadCurrentMapset()
 
     def OnAddGrassDB(self, event):
-        """Add an existing grass database"""
+        """Add grass database"""
         dlg = wx.DirDialog(self, _("Choose GRASS data directory:"),
                            os.getcwd(), wx.DD_DEFAULT_STYLE)
         if dlg.ShowModal() == wx.ID_OK:
             grassdatabase = dlg.GetPath()
             self.tree.InsertGrassDb(name=grassdatabase)
-
         dlg.Destroy()
 
     def OnCreateMapset(self, event):

+ 1 - 2
gui/wxpython/datacatalog/frame.py

@@ -110,13 +110,12 @@ class DataCatalogFrame(wx.Frame):
         self.tree.ReloadCurrentMapset()
 
     def OnAddGrassDB(self, event):
-        """Add an existing grass database"""
+        """Add grass database"""
         dlg = wx.DirDialog(self, _("Choose GRASS data directory:"),
                            os.getcwd(), wx.DD_DEFAULT_STYLE)
         if dlg.ShowModal() == wx.ID_OK:
             grassdatabase = dlg.GetPath()
             self.tree.InsertGrassDb(name=grassdatabase)
-
         dlg.Destroy()
 
     def OnCreateMapset(self, event):

+ 94 - 8
gui/wxpython/datacatalog/tree.py

@@ -18,6 +18,7 @@ for details.
 @author Anna Petrasova (kratochanna gmail com)
 @author Linda Kladivova (l.kladivova@seznam.cz)
 """
+import os
 import re
 import copy
 from multiprocessing import Process, Queue, cpu_count
@@ -34,6 +35,7 @@ from gui_core.treeview import TreeView
 from gui_core.wrap import Menu
 from datacatalog.dialogs import CatalogReprojectionDialog
 from icons.icon import MetaIcon
+from core.settings import UserSettings
 from startup.guiutils import (
     create_mapset_interactively,
     create_location_interactively,
@@ -42,6 +44,7 @@ from startup.guiutils import (
     delete_mapsets_interactively,
     delete_locations_interactively,
     download_location_interactively,
+    delete_grassdb_interactively
 )
 
 from grass.pydispatch.signal import Signal
@@ -254,8 +257,13 @@ class DataCatalogTree(TreeView):
         self._initVariablesCatalog()
         self.UpdateCurrentDbLocationMapsetNode()
 
-        self.grassdatabases = []
-        self.grassdatabases.append(gisenv()['GISDBASE'])
+        # Get databases from settings
+        # add current to settings if it's not included
+        self.grassdatabases = self._getValidSavedGrassDBs()
+        currentDB = gisenv()['GISDBASE']
+        if currentDB not in self.grassdatabases:
+            self.grassdatabases.append(currentDB)
+            self._saveGrassDBs()
 
         self.beginDrag = Signal('DataCatalogTree.beginDrag')
         self.endDrag = Signal('DataCatalogTree.endDrag')
@@ -276,6 +284,29 @@ class DataCatalogTree(TreeView):
         self.startEdit.connect(self.OnStartEditLabel)
         self.endEdit.connect(self.OnEditLabel)
 
+    def _getValidSavedGrassDBs(self):
+        """Returns list of GRASS databases from settings.
+        Returns only existing directories."""
+        dbs = UserSettings.Get(group='datacatalog',
+                               key='grassdbs',
+                               subkey='listAsString')
+        dbs = [db for db in dbs.split(',') if os.path.isdir(db)]
+        return dbs
+
+    def _saveGrassDBs(self):
+        """Save current grass dbs in tree to settings"""
+        UserSettings.Set(group='datacatalog',
+                         key='grassdbs',
+                         subkey='listAsString',
+                         value=",".join(self.grassdatabases))
+        grassdbSettings = {}
+        UserSettings.ReadSettingsFile(settings=grassdbSettings)
+        if 'datacatalog' not in grassdbSettings:
+            grassdbSettings['datacatalog'] = UserSettings.Get(group='datacatalog')
+        # update only dbs
+        grassdbSettings['datacatalog']['grassdbs'] = UserSettings.Get(group='datacatalog', key='grassdbs')
+        UserSettings.SaveToFile(grassdbSettings)
+
     def _reloadMapsetNode(self, mapset_node):
         """Recursively reload the model of a specific mapset node"""
         if mapset_node.children:
@@ -921,12 +952,21 @@ class DataCatalogTree(TreeView):
         return location_node
 
     def InsertGrassDb(self, name):
-        """Insert new grass db into model and refresh tree"""
-        self.grassdatabases.append(name)
-        grassdb_node = self._model.AppendNode(parent=self._model.root,
-                                              data=dict(type="grassdb", name=name))
-        self._reloadGrassDBNode(grassdb_node)
-        self.RefreshItems()
+        """
+        Insert new grass db into model, update user setting and refresh tree.
+        Check if not already added.
+        """
+        grassdb_node = self._model.SearchNodes(name=name,
+                                                   type='grassdb')
+        if not grassdb_node:
+            grassdb_node = self._model.AppendNode(parent=self._model.root,
+                                                  data=dict(type="grassdb", name=name))
+            self._reloadGrassDBNode(grassdb_node)
+            self.RefreshItems()
+
+            # Update user's settings
+            self.grassdatabases.append(name)
+            self._saveGrassDBs()
         return grassdb_node
 
     def OnDeleteMap(self, event):
@@ -1019,6 +1059,38 @@ class DataCatalogTree(TreeView):
         """
         self.DownloadLocation(self.selected_grassdb[0])
 
+    def DeleteGrassDb(self, grassdb_node):
+        """
+        Delete grassdb from disk.
+        """
+        grassdb = grassdb_node.data['name']
+        if (delete_grassdb_interactively(self, grassdb)):
+            self.RemoveGrassDB(grassdb_node)
+
+    def OnDeleteGrassDb(self, event):
+        """
+        Delete grassdb from disk.
+        """
+        self.DeleteGrassDb(self.selected_grassdb[0])
+
+    def OnRemoveGrassDb(self, event):
+        """
+        Remove grassdb node from data catalogue.
+        """
+        self.RemoveGrassDB(self.selected_grassdb[0])
+
+    def RemoveGrassDB(self, grassdb_node):
+        """
+        Remove grassdb node from tree
+        and updates settings. Doesn't check if it's current db.
+        """
+        self.grassdatabases.remove(grassdb_node.data['name'])
+        self._model.RemoveNode(grassdb_node)
+        self.RefreshItems()
+
+        # Update user's settings
+        self._saveGrassDBs()
+
     def OnDisplayLayer(self, event):
         """
         Display layer in current graphics view
@@ -1332,6 +1404,8 @@ class DataCatalogTree(TreeView):
     def _popupMenuGrassDb(self):
         """Create popup menu for grass db"""
         menu = Menu()
+        genv = gisenv()
+        currentGrassDb, currentLocation, currentMapset = self._isCurrent(genv)
 
         item = wx.MenuItem(menu, wx.ID_ANY, _("&Create new location"))
         menu.AppendItem(item)
@@ -1341,6 +1415,18 @@ class DataCatalogTree(TreeView):
         menu.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnDownloadLocation, item)
 
+        item = wx.MenuItem(menu, wx.ID_ANY, _("&Remove GRASS database from data catalog"))
+        menu.AppendItem(item)
+        self.Bind(wx.EVT_MENU, self.OnRemoveGrassDb, item)
+        if currentGrassDb:
+            item.Enable(False)
+
+        item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete GRASS database from disk"))
+        menu.AppendItem(item)
+        self.Bind(wx.EVT_MENU, self.OnDeleteGrassDb, item)
+        if currentGrassDb:
+            item.Enable(False)
+
         self.PopupMenu(menu)
         menu.Destroy()
 

+ 69 - 0
gui/wxpython/startup/guiutils.py

@@ -26,6 +26,7 @@ from grass.grassdb.create import create_mapset, get_default_mapset_name
 from grass.grassdb.manage import (
     delete_mapset,
     delete_location,
+    delete_grassdb,
     rename_mapset,
     rename_location,
 )
@@ -623,6 +624,74 @@ def delete_locations_interactively(guiparent, locations):
     return modified
 
 
+def delete_grassdb_interactively(guiparent, grassdb):
+    """
+    Delete grass database if could be deleted.
+
+    If current grass database found, desired operation cannot be performed.
+
+    Exceptions during deleting are handled in this function.
+
+    Returns True if grass database is deleted from the disk. Returns None if
+    cannot be deleted (see above the possible reasons).
+    """
+
+    genv = gisenv()
+    issue = None
+    deleted = False
+
+    # Check for current grassdb
+    if (grassdb == genv['GISDBASE']):
+        issue = _("<{}> is current GRASS database.").format(grassdb)
+
+    if issue:
+        dlg = wx.MessageDialog(
+        parent=guiparent,
+        message=_(
+            "Cannot delete GRASS database from disk for the following reason:\n\n"
+            "{}\n\n"
+            "GRASS database will not be deleted."
+        ).format(issue),
+        caption=_("Unable to delete selected GRASS database"),
+        style=wx.OK | wx.ICON_WARNING
+        )
+        dlg.ShowModal()
+    else:
+        dlg = wx.MessageDialog(
+            parent=guiparent,
+            message=_(
+                "Do you want to delete"
+                " the following GRASS database from disk?\n\n"
+                "{}\n\n"
+                "The directory will be permanently deleted!"
+            ).format(grassdb),
+            caption=_("Delete selected GRASS database"),
+            style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
+        )
+        if dlg.ShowModal() == wx.ID_YES:
+            try:
+                delete_grassdb(grassdb)
+                deleted = True
+                dlg.Destroy()
+                return deleted
+            except OSError as error:
+                wx.MessageBox(
+                    parent=guiparent,
+                    caption=_("Error when deleting GRASS database"),
+                    message=_(
+                        "The following error occured when deleting database <{path}>:"
+                        "\n\n{error}\n\n"
+                        "Deleting of GRASS database was interrupted."
+                    ).format(
+                        path=grassdb,
+                        error=error,
+                    ),
+                    style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
+                )
+    dlg.Destroy()
+    return deleted
+
+
 def import_file(guiparent, filePath):
     """Tries to import file as vector or raster.
 

+ 5 - 0
lib/python/grassdb/manage.py

@@ -28,6 +28,11 @@ def delete_location(database, location):
     shutil.rmtree(os.path.join(database, location))
 
 
+def delete_grassdb(database):
+    """Deletes a specified GRASS database"""
+    shutil.rmtree(database)
+
+
 def rename_mapset(database, location, old_name, new_name):
     """Rename mapset from *old_name* to *new_name*"""
     if old_name == "PERMANENT":