From 0c4c081dd0c9a9d5b7f0a61c0e098bc6af528781 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 23 Jan 2026 15:55:01 +0100 Subject: [PATCH 1/2] Also weigh preference of context in present-method selection --- rendercanvas/base.py | 5 +++-- rendercanvas/contexts/bitmapcontext.py | 2 +- rendercanvas/contexts/wgpucontext.py | 2 +- rendercanvas/pyodide.py | 8 ++++++-- rendercanvas/qt.py | 15 +++------------ rendercanvas/wx.py | 15 +++------------ 6 files changed, 17 insertions(+), 30 deletions(-) diff --git a/rendercanvas/base.py b/rendercanvas/base.py index 080c0b5..6daa736 100644 --- a/rendercanvas/base.py +++ b/rendercanvas/base.py @@ -670,8 +670,9 @@ def _rc_get_present_info(self, present_methods: list[str]) -> dict | None: selected presentation method. The ``present_methods`` represents the supported methods of the - canvas-context, possibly filtered by a user-specified method. A canvas - backend must implement at least the "screen" or "bitmap" method. + canvas-context, in order of context-preference, possibly filtered by a + user-specified method. A canvas backend must implement at least the + "screen" or "bitmap" method. The returned dict must contain at least the key 'method', which must match one of the ``present_methods``. The remaining values represent diff --git a/rendercanvas/contexts/bitmapcontext.py b/rendercanvas/contexts/bitmapcontext.py index 3a5a5c0..2fe667e 100644 --- a/rendercanvas/contexts/bitmapcontext.py +++ b/rendercanvas/contexts/bitmapcontext.py @@ -14,7 +14,7 @@ class BitmapContext(BaseContext): # Note: instantiating this class creates an instance of a sub-class, dedicated to the present method of the canvas. - present_methods = ["bitmap", "screen"] + present_methods = ["bitmap", "screen"] # in order of preference def __new__(cls, present_info: dict): # Instantiating this class actually produces a subclass diff --git a/rendercanvas/contexts/wgpucontext.py b/rendercanvas/contexts/wgpucontext.py index 8d9f96a..7d490d4 100644 --- a/rendercanvas/contexts/wgpucontext.py +++ b/rendercanvas/contexts/wgpucontext.py @@ -16,7 +16,7 @@ class WgpuContext(BaseContext): # Note: instantiating this class creates an instance of a sub-class, dedicated to the present method of the canvas. - present_methods = ["screen", "bitmap"] + present_methods = ["screen", "bitmap"] # in order of preference def __new__(cls, present_info: dict): # Instantiating this class actually produces a subclass diff --git a/rendercanvas/pyodide.py b/rendercanvas/pyodide.py index 39ae978..b5ee10a 100644 --- a/rendercanvas/pyodide.py +++ b/rendercanvas/pyodide.py @@ -428,14 +428,18 @@ def _rc_gui_poll(self): pass # Nothing to be done; the JS loop is always running (and Pyodide wraps that in a global asyncio loop) def _rc_get_present_info(self, present_methods): - if "screen" in present_methods: + # Select method + the_method = present_methods[0] + + # Apply + if the_method == "screen": # wgpu-specific presentation. The wgpu.backends.pyodide.GPUCanvasContext must be able to consume this. return { "method": "screen", "platform": "browser", "window": self._canvas_element, # Just provide the canvas object } - elif "bitmap" in present_methods: + elif the_method == "bitmap": # Generic presentation return { "method": "bitmap", diff --git a/rendercanvas/qt.py b/rendercanvas/qt.py index 2a42be9..e17cfeb 100644 --- a/rendercanvas/qt.py +++ b/rendercanvas/qt.py @@ -352,23 +352,14 @@ def _rc_gui_poll(self): loop._app.processEvents() def _rc_get_present_info(self, present_methods): - # Select what method the canvas prefers - preferred_method = "screen" - if SYSTEM_IS_WAYLAND: + # Select the method + the_method = present_methods[0] + if SYSTEM_IS_WAYLAND and "bitmap" in present_methods: # Trying to render to screen on Wayland segfaults. This might be because # the "display" is not the real display id. We can tell Qt to use # XWayland, so we can use the X11 path. This worked at some point, # but later this resulted in a Rust panic. So, until this is sorted # out, we fall back to rendering via an image. - preferred_method = "bitmap" - - # Select method - the_method = None - if preferred_method in present_methods: - the_method = preferred_method - elif "screen" in present_methods: - the_method = "screen" - elif "bitmap" in present_methods: the_method = "bitmap" # Apply diff --git a/rendercanvas/wx.py b/rendercanvas/wx.py index f4ee0d7..97c5b9a 100644 --- a/rendercanvas/wx.py +++ b/rendercanvas/wx.py @@ -282,19 +282,10 @@ def _rc_gui_poll(self): loop.process_wx_events() def _rc_get_present_info(self, present_methods): - # Select what method the canvas prefers - preferred_method = "screen" - if SYSTEM_IS_WAYLAND: - preferred_method = "bitmap" # also see qt.py - # Select method - the_method = None - if preferred_method in present_methods: - the_method = preferred_method - elif "screen" in present_methods: - the_method = "screen" - elif "bitmap" in present_methods: - the_method = "bitmap" + the_method = present_methods[0] + if SYSTEM_IS_WAYLAND and "bitmap" in present_methods: + the_method = "bitmap" # also see qt.py # Apply if the_method == "screen": From 5d78eab91944b35cfe962b306d4531a38cbcf2c6 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 23 Jan 2026 16:06:52 +0100 Subject: [PATCH 2/2] bit more leeway for spurious test fail --- tests/test_loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index c05d5e9..93e0eb0 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -395,7 +395,7 @@ def test_run_loop_and_close_canvases(SomeLoop): et = time.time() - t0 print(et) - assert 0.25 < et < 0.45 + leeway + assert 0.25 < et < 0.50 + leeway assert canvas1._events.is_closed assert canvas2._events.is_closed