Skip to content

Commit

Permalink
wxGUI: Replace startup screen by fallback session (#1400)
Browse files Browse the repository at this point in the history
* Startup screen code removed. Used only when 
* Either demo/default world location is used (first time user) or an XY location temporary location is used (fallback).
* Last mapset path is now noted in gisrc of the current session. Used in GUI to offer switch from fallback to locked.
* non_standard_startup and first_time_user in grassdb/checks.py.
* grassdb/config for special strings such as `<UNKNOWN>`.
* Fallback tmp location only for GUI. Default currently for both GUI and CLI (`--text`).
* Call set_mapset in every branch (all paths lead to the same series of checks and setup).
* This increases the need for refactoring of `main()` and needs removal of `--gtext`.
  • Loading branch information
lindakladivova committed Apr 13, 2021
1 parent 4730d76 commit 8fe1363
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 1,228 deletions.
2 changes: 1 addition & 1 deletion gui/wxpython/Makefile
Expand Up @@ -14,7 +14,7 @@ SRCFILES := $(wildcard icons/*.py scripts/*.py xml/*) \
mapswipe/*.py modules/*.py nviz/*.py psmap/*.py rdigit/*.py \
rlisetup/*.py startup/*.py timeline/*.py vdigit/*.py \
vnet/*.py web_services/*.py wxplot/*.py iscatt/*.py tplot/*.py photo2image/*.py image2target/*.py) \
gis_set.py gis_set_error.py wxgui.py README
wxgui.py README

DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \
$(patsubst %.py,$(DSTDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
Expand Down
43 changes: 39 additions & 4 deletions gui/wxpython/datacatalog/catalog.py
Expand Up @@ -26,10 +26,14 @@
from datacatalog.infomanager import DataCatalogInfoManager
from gui_core.wrap import Menu
from gui_core.forms import GUI
from grass.script import gisenv

from grass.pydispatch.signal import Signal

from grass.grassdb.checks import is_current_mapset_in_demolocation
from grass.grassdb.manage import split_mapset_path
from grass.grassdb.checks import (get_reason_id_mapset_not_usable,
is_fallback_session,
is_first_time_user)


class DataCatalog(wx.Panel):
Expand All @@ -55,7 +59,9 @@ def __init__(self, parent, giface=None, id=wx.ID_ANY,
self.tree.showNotification.connect(self.showNotification)

# infobar for data catalog
delay = 2000
self.infoBar = InfoBar(self)
self.giface.currentMapsetChanged.connect(self.dismissInfobar)

# infobar manager for data catalog
self.infoManager = DataCatalogInfoManager(infobar=self.infoBar,
Expand All @@ -65,9 +71,22 @@ def __init__(self, parent, giface=None, id=wx.ID_ANY,
# some layout
self._layout()

# show data structure infobar for first-time user with proper layout
if is_current_mapset_in_demolocation():
wx.CallLater(2000, self.showDataStructureInfo)
# show infobar for first-time user if applicable
if is_first_time_user():
# show data structure infobar for first-time user
wx.CallLater(delay, self.showDataStructureInfo)

# show infobar if last used mapset is not usable
if is_fallback_session():
# get reason why last used mapset is not usable
last_mapset_path = gisenv()["LAST_MAPSET_PATH"]
self.reason_id = get_reason_id_mapset_not_usable(last_mapset_path)
if self.reason_id in ("non-existent", "invalid", "different-owner"):
# show non-standard situation info
wx.CallLater(delay, self.showFallbackSessionInfo)
elif self.reason_id == "locked":
# show info allowing to switch to locked mapset
wx.CallLater(delay, self.showLockedMapsetInfo)

def _layout(self):
"""Do layout"""
Expand All @@ -85,12 +104,22 @@ def _layout(self):
def showDataStructureInfo(self):
self.infoManager.ShowDataStructureInfo(self.OnCreateLocation)

def showLockedMapsetInfo(self):
self.infoManager.ShowLockedMapsetInfo(self.OnSwitchToLastUsedMapset)

def showFallbackSessionInfo(self):
self.infoManager.ShowFallbackSessionInfo(self.reason_id)

def showImportDataInfo(self):
self.infoManager.ShowImportDataInfo(self.OnImportOgrLayers, self.OnImportGdalLayers)

def LoadItems(self):
self.tree.ReloadTreeItems()

def dismissInfobar(self):
if self.infoBar.IsShown():
self.infoBar.Dismiss()

def OnReloadTree(self, event):
"""Reload whole tree"""
self.LoadItems()
Expand Down Expand Up @@ -135,6 +164,12 @@ def OnDownloadLocation(self, event):
db_node, loc_node, mapset_node = self.tree.GetCurrentDbLocationMapsetNode()
self.tree.DownloadLocation(db_node)

def OnSwitchToLastUsedMapset(self, event):
"""Switch to last used mapset"""
last_mapset_path = gisenv()["LAST_MAPSET_PATH"]
grassdb, location, mapset = split_mapset_path(last_mapset_path)
self.tree.SwitchMapset(grassdb, location, mapset)

def OnImportGdalLayers(self, event):
"""Convert multiple GDAL layers to GRASS raster map layers"""
from modules.import_export import GdalImportDialog
Expand Down
59 changes: 53 additions & 6 deletions gui/wxpython/datacatalog/infomanager.py
Expand Up @@ -20,6 +20,7 @@
import wx

from grass.script import gisenv
from grass.grassdb.checks import get_mapset_owner


class DataCatalogInfoManager:
Expand All @@ -31,8 +32,10 @@ def __init__(self, infobar, giface):

def ShowDataStructureInfo(self, onCreateLocationHandler):
"""Show info about the data hierarchy focused on the first-time user"""
buttons = [("Create new Location", onCreateLocationHandler),
("Learn More", self._onLearnMore)]
buttons = [
(_("Create new Location"), onCreateLocationHandler),
(_("Learn more"), self._onLearnMore),
]
message = _(
"GRASS GIS helps you organize your data using Locations (projects) "
"which contain Mapsets (subprojects). All data in one Location is "
Expand All @@ -41,22 +44,66 @@ def ShowDataStructureInfo(self, onCreateLocationHandler):
"which uses WGS 84 (EPSG:4326). Consider creating a new Location with a CRS "
"specific to your area. You can do it now or anytime later from "
"the toolbar above."
).format(loc=gisenv()['LOCATION_NAME'])
).format(loc=gisenv()["LOCATION_NAME"])
self.infoBar.ShowMessage(message, wx.ICON_INFORMATION, buttons)

def ShowImportDataInfo(self, OnImportOgrLayersHandler, OnImportGdalLayersHandler):
"""Show info about the data import focused on the first-time user"""
buttons = [("Import vector data", OnImportOgrLayersHandler),
("Import raster data", OnImportGdalLayersHandler)]
buttons = [
(_("Import vector data"), OnImportOgrLayersHandler),
(_("Import raster data"), OnImportGdalLayersHandler),
]
message = _(
"You have successfully created a new Location {loc}. "
"Currently you are in its PERMANENT Mapset which is used for "
"storing your base maps to make them readily available in other "
"Mapsets. You can create new Mapsets for different tasks by right "
"clicking on the Location name.\n\n"
"To import data, go to the toolbar above or use the buttons below."
).format(loc=gisenv()['LOCATION_NAME'])
).format(loc=gisenv()["LOCATION_NAME"])
self.infoBar.ShowMessage(message, wx.ICON_INFORMATION, buttons)

def ShowFallbackSessionInfo(self, reason_id):
"""Show info when last used mapset is not usable"""
string = self._text_from_reason_id(reason_id)
message = _(
"{string} GRASS GIS has started in a temporary Location. "
"To continue, use Data Catalog below to switch to a different Location."
).format(
string=string,
)
self.infoBar.ShowMessage(message, wx.ICON_INFORMATION)

def ShowLockedMapsetInfo(self, OnSwitchMapsetHandler):
"""Show info when last used mapset is locked"""
last_used_mapset_path = gisenv()["LAST_MAPSET_PATH"]
buttons = [(_("Switch to last used mapset"), OnSwitchMapsetHandler)]
message = _(
"Last used mapset in path '{mapsetpath}' is currently in use. "
"GRASS GIS has started in a temporary Location. "
"To continue, use Data Catalog below to switch to a different Location "
"or remove lock file and switch to the last used mapset."
).format(mapsetpath=last_used_mapset_path)
self.infoBar.ShowMessage(message, wx.ICON_INFORMATION, buttons)

def _text_from_reason_id(self, reason_id):
""" Get string for infobar message based on the reason."""
last_used_mapset_path = gisenv()["LAST_MAPSET_PATH"]
reason = None
if reason_id == "non-existent":
reason = _(
"Last used mapset in path '{mapsetpath}' does not exist."
).format(mapsetpath=last_used_mapset_path)
elif reason_id == "invalid":
reason = _("Last used mapset in path '{mapsetpath}' is invalid.").format(
mapsetpath=last_used_mapset_path
)
elif reason_id == "different-owner":
owner = get_mapset_owner(last_used_mapset_path)
reason = _(
"Last used mapset in path '{mapsetpath}' has different owner {owner}."
).format(owner=owner, mapsetpath=last_used_mapset_path)
return reason

def _onLearnMore(self, event):
self._giface.Help(entry="grass_database")
18 changes: 11 additions & 7 deletions gui/wxpython/datacatalog/tree.py
Expand Up @@ -74,8 +74,7 @@
from grass.script import gisenv
from grass.grassdb.data import map_exists
from grass.grassdb.checks import (get_mapset_owner, is_mapset_locked,
is_different_mapset_owner,
is_current_mapset_in_demolocation)
is_different_mapset_owner, is_first_time_user)
from grass.exceptions import CalledModuleError


Expand Down Expand Up @@ -957,7 +956,7 @@ def CreateLocation(self, grassdb_node):
"""
Creates new location interactively and adds it to the tree and switch
to its new PERMANENT mapset.
If a user was in Demolocation, it shows data import infobar.
If a user is a first-time user, it shows data import infobar.
"""
grassdatabase, location, mapset = (
create_location_interactively(self, grassdb_node.data['name'])
Expand All @@ -968,14 +967,14 @@ def CreateLocation(self, grassdb_node):
element='location',
action='new')

# show data import infobar for first-time user with proper layout
if is_current_mapset_in_demolocation():
self.showImportDataInfo.emit()

# switch to PERMANENT mapset in newly created location
self.SwitchMapset(grassdatabase, location, mapset,
show_confirmation=True)

# show data import infobar for first-time user with proper layout
if is_first_time_user():
self.showImportDataInfo.emit()

def OnCreateLocation(self, event):
"""Create new location"""
self.CreateLocation(self.selected_grassdb[0])
Expand Down Expand Up @@ -1560,6 +1559,11 @@ def _updateAfterGrassdbChanged(self, action, element, grassdb, location, mapset=
location=location)
if node:
self._renameNode(node, newname)
elif element == 'grassdb':
if action == 'delete':
node = self.GetDbNode(grassdb=grassdb)
if node:
self.RemoveGrassDB(node)
elif element in ('raster', 'vector', 'raster_3d'):
# when watchdog is used, it watches current mapset,
# so we don't process any signals here,
Expand Down

0 comments on commit 8fe1363

Please sign in to comment.