From ae826ab228583903311f0476b1bd3cadff14c7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Mon, 30 Mar 2026 21:04:13 +0200 Subject: [PATCH 01/13] fix: Correct variable replacement --- gui/wxpython/gmodeler/model_convert.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/gmodeler/model_convert.py b/gui/wxpython/gmodeler/model_convert.py index 9c205e870fd..d9abf435009 100644 --- a/gui/wxpython/gmodeler/model_convert.py +++ b/gui/wxpython/gmodeler/model_convert.py @@ -969,14 +969,15 @@ def _substitutePythonParamValue( # check for variables formattedVar = False for var in variables["vars"]: - pattern = re.compile("%{" + var + "}") - found = pattern.search(value) + # curly braces are optional + pattern = re.compile("%{?" + var + "}?") + found = pattern.search(parameterizedValue) if found: foundVar = True if found.end() != len(value): formattedVar = True parameterizedValue = pattern.sub( - "{options['" + var + "']}", value + "{options['" + var + "']}", parameterizedValue ) else: parameterizedValue = f'options["{var}"]' From 4c70abcfefc1bde3601269ae400b79499e3bedff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 15 Apr 2026 17:46:29 +0200 Subject: [PATCH 02/13] wxGUI/gmodeler: backward compatibility for curly braces --- gui/wxpython/gmodeler/model.py | 9 ++++++--- gui/wxpython/gmodeler/model_convert.py | 3 ++- gui/wxpython/gmodeler/model_items.py | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 1d75f3869df..57cdaa03dba 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -539,7 +539,8 @@ def _substituteFile(self, item, params=None, checkOnly=False): write = False variables = self.GetVariables() for variable in variables: - pattern = re.compile("%{" + variable + "}") + # curly braces are optional + pattern = re.compile("%{?" + variable + "}?") value = "" if params and "variables" in params: for p in params["variables"]["params"]: @@ -690,7 +691,8 @@ def Run(self, log, onDone, parent=None): # substitute variables in condition variables = self.GetVariables() for variable in variables: - pattern = re.compile("%{" + variable + "}") + # curly braces are optional + pattern = re.compile("%{?" + variable + "}?") if not pattern.search(cond): continue value = "" @@ -713,7 +715,8 @@ def Run(self, log, onDone, parent=None): # split condition # TODO: this part needs some better solution condVar, condText = (x.strip() for x in re.split(r"\s* in \s*", cond)) - pattern = re.compile("%{" + condVar + "}") + # curly braces are optional + pattern = re.compile("%{?" + condVar + "}?") # for vars()[condVar] in eval(condText): ? vlist = [] if condText[0] == "`" and condText[-1] == "`": diff --git a/gui/wxpython/gmodeler/model_convert.py b/gui/wxpython/gmodeler/model_convert.py index 7b7377a8607..0ad25a26577 100644 --- a/gui/wxpython/gmodeler/model_convert.py +++ b/gui/wxpython/gmodeler/model_convert.py @@ -56,7 +56,8 @@ def _writeItem(self, item, ignoreBlock=True, variables={}): # substitute condition cond = item.GetLabel() for variable in self.model.GetVariables(): - pattern = re.compile("%{" + variable + "}") + # curly braces are optional + pattern = re.compile("%{?" + variable + "}?") if pattern.search(cond): value = variables[variable].get("value", "") if variables[variable].get("type", "string") == "string": diff --git a/gui/wxpython/gmodeler/model_items.py b/gui/wxpython/gmodeler/model_items.py index c866817ea55..07174e54a59 100644 --- a/gui/wxpython/gmodeler/model_items.py +++ b/gui/wxpython/gmodeler/model_items.py @@ -347,7 +347,8 @@ def GetLog(self, string=True, substitute=None): # order variables by length for variable in sorted(variables, key=len, reverse=True): - pattern = re.compile("%{" + variable + "}") + # curly braces are optional + pattern = re.compile("%{?" + variable + "}?") value = "" if substitute and "variables" in substitute: for p in substitute["variables"]["params"]: From 5a1f52d931079a88994a53171b9cfca656787e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 15 Apr 2026 17:54:37 +0200 Subject: [PATCH 03/13] wxGUI/gmodeler: parsing without curly braces fix --- gui/wxpython/gmodeler/model.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 57cdaa03dba..aebce611ba4 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -486,7 +486,8 @@ def Validate(self): sval = pattern.search(value) if not sval: continue - var = sval.group(2).strip()[2:-1] # strip '%{...}' + s = sval.group(2).strip() + var = s[2:-1] if s.startswith("%{") else s[1:] # strip curly braces only if present found = False for v in variables: if var.startswith(v): @@ -561,7 +562,8 @@ def _substituteFile(self, item, params=None, checkOnly=False): pattern = re.compile(r"(.*)(%\{.+})(.*)") sval = pattern.search(data) if sval: - var = sval.group(2).strip()[2:-1] # ignore '%{...}' + s = sval.group(2).strip() + var = s[2:-1] if s.startswith("%{") else s[1:] # strip curly braces only if present cmd = item.GetLog(string=False)[0] errList.append(cmd + ": " + _("undefined variable '%s'") % var) From 20ccd1c3973e23b6d25aeb47ff60dac121fe6e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 29 Apr 2026 13:55:10 +0200 Subject: [PATCH 04/13] regex: Allow only both or none curly braces --- gui/wxpython/gmodeler/model.py | 6 +++--- gui/wxpython/gmodeler/model_convert.py | 4 ++-- gui/wxpython/gmodeler/model_items.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index aebce611ba4..a41fd88e000 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -541,7 +541,7 @@ def _substituteFile(self, item, params=None, checkOnly=False): variables = self.GetVariables() for variable in variables: # curly braces are optional - pattern = re.compile("%{?" + variable + "}?") + pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") value = "" if params and "variables" in params: for p in params["variables"]["params"]: @@ -694,7 +694,7 @@ def Run(self, log, onDone, parent=None): variables = self.GetVariables() for variable in variables: # curly braces are optional - pattern = re.compile("%{?" + variable + "}?") + pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") if not pattern.search(cond): continue value = "" @@ -718,7 +718,7 @@ def Run(self, log, onDone, parent=None): # TODO: this part needs some better solution condVar, condText = (x.strip() for x in re.split(r"\s* in \s*", cond)) # curly braces are optional - pattern = re.compile("%{?" + condVar + "}?") + pattern = re.compile(r"%(?:\{" + condVar + r"\}|" + condVar + r")") # for vars()[condVar] in eval(condText): ? vlist = [] if condText[0] == "`" and condText[-1] == "`": diff --git a/gui/wxpython/gmodeler/model_convert.py b/gui/wxpython/gmodeler/model_convert.py index 0ad25a26577..07735a1b0ee 100644 --- a/gui/wxpython/gmodeler/model_convert.py +++ b/gui/wxpython/gmodeler/model_convert.py @@ -57,7 +57,7 @@ def _writeItem(self, item, ignoreBlock=True, variables={}): cond = item.GetLabel() for variable in self.model.GetVariables(): # curly braces are optional - pattern = re.compile("%{?" + variable + "}?") + pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") if pattern.search(cond): value = variables[variable].get("value", "") if variables[variable].get("type", "string") == "string": @@ -971,7 +971,7 @@ def _substitutePythonParamValue( formattedVar = False for var in variables["vars"]: # curly braces are optional - pattern = re.compile("%{?" + var + "}?") + pattern = re.compile(r"%(?:\{" + var + r"\}|" + var + r")") found = pattern.search(parameterizedValue) if found: foundVar = True diff --git a/gui/wxpython/gmodeler/model_items.py b/gui/wxpython/gmodeler/model_items.py index 07174e54a59..19d8319c944 100644 --- a/gui/wxpython/gmodeler/model_items.py +++ b/gui/wxpython/gmodeler/model_items.py @@ -348,7 +348,7 @@ def GetLog(self, string=True, substitute=None): # order variables by length for variable in sorted(variables, key=len, reverse=True): # curly braces are optional - pattern = re.compile("%{?" + variable + "}?") + pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") value = "" if substitute and "variables" in substitute: for p in substitute["variables"]["params"]: From 952c3dd0405085629d3c5da8e4d63f4aac986ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Fri, 15 May 2026 20:15:25 +0200 Subject: [PATCH 05/13] fix: Proper map name from model --- gui/wxpython/gmodeler/model.py | 9 +++------ gui/wxpython/gmodeler/model_items.py | 28 +++++++++++++++++++++++++--- gui/wxpython/gmodeler/panels.py | 23 ++++++++++++++++++++--- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index a41fd88e000..c43cff582c1 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -747,12 +747,9 @@ def Run(self, log, onDone, parent=None): if delInterData: self.DeleteIntermediateData(log) - - # discard values - if params: - for item in params.values(): - for p in item["params"]: - p["value"] = "" + + # store run params + self._runParams = params def DeleteIntermediateData(self, log): """Delete intermediate data""" diff --git a/gui/wxpython/gmodeler/model_items.py b/gui/wxpython/gmodeler/model_items.py index 19d8319c944..3dfb963e36d 100644 --- a/gui/wxpython/gmodeler/model_items.py +++ b/gui/wxpython/gmodeler/model_items.py @@ -679,8 +679,30 @@ def Update(self): self._setPen() self.SetLabel() - def GetDisplayCmd(self): - """Get display command as list""" + def GetResolvedValue(self, resolved=None): + """Get value with model substituted variables + + :param resolved: dict mapping variable name to resolved value, + or None to return the raw value + """ + if not resolved: + return self.value + value = self.value + + # find the variable in resolved + for variable, var_value in resolved.items(): + pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") + value = pattern.sub(var_value, value) + + # return substituted value + return value + + def GetDisplayCmd(self, resolved=None): + """Get display command as list + + :param resolved: dict mapping variable name to resolved value, + or None to return the raw value + """ cmd = [] if self.prompt == "raster": cmd.append("d.rast") @@ -690,7 +712,7 @@ def GetDisplayCmd(self): msg = "Unsupported display prompt: {}".format(self.prompt) raise GException(msg) - cmd.append("map=" + self.value) + cmd.append("map=" + self.GetResolvedValue(resolved)) return cmd diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 506f6169305..7492c72861c 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -408,13 +408,23 @@ def OnModelDone(self, event): # delete intermediate data self._deleteIntermediateData() + # store resolved variables + run_params = getattr(self.model, "_runParams", None) + resolved = {} + if run_params and "variables" in run_params: + for p in run_params["variables"]["params"]: + name = p.get("name", "") + value = p.get("value", "") + if name and value: + resolved[name] = value + # display data if required for data in self.model.GetData(): if not data.HasDisplay(): continue # remove existing map layers first - layers = self._giface.GetLayerList().GetLayersByName(data.GetValue()) + layers = self._giface.GetLayerList().GetLayersByName(data.GetResolvedValue(resolved)) if layers: for layer in layers: self._giface.GetLayerList().DeleteLayer(layer) @@ -422,10 +432,17 @@ def OnModelDone(self, event): # add new map layer self._giface.GetLayerList().AddLayer( ltype=data.GetPrompt(), - name=data.GetValue(), + name=data.GetResolvedValue(resolved), checked=True, - cmd=data.GetDisplayCmd(), + cmd=data.GetDisplayCmd(resolved), ) + + # discard values + if run_params: + for item in run_params.values(): + for p in item["params"]: + p["value"] = "" + del self.model._runParams def _switchPageHandler(self, event, notification): self._switchPage(notification=notification) From d7eb782daa575611e08cd296a82384a3733570b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Fri, 15 May 2026 21:08:18 +0200 Subject: [PATCH 06/13] fix: Map variable replacement after Python --- gui/wxpython/gmodeler/panels.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 7492c72861c..6dd12dd0ca6 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -1818,6 +1818,36 @@ def OnDone(self, event): try_remove(self.filename) self.filename = None + # store resolved variables + model = self.parent.GetModel() + run_params = getattr(model, "_runParams", None) + resolved = {} + if run_params and "variables" in run_params: + for p in run_params["variables"]["params"]: + name = p.get("name", "") + value = p.get("value", "") + if name and value: + resolved[name] = value + + # display data if required + for data in model.GetData(): + if not data.HasDisplay(): + continue + + # remove existing map layers first + layers = self.parent._giface.GetLayerList().GetLayersByName(data.GetResolvedValue(resolved)) + if layers: + for layer in layers: + self.parent._giface.GetLayerList().DeleteLayer(layer) + + # add new map layer + self.parent._giface.GetLayerList().AddLayer( + ltype=data.GetPrompt(), + name=data.GetResolvedValue(resolved), + checked=True, + cmd=data.GetDisplayCmd(resolved), + ) + def OnChangeScriptType(self, event): new_script_type = self.script_type_box.GetStringSelection() From d9c9070afef4074c1439c2ede8d0beb2700a0aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Fri, 15 May 2026 21:08:43 +0200 Subject: [PATCH 07/13] fix: Map variable replacement on Display change --- gui/wxpython/gmodeler/canvas.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 830e9983ccd..59e3b02adee 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -482,19 +482,29 @@ def OnHasDisplay(self, event): shape.SetHasDisplay(event.IsChecked()) self.frame.canvas.Refresh() + model = self.frame.GetModel() + run_params = getattr(model, "_runParams", None) + resolved = {} + if run_params and "variables" in run_params: + for p in run_params["variables"]["params"]: + name = p.get("name", "") + value = p.get("value", "") + if name and value: + resolved[name] = value + try: if event.IsChecked(): # add map layer to display self.frame._giface.GetLayerList().AddLayer( ltype=shape.GetPrompt(), - name=shape.GetValue(), + name=shape.GetResolvedValue(resolved), checked=True, - cmd=shape.GetDisplayCmd(), + cmd=shape.GetDisplayCmd(resolved), ) else: # remove map layer(s) from display layers = self.frame._giface.GetLayerList().GetLayersByName( - shape.GetValue() + shape.GetResolvedValue(resolved) ) for layer in layers: self.frame._giface.GetLayerList().DeleteLayer(layer) From a93b0cc44265f3f06e72e2b592e42ffe74ab2a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jun 2026 05:47:52 -0400 Subject: [PATCH 08/13] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- gui/wxpython/gmodeler/model.py | 13 +++++++++---- gui/wxpython/gmodeler/model_items.py | 3 --- gui/wxpython/gmodeler/panels.py | 11 ++++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index c43cff582c1..c48d92b6733 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -487,7 +487,9 @@ def Validate(self): if not sval: continue s = sval.group(2).strip() - var = s[2:-1] if s.startswith("%{") else s[1:] # strip curly braces only if present + var = ( + s[2:-1] if s.startswith("%{") else s[1:] + ) # strip curly braces only if present found = False for v in variables: if var.startswith(v): @@ -563,7 +565,9 @@ def _substituteFile(self, item, params=None, checkOnly=False): sval = pattern.search(data) if sval: s = sval.group(2).strip() - var = s[2:-1] if s.startswith("%{") else s[1:] # strip curly braces only if present + var = ( + s[2:-1] if s.startswith("%{") else s[1:] + ) # strip curly braces only if present cmd = item.GetLog(string=False)[0] errList.append(cmd + ": " + _("undefined variable '%s'") % var) @@ -694,7 +698,9 @@ def Run(self, log, onDone, parent=None): variables = self.GetVariables() for variable in variables: # curly braces are optional - pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") + pattern = re.compile( + r"%(?:\{" + variable + r"\}|" + variable + r")" + ) if not pattern.search(cond): continue value = "" @@ -747,7 +753,6 @@ def Run(self, log, onDone, parent=None): if delInterData: self.DeleteIntermediateData(log) - # store run params self._runParams = params diff --git a/gui/wxpython/gmodeler/model_items.py b/gui/wxpython/gmodeler/model_items.py index 3dfb963e36d..bb3d31ac9a1 100644 --- a/gui/wxpython/gmodeler/model_items.py +++ b/gui/wxpython/gmodeler/model_items.py @@ -681,7 +681,6 @@ def Update(self): def GetResolvedValue(self, resolved=None): """Get value with model substituted variables - :param resolved: dict mapping variable name to resolved value, or None to return the raw value """ @@ -693,13 +692,11 @@ def GetResolvedValue(self, resolved=None): for variable, var_value in resolved.items(): pattern = re.compile(r"%(?:\{" + variable + r"\}|" + variable + r")") value = pattern.sub(var_value, value) - # return substituted value return value def GetDisplayCmd(self, resolved=None): """Get display command as list - :param resolved: dict mapping variable name to resolved value, or None to return the raw value """ diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 07f61a89c53..40344a1a59f 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -417,14 +417,15 @@ def OnModelDone(self, event): value = p.get("value", "") if name and value: resolved[name] = value - # display data if required for data in self.model.GetData(): if not data.HasDisplay(): continue # remove existing map layers first - layers = self._giface.GetLayerList().GetLayersByName(data.GetResolvedValue(resolved)) + layers = self._giface.GetLayerList().GetLayersByName( + data.GetResolvedValue(resolved) + ) if layers: for layer in layers: self._giface.GetLayerList().DeleteLayer(layer) @@ -436,7 +437,6 @@ def OnModelDone(self, event): checked=True, cmd=data.GetDisplayCmd(resolved), ) - # discard values if run_params: for item in run_params.values(): @@ -1835,14 +1835,15 @@ def OnDone(self, event): value = p.get("value", "") if name and value: resolved[name] = value - # display data if required for data in model.GetData(): if not data.HasDisplay(): continue # remove existing map layers first - layers = self.parent._giface.GetLayerList().GetLayersByName(data.GetResolvedValue(resolved)) + layers = self.parent._giface.GetLayerList().GetLayersByName( + data.GetResolvedValue(resolved) + ) if layers: for layer in layers: self.parent._giface.GetLayerList().DeleteLayer(layer) From b96413f3970277c5456b862fbe33d70f8c308c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Thu, 18 Jun 2026 19:22:48 +0200 Subject: [PATCH 09/13] fix: Run params getter --- gui/wxpython/gmodeler/canvas.py | 2 +- gui/wxpython/gmodeler/model.py | 4 ++++ gui/wxpython/gmodeler/panels.py | 10 ++-------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 59e3b02adee..efea7bab3fe 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -483,7 +483,7 @@ def OnHasDisplay(self, event): self.frame.canvas.Refresh() model = self.frame.GetModel() - run_params = getattr(model, "_runParams", None) + run_params = model.GetRunParams() resolved = {} if run_params and "variables" in run_params: for p in run_params["variables"]["params"]: diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index c48d92b6733..f8e3525cac6 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -756,6 +756,10 @@ def Run(self, log, onDone, parent=None): # store run params self._runParams = params + def GetRunParams(self): + """Get the models run parameters""" + return getattr(self, "_runParams", None) + def DeleteIntermediateData(self, log): """Delete intermediate data""" rast, vect, rast3d, msg = self.GetIntermediateData() diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 40344a1a59f..fbf005686d6 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -409,7 +409,7 @@ def OnModelDone(self, event): self._deleteIntermediateData() # store resolved variables - run_params = getattr(self.model, "_runParams", None) + run_params = self.model.GetRunParams() resolved = {} if run_params and "variables" in run_params: for p in run_params["variables"]["params"]: @@ -437,12 +437,6 @@ def OnModelDone(self, event): checked=True, cmd=data.GetDisplayCmd(resolved), ) - # discard values - if run_params: - for item in run_params.values(): - for p in item["params"]: - p["value"] = "" - del self.model._runParams def _switchPageHandler(self, event, notification): self._switchPage(notification=notification) @@ -1827,7 +1821,7 @@ def OnDone(self, event): # store resolved variables model = self.parent.GetModel() - run_params = getattr(model, "_runParams", None) + run_params = model.GetRunParams() resolved = {} if run_params and "variables" in run_params: for p in run_params["variables"]["params"]: From 89b02fd5419ef63eddc3b2f0c381ad13124f94b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 24 Jun 2026 07:48:08 +0200 Subject: [PATCH 10/13] fix: Adding maps in standalone mode --- gui/wxpython/gmodeler/canvas.py | 12 +++++++----- gui/wxpython/gmodeler/panels.py | 30 ++++++++++++++++++------------ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index efea7bab3fe..69ab40c88c7 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -492,10 +492,14 @@ def OnHasDisplay(self, event): if name and value: resolved[name] = value + layer_list = self.frame._giface.GetLayerList() + if not hasattr(layer_list, "GetLayersByName"): + return + try: if event.IsChecked(): # add map layer to display - self.frame._giface.GetLayerList().AddLayer( + layer_list.AddLayer( ltype=shape.GetPrompt(), name=shape.GetResolvedValue(resolved), checked=True, @@ -503,11 +507,9 @@ def OnHasDisplay(self, event): ) else: # remove map layer(s) from display - layers = self.frame._giface.GetLayerList().GetLayersByName( - shape.GetResolvedValue(resolved) - ) + layers = layer_list.GetLayersByName(shape.GetResolvedValue(resolved)) for layer in layers: - self.frame._giface.GetLayerList().DeleteLayer(layer) + layer_list.DeleteLayer(layer) except GException as e: GError(parent=self, message="{}".format(e)) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index fbf005686d6..b184b46ef32 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -417,21 +417,24 @@ def OnModelDone(self, event): value = p.get("value", "") if name and value: resolved[name] = value - # display data if required + + # display data if required and is possible + layer_list = self._giface.GetLayerList() + if not hasattr(layer_list, "GetLayersByName"): + return + for data in self.model.GetData(): if not data.HasDisplay(): continue # remove existing map layers first - layers = self._giface.GetLayerList().GetLayersByName( - data.GetResolvedValue(resolved) - ) + layers = layer_list.GetLayersByName(data.GetResolvedValue(resolved)) if layers: for layer in layers: - self._giface.GetLayerList().DeleteLayer(layer) + layer_list.DeleteLayer(layer) # add new map layer - self._giface.GetLayerList().AddLayer( + layer_list.AddLayer( ltype=data.GetPrompt(), name=data.GetResolvedValue(resolved), checked=True, @@ -1829,21 +1832,24 @@ def OnDone(self, event): value = p.get("value", "") if name and value: resolved[name] = value - # display data if required + + # display data if required and is possible + layer_list = self.parent._giface.GetLayerList() + if not hasattr(layer_list, "GetLayersByName"): + return + for data in model.GetData(): if not data.HasDisplay(): continue # remove existing map layers first - layers = self.parent._giface.GetLayerList().GetLayersByName( - data.GetResolvedValue(resolved) - ) + layers = layer_list.GetLayersByName(data.GetResolvedValue(resolved)) if layers: for layer in layers: - self.parent._giface.GetLayerList().DeleteLayer(layer) + layer_list.DeleteLayer(layer) # add new map layer - self.parent._giface.GetLayerList().AddLayer( + layer_list.AddLayer( ltype=data.GetPrompt(), name=data.GetResolvedValue(resolved), checked=True, From df9f10b701b99fc70413aba65f56b82b0b93aac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Thu, 25 Jun 2026 12:24:38 +0200 Subject: [PATCH 11/13] fix: Resolve review comment --- gui/wxpython/gmodeler/canvas.py | 3 ++- gui/wxpython/gmodeler/panels.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 69ab40c88c7..5fd2495a83b 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -23,6 +23,7 @@ from gui_core.wrap import TextEntryDialog as wxTextEntryDialog, NewId, Menu from gui_core.forms import GUI from core.gcmd import GException, GError +from core.giface import StandaloneGrassInterface from gmodeler.model_items import ( ModelRelation, @@ -493,7 +494,7 @@ def OnHasDisplay(self, event): resolved[name] = value layer_list = self.frame._giface.GetLayerList() - if not hasattr(layer_list, "GetLayersByName"): + if isinstance(self.frame._giface, StandaloneGrassInterface): return try: diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index b184b46ef32..942594d6dfa 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -45,7 +45,7 @@ from core.debug import Debug from core.gcmd import GMessage, GException, GWarning, GError from core.settings import UserSettings -from core.giface import Notification +from core.giface import Notification, StandaloneGrassInterface from gui_core.widgets import GNotebook from gui_core.goutput import GConsoleWindow @@ -420,7 +420,7 @@ def OnModelDone(self, event): # display data if required and is possible layer_list = self._giface.GetLayerList() - if not hasattr(layer_list, "GetLayersByName"): + if isinstance(self._giface, StandaloneGrassInterface): return for data in self.model.GetData(): @@ -1835,7 +1835,7 @@ def OnDone(self, event): # display data if required and is possible layer_list = self.parent._giface.GetLayerList() - if not hasattr(layer_list, "GetLayersByName"): + if isinstance(self.parent._giface, StandaloneGrassInterface): return for data in model.GetData(): From 9aa664f20384eda93a98a13506e9f4aac62bde03 Mon Sep 17 00:00:00 2001 From: Martin Landa Date: Thu, 25 Jun 2026 15:54:32 +0200 Subject: [PATCH 12/13] check StandaloneGrassInterface ASAP --- gui/wxpython/gmodeler/canvas.py | 6 +++--- gui/wxpython/gmodeler/panels.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 5fd2495a83b..6db6f65a03e 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -483,6 +483,9 @@ def OnHasDisplay(self, event): shape.SetHasDisplay(event.IsChecked()) self.frame.canvas.Refresh() + if isinstance(self.frame._giface, StandaloneGrassInterface): + return + model = self.frame.GetModel() run_params = model.GetRunParams() resolved = {} @@ -494,9 +497,6 @@ def OnHasDisplay(self, event): resolved[name] = value layer_list = self.frame._giface.GetLayerList() - if isinstance(self.frame._giface, StandaloneGrassInterface): - return - try: if event.IsChecked(): # add map layer to display diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 942594d6dfa..d1d18452cf8 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -419,10 +419,10 @@ def OnModelDone(self, event): resolved[name] = value # display data if required and is possible - layer_list = self._giface.GetLayerList() if isinstance(self._giface, StandaloneGrassInterface): return + layer_list = self._giface.GetLayerList() for data in self.model.GetData(): if not data.HasDisplay(): continue From f59604a6fec0c92daa8118b3b313e0e26934910b Mon Sep 17 00:00:00 2001 From: Martin Landa Date: Thu, 25 Jun 2026 16:00:42 +0200 Subject: [PATCH 13/13] check StandaloneGrassInterface before calling GetLayerList() --- gui/wxpython/gmodeler/panels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index d1d18452cf8..fabe132637b 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -1834,10 +1834,10 @@ def OnDone(self, event): resolved[name] = value # display data if required and is possible - layer_list = self.parent._giface.GetLayerList() if isinstance(self.parent._giface, StandaloneGrassInterface): return + layer_list = self.parent._giface.GetLayerList() for data in model.GetData(): if not data.HasDisplay(): continue