Skip to content

Commit

Permalink
wxGUI: Ask to remove lock when switching to another mapset from datac…
Browse files Browse the repository at this point in the history
…atalog and menu (#906)
  • Loading branch information
lindakladivova committed Aug 18, 2020
1 parent a4fd41e commit e391950
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 35 deletions.
49 changes: 25 additions & 24 deletions gui/wxpython/datacatalog/tree.py
Expand Up @@ -44,7 +44,8 @@
delete_mapsets_interactively,
delete_locations_interactively,
download_location_interactively,
delete_grassdb_interactively
delete_grassdb_interactively,
can_switch_mapset_interactive
)

from grass.pydispatch.signal import Signal
Expand Down Expand Up @@ -1259,30 +1260,30 @@ def OnEndDrag(self, node, event):
event.Veto()
return

def OnSwitchDbLocationMapset(self, event):
"""Switch to location and mapset"""
self._SwitchDbLocationMapset()

def _SwitchDbLocationMapset(self):
def OnSwitchMapset(self, event):
"""Switch to location and mapset"""
genv = gisenv()
# Distinguish when only part of db/location/mapset is changed.
if (
self.selected_grassdb[0].data['name'] == genv['GISDBASE']
and self.selected_location[0].data['name'] == genv['LOCATION_NAME']
):
self.changeMapset.emit(mapset=self.selected_mapset[0].data['name'])
elif self.selected_grassdb[0].data['name'] == genv['GISDBASE']:
self.changeLocation.emit(mapset=self.selected_mapset[0].data['name'],
location=self.selected_location[0].data['name'],
dbase=None)
else:
self.changeLocation.emit(mapset=self.selected_mapset[0].data['name'],
location=self.selected_location[0].data['name'],
dbase=self.selected_grassdb[0].data['name'])
self.UpdateCurrentDbLocationMapsetNode()
self.ExpandCurrentMapset()
self.RefreshItems()
grassdb = self.selected_grassdb[0].data['name']
location = self.selected_location[0].data['name']
mapset = self.selected_mapset[0].data['name']

if can_switch_mapset_interactive(self, grassdb, location, mapset):
# Switch to mapset in the same location
if (grassdb == genv['GISDBASE'] and location == genv['LOCATION_NAME']):
self.changeMapset.emit(mapset=mapset)
# Switch to mapset in the same grassdb
elif grassdb == genv['GISDBASE']:
self.changeLocation.emit(mapset=mapset,
location=location,
dbase=None)
# Switch to mapset in a different grassdb
else:
self.changeLocation.emit(mapset=mapset,
location=location,
dbase=grassdb)
self.UpdateCurrentDbLocationMapsetNode()
self.ExpandCurrentMapset()
self.RefreshItems()

def OnMetadata(self, event):
"""Show metadata of any raster/vector/3draster"""
Expand Down Expand Up @@ -1452,7 +1453,7 @@ def _popupMenuMapset(self):

item = wx.MenuItem(menu, wx.ID_ANY, _("&Switch mapset"))
menu.AppendItem(item)
self.Bind(wx.EVT_MENU, self.OnSwitchDbLocationMapset, item)
self.Bind(wx.EVT_MENU, self.OnSwitchMapset, item)
if (
self.selected_grassdb[0].data['name'] == genv['GISDBASE']
and self.selected_location[0].data['name'] == genv['LOCATION_NAME']
Expand Down
18 changes: 16 additions & 2 deletions gui/wxpython/lmgr/frame.py
Expand Up @@ -43,6 +43,9 @@

from grass.script import core as grass
from grass.script.utils import decode
from startup.guiutils import (
can_switch_mapset_interactive
)

from core.gcmd import RunCommand, GError, GMessage
from core.settings import UserSettings, GetDisplayVectSettings
Expand Down Expand Up @@ -1062,6 +1065,8 @@ def OnRunScript(self, event):
def OnChangeLocation(self, event):
"""Change current location"""
dlg = LocationDialog(parent=self)
gisenv = grass.gisenv()

if dlg.ShowModal() == wx.ID_OK:
location, mapset = dlg.GetValues()
dlg.Destroy()
Expand All @@ -1072,7 +1077,11 @@ def OnChangeLocation(self, event):
message=_(
"No location/mapset provided. Operation canceled."))
return # this should not happen
self.ChangeLocation(location, mapset)
if can_switch_mapset_interactive(self,
gisenv['GISDBASE'],
location,
mapset):
self.ChangeLocation(location, mapset)

