From 3806b29136e13f5845936589d019ae5d16cf40e9 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:24:56 +0800 Subject: [PATCH 1/2] glcanon: decide preview "too large" once at load, not every frame The byte-size check ran per frame in _resolve_show_program(), re-stat'ing the file each repaint and latching the refused name so the warning would not repeat. Decide it once in load_preview() instead, log it there, and have _resolve_show_program() read a flag. The file is still parsed fully, so the run-time limit check stays valid; only drawing is suppressed. --- lib/python/rs274/glcanon.py | 40 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/python/rs274/glcanon.py b/lib/python/rs274/glcanon.py index 2d329f92567..61f71b1c3d2 100644 --- a/lib/python/rs274/glcanon.py +++ b/lib/python/rs274/glcanon.py @@ -706,9 +706,10 @@ def __init__(self, s=None, lp=None, g=None): # the cap is left as it behaves rather than as it was described. self.max_file_size = min(system_memory_gb, 20) * 1024 * 1024 - #: Name of the file the size limit last refused, so that the refusal is - #: reported once instead of on every frame. See _resolve_show_program. - self._refused_file = None + #: Set once per load in load_preview() when the file exceeds the size + #: limit; _resolve_show_program() reads it instead of re-stat'ing the + #: file on every frame. + self.preview_too_large = False try: if os.environ["INI_FILE_NAME"]: @@ -1274,26 +1275,12 @@ def redraw(self): def _resolve_show_program(self): """get_show_program(), refused for a file too large to preview. - Called from frame_context(), so this runs once per frame - which is why - the refusal is latched on the file it refused rather than logged where - it is decided. Unlatched, a single over-size file produces a warning per - redraw for as long as it stays loaded. Latching on the name and not on a - bool means a different over-size file still reports. + The size decision is made once per load in load_preview(); this only + reads the flag, so no file is stat'ed per frame. """ - show_program = self.get_show_program() - s = self.stat - if (os.path.exists(s.file) - and 0 < self.max_file_size < os.stat(s.file).st_size): - if self._refused_file != s.file: - self._refused_file = s.file - log.warning("%s is larger than the %.0f MB preview limit; " - "preview disabled for it", - s.file, self.max_file_size / (1024 * 1024)) + if self.preview_too_large: return False - # Cleared rather than left set, so that loading a small file and then - # coming back to the big one reports it again. - self._refused_file = None - return show_program + return self.get_show_program() def lathe_historical_config(self,trajcoordinates): # detect historical lathe config with dummy joint 1 @@ -1464,6 +1451,7 @@ def extents_info(self): def load_preview(self, f, canon, *args): self.set_canon(canon) + self.preview_too_large = False result, seq = gcode.parse(f, canon, *args) if result <= gcode.MIN_ERROR: @@ -1472,6 +1460,16 @@ def load_preview(self, f, canon, *args): self.stale_dlist('program_rapids') self.stale_dlist('program_norapids') + # Parsed fully (extents and the run-time limit check stay valid); only + # drawing is suppressed. + if 0 < self.max_file_size and os.path.exists(f) \ + and self.max_file_size < os.stat(f).st_size: + self.preview_too_large = True + log.warning("%s is larger than the %.0f MB preview limit; " + "preview disabled for it. The program still runs, " + "but without a graphical extents check.", + f, self.max_file_size / (1024 * 1024)) + return result, seq def from_internal_units(self, pos, unit=None): From 683ac30b71d60cb7b53fe0a046789c23a8fa063e Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:35:51 +0800 Subject: [PATCH 2/2] axis: warn when run_warn used a truncated preview run_warn() checks program extents from the preview parse. An aborted parse (PREVIEW_TIMEOUT or cancel) skips calc_extents(), leaving init extents, so run_warn() gives a false all-clear. Compute partial extents on abort and flag the preview incomplete so run_warn() reports it. --- lib/python/rs274/glcanon.py | 12 +++++++++++- src/emc/usr_intf/axis/scripts/axis.py | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/python/rs274/glcanon.py b/lib/python/rs274/glcanon.py index 61f71b1c3d2..c20a6cbf51e 100644 --- a/lib/python/rs274/glcanon.py +++ b/lib/python/rs274/glcanon.py @@ -132,6 +132,8 @@ def __init__(self, colors, geometry, is_foam=0, foam_w=1.5, foam_z=0.0): self.min_extents_notool_zero_rxy = [9e99,9e99,9e99] self.max_extents_notool_zero_rxy = [-9e99,-9e99,-9e99] self.colors = colors + # Set if the parse was aborted, so the extents above are only partial. + self.preview_incomplete = False self.in_arc = 0 self.xo = self.yo = self.zo = self.ao = self.bo = self.co = self.uo = self.vo = self.wo = 0 self.dwell_time = 0 @@ -1452,7 +1454,15 @@ def extents_info(self): def load_preview(self, f, canon, *args): self.set_canon(canon) self.preview_too_large = False - result, seq = gcode.parse(f, canon, *args) + canon.preview_incomplete = False + try: + result, seq = gcode.parse(f, canon, *args) + except KeyboardInterrupt: + # Aborted parse: extents cover only the parsed portion. Flag it so + # callers do not treat the partial check as complete. + canon.preview_incomplete = True + canon.calc_extents() + raise if result <= gcode.MIN_ERROR: self.canon.progress.nextphase(1) diff --git a/src/emc/usr_intf/axis/scripts/axis.py b/src/emc/usr_intf/axis/scripts/axis.py index 8ccf6b1e73f..f9f04cc72a5 100755 --- a/src/emc/usr_intf/axis/scripts/axis.py +++ b/src/emc/usr_intf/axis/scripts/axis.py @@ -1893,6 +1893,11 @@ def run_warn(): if o.canon.max_extents_notool[i] > machine_limit_max[i]: warnings.append(_("Program exceeds machine maximum on axis %s") % "XYZABCUVW"[i]) + # A truncated preview only checked part of the program; warn rather + # than imply a clean all-clear. + if getattr(o.canon, "preview_incomplete", False): + warnings.append(_("G-code preview was truncated before completion; " + "machine limits could not be fully checked.")) if warnings: text = "\n".join(warnings) return int(root_window.tk.call("nf_dialog", ".error",