Skip to content

Commit

Permalink
wxGUI/data: use watchdog to update tree when mapset is switched exter…
Browse files Browse the repository at this point in the history
…nally (cmdline) (#1174)

* wxGUI/data: use watchdog to update tree when mapset is switched externally (cmdline)
* add new mapset node if mapset is newly created
  • Loading branch information
petrasovaa committed Dec 7, 2021
1 parent 60e9746 commit 400bef3
Showing 1 changed file with 74 additions and 5 deletions.
79 changes: 74 additions & 5 deletions gui/wxpython/datacatalog/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
watchdog_used = True
try:
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
from watchdog.events import PatternMatchingEventHandler, FileSystemEventHandler
except ImportError:
watchdog_used = False
PatternMatchingEventHandler = object
FileSystemEventHandler = object


import wx
Expand Down Expand Up @@ -80,6 +81,7 @@


updateMapset, EVT_UPDATE_MAPSET = NewEvent()
currentMapsetChanged, EVT_CURRENT_MAPSET_CHANGED = NewEvent()


def getLocationTree(gisdbase, location, queue, mapsets=None, lazy=False):
Expand Down Expand Up @@ -143,6 +145,42 @@ def getLocationTree(gisdbase, location, queue, mapsets=None, lazy=False):
gscript.try_remove(tmp_gisrc_file)


class CurrentMapsetWatch(FileSystemEventHandler):
"""Monitors rc file to check if mapset has been changed.
In that case wx event is dispatched to event handler.
Needs to check timestamp, because the modified event is sent twice.
This assumes new instance of this class is started
whenever mapset is changed."""

def __init__(self, rcfile, mapset_path, event_handler):
FileSystemEventHandler.__init__(self)
self.event_handler = event_handler
self.mapset_path = mapset_path
self.rcfile_name = os.path.basename(rcfile)
self.modified_time = 0

def on_modified(self, event):
if (
not event.is_directory
and os.path.basename(event.src_path) == self.rcfile_name
):
timestamp = os.stat(event.src_path).st_mtime
if timestamp - self.modified_time < 0.5:
return
self.modified_time = timestamp
with open(event.src_path, "r") as f:
gisrc = {}
for line in f.readlines():
key, val = line.split(":")
gisrc[key.strip()] = val.strip()
new = os.path.join(
gisrc["GISDBASE"], gisrc["LOCATION_NAME"], gisrc["MAPSET"]
)
if new != self.mapset_path:
evt = currentMapsetChanged()
wx.PostEvent(self.event_handler, evt)


class MapWatch(PatternMatchingEventHandler):
"""Monitors file events (create, delete, move files) using watchdog
to inform about changes in current mapset. One instance monitors
Expand Down Expand Up @@ -327,7 +365,7 @@ def __init__(
self.parent = parent
self.contextMenu.connect(self.OnRightClick)
self.itemActivated.connect(self.OnDoubleClick)
self._giface.currentMapsetChanged.connect(self._updateAfterMapsetChanged)
self._giface.currentMapsetChanged.connect(self.UpdateAfterMapsetChanged)
self._giface.grassdbChanged.connect(self._updateAfterGrassdbChanged)
self._iconTypes = [
"grassdb",
Expand Down Expand Up @@ -387,6 +425,9 @@ def __init__(
EVT_UPDATE_MAPSET, lambda evt: self._onWatchdogMapsetReload(evt.src_path)
)
self.Bind(wx.EVT_IDLE, self._onUpdateMapsetWhenIdle)
self.Bind(
EVT_CURRENT_MAPSET_CHANGED, lambda evt: self._updateAfterMapsetChanged()
)
self.observer = None

def _resetSelectVariables(self):
Expand Down Expand Up @@ -691,6 +732,7 @@ def ScheduleWatchCurrentMapset(self):
to detect changes not captured by other means (e.g. from command line).
Schedules 3 watches (raster, vector, 3D raster).
If watchdog observers are active, it restarts the observers in current mapset.
Also schedules monitoring of rc file to detect mapset change.
"""
global watchdog_used
if not watchdog_used:
Expand All @@ -703,14 +745,21 @@ def ScheduleWatchCurrentMapset(self):
self.observer = Observer()

gisenv = gscript.gisenv()
mapset_path = os.path.join(
gisenv["GISDBASE"], gisenv["LOCATION_NAME"], gisenv["MAPSET"]
)
rcfile = os.environ["GISRC"]
self.observer.schedule(
CurrentMapsetWatch(rcfile, mapset_path, self),
os.path.dirname(rcfile),
recursive=False,
)
for element, directory in (
("raster", "cell"),
("vector", "vector"),
("raster_3d", "grid3"),
):
path = os.path.join(
gisenv["GISDBASE"], gisenv["LOCATION_NAME"], gisenv["MAPSET"], directory
)
path = os.path.join(mapset_path, directory)
if not os.path.exists(path):
try:
os.mkdir(path)
Expand Down Expand Up @@ -756,6 +805,15 @@ def _onWatchdogMapsetReload(self, event_path):
self._reloadMapsetNode(node)
self.RefreshNode(node, recursive=True)

def UpdateAfterMapsetChanged(self):
"""Wrapper around updating function called
after mapset was changed, as a handler of signal.
If watchdog is active, updating is skipped here
to avoid double updating.
"""
if not watchdog_used:
self._updateAfterMapsetChanged()

def GetDbNode(self, grassdb, location=None, mapset=None, map=None, map_type=None):
"""Returns node representing db/location/mapset/map or None if not found."""
grassdb_nodes = self._model.SearchNodes(name=grassdb, type="grassdb")
Expand Down Expand Up @@ -1973,6 +2031,17 @@ def _updateAfterGrassdbChanged(

def _updateAfterMapsetChanged(self):
"""Update tree after current mapset has changed"""
db_node, location_node, mapset_node = self.GetCurrentDbLocationMapsetNode()
# mapset is not in tree, create its node
if not mapset_node:
genv = gisenv()
if not location_node:
if not db_node:
self.InsertGrassDb(genv["GISDBASE"])
else:
self.InsertLocation(genv["LOCATION_NAME"], db_node)
else:
self.InsertMapset(genv["MAPSET"], location_node)
self.UpdateCurrentDbLocationMapsetNode()
self._reloadMapsetNode(self.current_mapset_node)
self.RefreshNode(self.current_mapset_node, recursive=True)
Expand Down

0 comments on commit 400bef3

Please sign in to comment.