Skip to content

Commit

Permalink
Implement jupyter canvas - untested
Browse files Browse the repository at this point in the history
  • Loading branch information
almarklein committed Oct 15, 2024
1 parent ed6f81b commit ebf3286
Showing 1 changed file with 28 additions and 23 deletions.
51 changes: 28 additions & 23 deletions wgpu/gui/jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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

Expand All @@ -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()

0 comments on commit ebf3286

Please sign in to comment.