Skip to content

Commit

Permalink
Improve gui docs (#431)
Browse files Browse the repository at this point in the history
* Improve gui docs

* more docstrings

* Fix crossrefs

* black

---------

Co-authored-by: Korijn van Golen <[email protected]>
  • Loading branch information
almarklein and Korijn authored Nov 23, 2023
1 parent 25cf7dc commit f68d910
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 33 deletions.
101 changes: 69 additions & 32 deletions wgpu/gui/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class WgpuCanvasInterface:
Any object that implements these methods is a canvas that wgpu can work with.
The object does not even have to derive from this class.
In most cases it's more convenient to subclass `gui.WgpuCanvasBase`.
In most cases it's more convenient to subclass :class:`WgpuCanvasBase <wgpu.gui.WgpuCanvasBase>`.
"""

def __init__(self, *args, **kwargs):
Expand All @@ -73,15 +73,19 @@ def __init__(self, *args, **kwargs):
self._canvas_context = None

def get_window_id(self):
"""Get the native window id. This is used to obtain a surface id,
so that wgpu can render to the region of the screen occupied by the canvas.
"""Get the native window id.
This is used to obtain a surface id, so that wgpu can render
to the region of the screen occupied by the canvas.
"""
raise NotImplementedError()

def get_display_id(self):
"""Get the native display id on Linux. This is needed in addition to the
window id to obtain a surface id. The default implementation calls into
the X11 lib to get the display id.
"""Get the native display id (Linux only).
On Linux this is needed in addition to the window id to obtain
a surface id. The default implementation calls into the X11 lib
to get the display id.
"""
# Re-use to avoid creating loads of id's
if getattr(self, "_display_id", None) is not None:
Expand All @@ -107,8 +111,14 @@ def get_physical_size(self):
raise NotImplementedError()

def get_context(self, kind="webgpu"):
"""Get the GPUCanvasContext object corresponding to this canvas,
which can be used to e.g. obtain a texture to render to.
"""Get the ``GPUCanvasContext`` object corresponding to this canvas.
The context is used to obtain a texture to render to, and to
present that texture to the canvas. This class provides a
default implementation to get the appropriate context.
The ``kind`` argument is a remnant from the WebGPU spec and
must always be "webgpu".
"""
# Note that this function is analog to HtmlCanvas.getContext(), except
# here the only valid arg is 'webgpu', which is also made the default.
Expand All @@ -123,15 +133,20 @@ def get_context(self, kind="webgpu"):


class WgpuCanvasBase(WgpuCanvasInterface):
"""A canvas class that provides a basis for all GUI toolkits.
"""A convenient base canvas class.
This class provides a uniform API and implements common
functionality, to increase consistency and reduce code duplication.
It is convenient (but not strictly necessary) for canvas classes
to inherit from this class (but all builtin canvases do).
This class implements common functionality, to realize a common API
and avoid code duplication. It is convenient (but not strictly necessary)
for canvas classes to inherit from this class (all builtin canvases do).
This class provides an API for scheduling draws (``request_draw()``)
and implements a mechanism to call the provided draw function
(``draw_frame()``) and then present the result to the canvas.
Amongst other things, this class implements draw rate limiting,
which can be set with the ``max_fps`` attribute (default 30). For
benchmarks you may also want to set ``vsync`` to False.
This class also implements draw rate limiting, which can be set
with the ``max_fps`` attribute (default 30). For benchmarks you may
also want to set ``vsync`` to False.
"""

def __init__(self, *args, max_fps=30, vsync=True, **kwargs):
Expand All @@ -154,23 +169,35 @@ def __del__(self):
pass

def draw_frame(self):
"""The function that gets called at each draw. You can implement
this method in a subclass, or set it via a call to request_draw().
"""The function that gets called at each draw.
You can implement this method in a subclass, or set it via a
call to request_draw().
"""
pass

def request_draw(self, draw_function=None):
"""Request from the main loop to schedule a new draw event,
so that the canvas will be updated. If draw_function is not
given, the last set drawing function is used.
"""Schedule a new draw event.
This function does not perform a draw directly, but schedules
a draw event at a suitable moment in time. In the draw event
the draw function is called, and the resulting rendered image
is presented to screen.
Arguments:
draw_function (callable or None): The function to set as the new draw
function. If not given or None, the last set draw function is used.
"""
if draw_function is not None:
self.draw_frame = draw_function
self._request_draw()

def _draw_frame_and_present(self):
"""Draw the frame and present the result. Errors are logged to the
"wgpu" logger. Should be called by the subclass at an appropriate time.
"""Draw the frame and present the result.
Errors are logged to the "wgpu" logger. Should be called by the
subclass at an appropriate time.
"""
self._last_draw_time = time.perf_counter()
# Perform the user-defined drawing code. When this errors,
Expand Down Expand Up @@ -215,19 +242,23 @@ def is_closed(self):
raise NotImplementedError()

def _request_draw(self):
"""This should invoke a new draw in a later event loop
iteration (i.e. the call itself should return directly).
Multiple calls should result in a single new draw. Preferably
the FPS is limited to avoid draining CPU and power.
"""GUI-specific implementation for ``request_draw()``.
* This should invoke a new draw at a later time.
* The call itself should return directly.
* Multiple calls should result in a single new draw.
* Preferably the ``max_fps`` and ``vsync`` are honored.
"""
raise NotImplementedError()


class WgpuAutoGui:
"""Mixin class for canvases implementing autogui.
AutoGui canvases provide an API for handling events and registering event
handlers.
This class provides a common API for handling events and registering
event handlers. It adds to :class:`WgpuCanvasBase <wgpu.gui.WgpuCanvasBase>`
that interactive examples and applications can be written in a
generic way (no-GUI specific code).
"""

def __init__(self, *args, **kwargs):
Expand All @@ -237,8 +268,10 @@ def __init__(self, *args, **kwargs):
self._event_handlers = defaultdict(set)

def _get_event_wait_time(self):
"""Calculate the time to wait for the next event dispatching
(for rate-limited events)."""
"""Calculate the time to wait for the next event dispatching.
Used for rate-limited events.
"""
rate = 75 # events per second
now = time.perf_counter()
target_time = self._last_event_time + 1.0 / rate
Expand All @@ -248,8 +281,9 @@ def _handle_event_rate_limited(
self, event, call_later_func, match_keys, accum_keys
):
"""Alternative `to handle_event()` for events that must be rate-limted.
If any of the `match_keys` keys of the new event differ from the currently
pending event, the old event is dispatched now. The `accum_keys` keys of
If any of the ``match_keys`` keys of the new event differ from the currently
pending event, the old event is dispatched now. The ``accum_keys`` keys of
the current and new event are added together (e.g. to accumulate wheel delta).
The (accumulated) event is handled in the following cases:
Expand Down Expand Up @@ -302,6 +336,9 @@ def handle_event(self, event):
The default implementation dispatches the event to the
registered event handlers.
Arguments:
event (dict): the event to handle.
"""
# Collect callbacks
event_type = event.get("event_type")
Expand Down
4 changes: 3 additions & 1 deletion wgpu/gui/offscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def present(self, texture):
return data.cast("B", (size[1], size[0], 4))

def draw(self):
"""Perform a draw and return the resulting array as an NxMx4 memoryview object.
"""Perform a draw and get the resulting image.
The image array is returned as an NxMx4 memoryview object.
This object can be converted to a numpy array (without copying data)
using ``np.asarray(arr)``.
"""
Expand Down

0 comments on commit f68d910

Please sign in to comment.