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)