diff --git a/wgpu/gui/jupyter.py b/wgpu/gui/jupyter.py index 082223ae..1eed7384 100644 --- a/wgpu/gui/jupyter.py +++ b/wgpu/gui/jupyter.py @@ -4,19 +4,16 @@ """ import weakref -import asyncio -from .base import WgpuAutoGui, WgpuCanvasBase +from .base import WgpuCanvasBase +from .asyncio import AsyncioWgpuLoop import numpy as np from jupyter_rfb import RemoteFrameBuffer from IPython.display import display -pending_jupyter_canvases = [] - - -class JupyterWgpuCanvas(WgpuAutoGui, WgpuCanvasBase, RemoteFrameBuffer): +class JupyterWgpuCanvas(WgpuCanvasBase, RemoteFrameBuffer): """An ipywidgets widget providing a wgpu canvas. Needs the jupyter_rfb library.""" def __init__(self, *, size=None, title=None, **kwargs): @@ -27,10 +24,9 @@ def __init__(self, *, size=None, title=None, **kwargs): self._pixel_ratio = 1 self._logical_size = 0, 0 self._is_closed = False - self._request_draw_timer_running = False # Register so this can be display'ed when run() is called - pending_jupyter_canvases.append(weakref.ref(self)) + loop._pending_jupyter_canvases.append(weakref.ref(self)) # Initialize size if size is not None: @@ -51,7 +47,6 @@ def handle_event(self, event): super().handle_event(event) def get_frame(self): - self._request_draw_timer_running = False # The _draw_frame_and_present() does the drawing and then calls # present_context.present(), which calls our present() method. # The result is either a numpy array or None, and this matches @@ -61,6 +56,9 @@ def get_frame(self): # Implementation needed for WgpuCanvasBase + def _get_loop(self): + return loop + def get_pixel_ratio(self): return self._pixel_ratio @@ -86,9 +84,10 @@ def is_closed(self): return self._is_closed def _request_draw(self): - if not self._request_draw_timer_running: - self._request_draw_timer_running = True - call_later(self._get_draw_wait_time(), RemoteFrameBuffer.request_draw, self) + RemoteFrameBuffer.request_draw(self) + + def _force_draw(self): + raise NotImplementedError() # todo: how? # Implementation needed for WgpuCanvasInterface @@ -111,16 +110,22 @@ def present_image(self, image, **kwargs): WgpuCanvas = JupyterWgpuCanvas -def call_later(delay, callback, *args): - loop = asyncio.get_event_loop() - loop.call_later(delay, callback, *args) +class JupyterAsyncioWgpuLoop(AsyncioWgpuLoop): + def __init__(self): + super().__init__() + self._pending_jupyter_canvases = [] + + def poll(self): + pass # Jupyter is running in a separate process :) + + def run(self): + # Show all widgets that have been created so far. + # No need to actually start an event loop, since Jupyter already runs it. + canvases = [r() for r in self._pending_jupyter_canvases] + self._pending_jupyter_canvases.clear() + for w in canvases: + if w and not w.is_closed(): + display(w) -def run(): - # Show all widgets that have been created so far. - # No need to actually start an event loop, since Jupyter already runs it. - canvases = [r() for r in pending_jupyter_canvases] - pending_jupyter_canvases.clear() - for w in canvases: - if w and not w.is_closed(): - display(w) +loop = JupyterAsyncioWgpuLoop()