From ffabdbb66a9985a7994fa6812847463ffbbed760 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 8 Nov 2024 12:50:20 +0100 Subject: [PATCH] Fixes for wx --- examples/wx_app.py | 33 ++++++++++++++++++++------------- rendercanvas/base.py | 5 ++++- rendercanvas/wx.py | 27 +++++++++++++++------------ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/examples/wx_app.py b/examples/wx_app.py index 8916de3..b20a629 100644 --- a/examples/wx_app.py +++ b/examples/wx_app.py @@ -2,6 +2,8 @@ An example demonstrating a wx app with a wgpu viz inside. """ +import time + import wx from rendercanvas.wx import RenderWidget @@ -13,30 +15,35 @@ def __init__(self): super().__init__(None, title="wgpu triangle embedded in a wx app") self.SetSize(640, 480) - splitter = wx.SplitterWindow(self) - + # Using present_method 'image' because it reports "The surface texture is suboptimal" + self.canvas = RenderWidget( + self, update_mode="continuous", present_method="image" + ) self.button = wx.Button(self, -1, "Hello world") - self.canvas1 = RenderWidget(splitter) - self.canvas2 = RenderWidget(splitter) - - splitter.SplitVertically(self.canvas1, self.canvas2) - splitter.SetSashGravity(0.5) + self.output = wx.StaticText(self) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.button, 0, wx.EXPAND) - sizer.Add(splitter, 1, wx.EXPAND) + sizer.Add(self.canvas, 1, wx.EXPAND) + sizer.Add(self.output, 1, wx.EXPAND) self.SetSizer(sizer) + self.button.Bind(wx.EVT_BUTTON, self.OnClicked) + + # Force the canvas to be shown, so that it gets a valid handle. + # Otherwise GetHandle() is initially 0, and getting a surface will fail. self.Show() + def OnClicked(self, event): # noqa: N802 + t = self.output.GetLabel() + t += f"\nClicked at {time.time():0.1f}" + self.output.SetLabel(t) + app = wx.App() example = Example() -draw_frame1 = setup_drawing_sync(example.canvas1) -draw_frame2 = setup_drawing_sync(example.canvas2) - -example.canvas1.request_draw(draw_frame1) -example.canvas2.request_draw(draw_frame2) +draw_frame = setup_drawing_sync(example.canvas) +example.canvas.request_draw(draw_frame) app.MainLoop() diff --git a/rendercanvas/base.py b/rendercanvas/base.py index 961109b..023aa5f 100644 --- a/rendercanvas/base.py +++ b/rendercanvas/base.py @@ -352,7 +352,10 @@ def is_closed(self): def set_logical_size(self, width, height): """Set the window size (in logical pixels).""" - self._rc_set_logical_size(float(width), float(height)) + width, height = float(width), float(height) + if width < 0 or height < 0: + raise ValueError("Canvas width and height must not be negative") + self._rc_set_logical_size(width, height) def set_title(self, title): """Set the window title.""" diff --git a/rendercanvas/wx.py b/rendercanvas/wx.py index eba0361..54654aa 100644 --- a/rendercanvas/wx.py +++ b/rendercanvas/wx.py @@ -160,6 +160,8 @@ def _rc_init(self, present_method=None, **_): self.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse_events) self.Bind(wx.EVT_MOTION, self._on_mouse_move) + self.Show() + def _on_resize_done(self, *args): self._draw_lock = False self.Refresh() @@ -200,6 +202,11 @@ def _rc_get_loop(self): def _rc_get_present_info(self): if self._surface_ids is None: + # On wx it can take a little while for the handle to be available, + # causing GetHandle() to be initially 0, so getting a surface will fail. + etime = time.perf_counter() + 1 + while self.GetHandle() == 0 and time.perf_counter() < etime: + loop.process_wx_events() self._surface_ids = self._get_surface_ids() global _show_image_method_warning if self._present_to_screen and self._surface_ids: @@ -251,8 +258,7 @@ def _rc_get_pixel_ratio(self): return self.GetContentScaleFactor() def _rc_set_logical_size(self, width, height): - if width < 0 or height < 0: - raise ValueError("Window width and height must not be negative") + width, height = int(width), int(height) parent = self.Parent if isinstance(parent, WxRenderCanvas): parent.SetSize(width, height) @@ -261,7 +267,11 @@ def _rc_set_logical_size(self, width, height): def _rc_close(self): self._is_closed = True - self.Hide() + parent = self.Parent + if isinstance(parent, WxRenderCanvas): + parent.Hide() + else: + self.Hide() def _rc_is_closed(self): return self._is_closed @@ -415,21 +425,14 @@ class WxRenderCanvas(WrapperRenderCanvas, wx.Frame): # Most of this is proxying stuff to the inner widget. - def __init__(*args, **kwargs): + def __init__(self, *, parent=None, **kwargs): loop.init_wx() - super().__init__(*args, **kwargs) + super().__init__(parent, **kwargs) def _rc_init(self, **canvas_kwargs): self._subwidget = WxRenderWidget(parent=self, **canvas_kwargs) - self.Bind(wx.EVT_CLOSE, lambda e: self.Destroy()) - - # Force the canvas to be shown, so that it gets a valid handle. - # Otherwise GetHandle() is initially 0, and getting a surface will fail. self.Show() - etime = time.perf_counter() + 1 - while self._subwidget.GetHandle() == 0 and time.perf_counter() < etime: - loop.process_wx_events() # wx methods