From a05b13b93ab4ed03397548348e8de48230b284f3 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sun, 16 Aug 2026 09:22:34 +1000 Subject: [PATCH] fix: idisp() crashes when reuse=True with no title given The OpenCV display path only auto-generated a window title when reuse=False -- `if not reuse and title is None: ...` -- so reuse=True with no explicit title left title as None, and the very next line's `assert title is not None` (added later, with a comment claiming this was already guaranteed) crashed with a bare AssertionError instead of ever reaching cv2.namedWindow(). reuse=True is meant for an animation loop redrawing into the same window across many calls, so the fallback can't be the existing counter-incremented "idisp.N" (that would open a new window per call, defeating reuse) -- use a fixed generic title instead, so repeated calls without an explicit title still share one window. Co-Authored-By: Claude Sonnet 5 --- src/machinevisiontoolbox/base/imageio.py | 13 +++++++++---- tests/base/test_io.py | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/machinevisiontoolbox/base/imageio.py b/src/machinevisiontoolbox/base/imageio.py index 112a782b..15aafdfd 100644 --- a/src/machinevisiontoolbox/base/imageio.py +++ b/src/machinevisiontoolbox/base/imageio.py @@ -882,10 +882,15 @@ def key_press(event: Any) -> None: ## display using OpenCV global __last_window_number - if not reuse and title is None: - # create a unique window name for each call - title = "idisp." + str(__last_window_number) - __last_window_number += 1 + if title is None: + if reuse: + # fixed name (no counter) so repeated calls in an + # animation loop share the same window + title = "idisp" + else: + # create a unique window name for each call + title = "idisp." + str(__last_window_number) + __last_window_number += 1 # At this point title is guaranteed to be a string assert title is not None diff --git a/tests/base/test_io.py b/tests/base/test_io.py index d60dad0c..3ca164e0 100644 --- a/tests/base/test_io.py +++ b/tests/base/test_io.py @@ -369,6 +369,20 @@ def test_idisp_uint16_image(self): result = idisp(im, matplotlib=False) self.assertIsNone(result) + def test_idisp_reuse_no_title(self): + """reuse=True with no title must not crash -- regression test for + the OpenCV path's title fallback (previously asserted title is not + None even though reuse=True skipped generating one)""" + from machinevisiontoolbox.base.imageio import cv_destroy_window, idisp + + im = np.random.randint(0, 256, (30, 30), dtype=np.uint8) + + try: + idisp(im, matplotlib=False, reuse=True) + idisp(im, matplotlib=False, reuse=True) + finally: + cv_destroy_window("idisp", block=False) + def test_idisp_float_image(self): """Test idisp with float image""" im = np.random.rand(30, 30).astype(np.float32)