Skip to content

Commit

Permalink
Implement scheduling (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
almarklein authored Nov 6, 2024
1 parent 7686503 commit 6d43bf4
Show file tree
Hide file tree
Showing 26 changed files with 2,362 additions and 830 deletions.
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import os
import sys
import shutil


ROOT_DIR = os.path.abspath(os.path.join(__file__, "..", ".."))
Expand Down
10 changes: 10 additions & 0 deletions docs/gui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ support for events (interactivity). In the next sections we demonstrates the dif
canvas classes that you can use.


Events
------

To implement interaction with a ``WgpuCanvas``, use the :func:`WgpuCanvasBase.add_event_handler()` method.
Events come in the following flavours:

.. autoclass:: WgpuEventType
:members:


The auto GUI backend
--------------------

Expand Down
55 changes: 55 additions & 0 deletions examples/gui_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
An example that uses events to trigger some canvas functionality.
A nice demo, and very convenient to test the different backends.
* Can be closed with Escape or by pressing the window close button.
* In both cases, it should print "Close detected" exactly once.
* Hit space to spend 2 seconds doing direct draws.
"""

import time

from rendercanvas.auto import WgpuCanvas, loop

from cube import setup_drawing_sync


canvas = WgpuCanvas(
size=(640, 480),
title="Canvas events on $backend - $fps fps",
max_fps=10,
update_mode="continuous",
present_method="",
)


draw_frame = setup_drawing_sync(canvas)
canvas.request_draw(lambda: (draw_frame(), canvas.request_draw()))


@canvas.add_event_handler("*")
def process_event(event):
if event["event_type"] not in ["pointer_move", "before_draw", "animate"]:
print(event)

if event["event_type"] == "key_down":
if event["key"] == "Escape":
canvas.close()
elif event["key"] == " ":
etime = time.time() + 2
i = 0
while time.time() < etime:
i += 1
canvas.force_draw()
print(f"force-drawed {i} frames in 2s.")
elif event["event_type"] == "close":
# Should see this exactly once, either when pressing escape, or
# when pressing the window close button.
print("Close detected!")
assert canvas.is_closed()


if __name__ == "__main__":
loop.run()
23 changes: 23 additions & 0 deletions examples/gui_multiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Run triangle and cube examples two canvases.
"""

# test_example = true

from rendercanvas.auto import WgpuCanvas, loop

from triangle import setup_drawing_sync as setup_drawing_sync_triangle
from cube import setup_drawing_sync as setup_drawing_sync_cube


canvas1 = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
draw_frame1 = setup_drawing_sync_triangle(canvas1)
canvas1.request_draw(draw_frame1)

canvas2 = WgpuCanvas(title=f"Cube example on {WgpuCanvas.__name__}")
draw_frame2 = setup_drawing_sync_cube(canvas2)
canvas2.request_draw(draw_frame2)


if __name__ == "__main__":
loop.run()
52 changes: 52 additions & 0 deletions examples/gui_threading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Example that renders frames in a separate thread.
This uses an offscreen canvas, the result is only used to print the
frame shape. But one can see how one can e.g. render a movie this way.
Threaded rendering using a real GUI is not supported right now, since
this is tricky to do with both Qt and glfw. Plus in general its a bad
idea to run your UI in anything other than the main thread. In other
words, you should probably only use threaded rendering for off-screen
stuff.
"""

# test_example = true

import time
import threading

from rendercanvas.offscreen import WgpuCanvas

from cube import setup_drawing_sync


# create canvas
canvas = WgpuCanvas()
draw_frame = setup_drawing_sync(canvas)


def main():
frame_count = 0
canvas.request_draw(draw_frame)

while not canvas.is_closed():
image = canvas.draw()
frame_count += 1
print(f"Rendered {frame_count} frames, last shape is {image.shape}")


if __name__ == "__main__":
t1 = threading.Thread(target=main)
t1.start()

# In the main thread, we wait a little
time.sleep(1)

# ... then change the canvas size, and wait some more
canvas.set_logical_size(200, 200)
time.sleep(1)

# Close the canvas to stop the tread
canvas.close()
9 changes: 5 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ glfw = ["glfw>=1.9"]
lint = ["ruff", "pre-commit"]
examples = ["numpy", "wgpu", "glfw", "pyside6"]
docs = ["sphinx>7.2", "sphinx_rtd_theme", "sphinx-gallery"]
tests = ["pytest", "psutil", "glfw", "pyside6"]
tests = ["pytest", "numpy", "psutil", "wgpu", "glfw"]
dev = ["rendercanvas[lint,tests,examples,docs]"]

[project.entry-points."pyinstaller40"]
Expand All @@ -54,7 +54,8 @@ line-length = 88
[tool.ruff.lint]
select = ["F", "E", "W", "N", "B", "RUF"]
ignore = [
"E501", # Line too long
"E731", # Do not assign a `lambda` expression, use a `def`
"B007", # Loop control variable `x` not used within loop body
"E501", # Line too long
"E731", # Do not assign a `lambda` expression, use a `def`
"B007", # Loop control variable `x` not used within loop body
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
]
7 changes: 5 additions & 2 deletions rendercanvas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

from ._version import __version__, version_info
from . import _gui_utils
from .base import WgpuCanvasInterface, WgpuCanvasBase, WgpuAutoGui
from ._events import WgpuEventType
from .base import WgpuCanvasInterface, WgpuCanvasBase, WgpuLoop, WgpuTimer

__all__ = [
"WgpuCanvasInterface",
"WgpuCanvasBase",
"WgpuAutoGui",
"WgpuEventType",
"WgpuLoop",
"WgpuTimer",
]
2 changes: 1 addition & 1 deletion rendercanvas/_coreutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def __getitem__(cls, key):

def __repr__(cls):
if cls is BaseEnum:
return "<wgpu.utils.BaseEnum>"
return "<rendercanvas.BaseEnum>"
pkg = cls.__module__.split(".")[0]
name = cls.__name__
options = []
Expand Down
Loading

0 comments on commit 6d43bf4

Please sign in to comment.