From 0863580578fe34a7ffb8c5970775ce151e72f737 Mon Sep 17 00:00:00 2001 From: saket0187 Date: Tue, 29 Sep 2026 17:06:35 +0530 Subject: [PATCH 1/2] wxGUI: Store history region in g.region JSON format --- gui/wxpython/history/browser.py | 13 ++++++++++--- python/grass/grassdb/history.py | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 98db94cdfc8..626b71dfd8b 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -40,6 +40,10 @@ "status": _("Status:"), "mask2d": _("Mask 2D:"), "mask3d": _("Mask 3D:"), + "north": _("North:"), + "south": _("South:"), + "west": _("West:"), + "east": _("East:"), "n": _("North:"), "s": _("South:"), "w": _("West:"), @@ -166,7 +170,7 @@ def _general_info_filter(self, key, value): return key in filter_keys or ((key in {"mask2d", "mask3d"}) and value is True) def _region_settings_filter(self, key): - return key not in {"projection", "zone", "cells"} + return key not in {"crs", "projection", "zone", "cells"} def _updateGeneralInfoBox(self, command_info): """Update a static box for displaying general info about the command. @@ -320,12 +324,15 @@ def hideCommandInfo(self): def _get_current_region(self): """Get current computational region settings.""" - return gs.region() + if "n" in self.region_settings: + return gs.region() + return gs.parse_command("g.region", flags="p", format="json") def _get_history_region(self): """Get computational region settings of executed command.""" + parameters = {"north": "n", "south": "s", "west": "w", "east": "e"} return { - key: value + parameters.get(key, key): value for key, value in self.region_settings.items() if self._region_settings_filter(key) } diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index 5608f792274..3c3cba5e264 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -264,7 +264,9 @@ def get_initial_command_info(env_run): mask3d_name = f"RASTER3D_MASK@{env['MAPSET']}" # Computational region settings - region_settings = gs.region(env=env_run) + region_settings = gs.parse_command( + "g.region", flags="p", format="json", env=env_run + ) # Finalize the command info dictionary return { From 9f113c0187026a1a9ecc8f47564bfc9436e19875 Mon Sep 17 00:00:00 2001 From: saket0187 Date: Fri, 2 Oct 2026 19:37:47 +0530 Subject: [PATCH 2/2] wxGUI: Normalize history region, use Tools API --- gui/wxpython/history/browser.py | 41 ++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 626b71dfd8b..6f0817accc4 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -16,6 +16,7 @@ @author Tomas Zigo """ +import math from datetime import datetime import wx @@ -25,7 +26,7 @@ from history.tree import HistoryBrowserTree from icons.icon import MetaIcon -import grass.script as gs +from grass.tools import Tools from grass.grassdb import history @@ -44,10 +45,6 @@ "south": _("South:"), "west": _("West:"), "east": _("East:"), - "n": _("North:"), - "s": _("South:"), - "w": _("West:"), - "e": _("East:"), "nsres": _("North-south resolution:"), "ewres": _("East-west resolution:"), "rows": _("Number of rows:"), @@ -73,6 +70,18 @@ def make_label(key): return TRANSLATION_KEYS.get(key, "") +# Region keys in the shell format of g.region, which are also the names of the g.region parameters +REGION_KEYS = {"n": "north", "s": "south", "w": "west", "e": "east"} + + +def normalize_region(region): + """Return region settings with the keys used by g.region format=json. + + History entries recorded by older versions use the keys of the shell format. + """ + return {REGION_KEYS.get(key, key): value for key, value in region.items()} + + class HistoryInfoPanel(SP.ScrolledPanel): def __init__(self, parent, giface, title=("Command Info"), style=wx.TAB_TRAVERSAL): super().__init__(parent=parent, id=wx.ID_ANY, style=style) @@ -80,6 +89,7 @@ def __init__(self, parent, giface, title=("Command Info"), style=wx.TAB_TRAVERSA self.parent = parent self.giface = giface self.title = title + self.tools = Tools() self.region_settings = None @@ -217,7 +227,7 @@ def _updateRegionSettingsGrid(self, command_info): """ self.sizer_region_settings_grid.Clear(True) - self.region_settings = command_info["region"] + self.region_settings = normalize_region(command_info["region"]) idx = 0 for key, value in self.region_settings.items(): if self._region_settings_filter(key): @@ -252,9 +262,12 @@ def _updateRegionSettingsMatch(self): self.sizer_region_settings_match.Clear(True) # Region condition - history_region = self.region_settings + history_region = self._get_history_region() current_region = self._get_current_region() - region_matches = history_region == current_region + region_matches = all( + math.isclose(value, current_region[key], rel_tol=0, abs_tol=1e-8) + for key, value in history_region.items() + ) # Icon and button according to the condition if region_matches: @@ -324,23 +337,23 @@ def hideCommandInfo(self): def _get_current_region(self): """Get current computational region settings.""" - if "n" in self.region_settings: - return gs.region() - return gs.parse_command("g.region", flags="p", format="json") + return self.tools.g_region(flags="p", format="json").json def _get_history_region(self): """Get computational region settings of executed command.""" - parameters = {"north": "n", "south": "s", "west": "w", "east": "e"} return { - parameters.get(key, key): value + key: value for key, value in self.region_settings.items() if self._region_settings_filter(key) } def OnUpdateRegion(self, event): """Set current region to the region of executed command.""" + parameters = {value: key for key, value in REGION_KEYS.items()} history_region = self._get_history_region() - gs.run_command("g.region", **history_region) + self.tools.g_region( + **{parameters.get(key, key): value for key, value in history_region.items()} + ) self.giface.updateMap.emit(render=False, renderVector=False) self._updateRegionSettingsMatch() self.Layout()