def ChangeLocation(self, location, mapset, dbase=None):
if dbase:
Expand Down Expand Up @@ -1132,6 +1141,7 @@ def OnCreateMapset(self, event):
def OnChangeMapset(self, event):
"""Change current mapset"""
dlg = MapsetDialog(parent=self)
gisenv = grass.gisenv()

if dlg.ShowModal() == wx.ID_OK:
mapset = dlg.GetMapset()
Expand All @@ -1141,7 +1151,11 @@ def OnChangeMapset(self, event):
GError(parent=self,
message=_("No mapset provided. Operation canceled."))
return
self.ChangeMapset(mapset)
if can_switch_mapset_interactive(self,
gisenv['GISDBASE'],
gisenv['LOCATION_NAME'],
mapset):
self.ChangeMapset(mapset)

def ChangeMapset(self, mapset):
"""Change current mapset and update map display title"""
Expand Down
76 changes: 67 additions & 9 deletions gui/wxpython/startup/guiutils.py
Expand Up @@ -21,7 +21,11 @@

import grass.script as gs
from grass.script import gisenv
from grass.grassdb.checks import mapset_exists, location_exists
from grass.grassdb.checks import (
mapset_exists,
location_exists,
is_mapset_locked,
get_mapset_lock_info)
from grass.grassdb.create import create_mapset, get_default_mapset_name
from grass.grassdb.manage import (
delete_mapset,
Expand Down Expand Up @@ -646,14 +650,14 @@ def delete_grassdb_interactively(guiparent, 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
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:
Expand Down Expand Up @@ -692,6 +696,60 @@ def delete_grassdb_interactively(guiparent, grassdb):
return deleted


def can_switch_mapset_interactive(guiparent, grassdb, location, mapset):
"""
Checks if mapset is locked and offers to remove the lock file.
Returns True if user wants to switch to the selected mapset in spite of
removing lock. Returns False if a user wants to stay in the current
mapset or if an error was encountered.
"""
can_switch = True
mapset_path = os.path.join(grassdb, location, mapset)

if is_mapset_locked(mapset_path):
info = get_mapset_lock_info(mapset_path)
user = info['owner'] if info['owner'] else _('unknown')
lockpath = info['lockpath']
timestamp = info['timestamp']

dlg = wx.MessageDialog(
parent=guiparent,
message=_("User {user} is already running GRASS in selected mapset "
"<{mapset}>\n (file {lockpath} created {timestamp} "
"found).\n\n"
"Concurrent use not allowed.\n\n"
"Do you want to stay in the current mapset or remove "
".gislock and switch to selected mapset?"
).format(user=user,
mapset=mapset,
lockpath=lockpath,
timestamp=timestamp),
caption=_("Mapset is in use"),
style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
)
dlg.SetYesNoLabels("S&witch to selected mapset",
"S&tay in current mapset")
if dlg.ShowModal() == wx.ID_YES:
# Remove lockfile
try:
os.remove(lockpath)
except IOError as e:
wx.MessageBox(
parent=guiparent,
caption=_("Error when removing lock file"),
message=_("Unable to remove {lockpath}.\n\n Details: {error}."
).format(lockpath=lockpath,
error=e),
style=wx.OK | wx.ICON_ERROR | wx.CENTRE
)
can_switch = False
else:
can_switch = False
dlg.Destroy()
return can_switch


def import_file(guiparent, filePath):
"""Tries to import file as vector or raster.
Expand Down
19 changes: 19 additions & 0 deletions lib/python/grassdb/checks.py
Expand Up @@ -11,6 +11,7 @@


import os
import datetime
from pathlib import Path


Expand Down Expand Up @@ -106,6 +107,24 @@ def get_lockfile_if_present(database, location, mapset):
return None


def get_mapset_lock_info(mapset_path):
"""Get information about .gislock file.
Assumes lock file exists, use is_mapset_locked to find out.
Returns information as a dictionary with keys
'owner' (None if unknown), 'lockpath', and 'timestamp'.
"""
info = {}
lock_name = ".gislock"
info['lockpath'] = os.path.join(mapset_path, lock_name)
try:
info['owner'] = Path(info['lockpath']).owner()
except KeyError:
info['owner'] = None
info['timestamp'] = (datetime.datetime.fromtimestamp(
os.path.getmtime(info['lockpath']))).replace(microsecond=0)
return info


def can_start_in_mapset(mapset_path, ignore_lock=False):
"""Check if a mapset from a gisrc file is usable for new session"""
if not is_mapset_valid(mapset_path):
Expand Down

0 comments on commit e391950

Please sign in to comment.