Skip to content

Commit 6d43bf4

Browse files
authored
Implement scheduling (#6)
1 parent 7686503 commit 6d43bf4

26 files changed

+2362
-830
lines changed

docs/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import os
1414
import sys
15-
import shutil
1615

1716

1817
ROOT_DIR = os.path.abspath(os.path.join(__file__, "..", ".."))

docs/gui.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ support for events (interactivity). In the next sections we demonstrates the dif
2020
canvas classes that you can use.
2121

2222

23+
Events
24+
------
25+
26+
To implement interaction with a ``WgpuCanvas``, use the :func:`WgpuCanvasBase.add_event_handler()` method.
27+
Events come in the following flavours:
28+
29+
.. autoclass:: WgpuEventType
30+
:members:
31+
32+
2333
The auto GUI backend
2434
--------------------
2535

examples/gui_demo.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
An example that uses events to trigger some canvas functionality.
3+
4+
A nice demo, and very convenient to test the different backends.
5+
6+
* Can be closed with Escape or by pressing the window close button.
7+
* In both cases, it should print "Close detected" exactly once.
8+
* Hit space to spend 2 seconds doing direct draws.
9+
10+
"""
11+
12+
import time
13+
14+
from rendercanvas.auto import WgpuCanvas, loop
15+
16+
from cube import setup_drawing_sync
17+
18+
19+
canvas = WgpuCanvas(
20+
size=(640, 480),
21+
title="Canvas events on $backend - $fps fps",
22+
max_fps=10,
23+
update_mode="continuous",
24+
present_method="",
25+
)
26+
27+
28+
draw_frame = setup_drawing_sync(canvas)
29+
canvas.request_draw(lambda: (draw_frame(), canvas.request_draw()))
30+
31+
32+
@canvas.add_event_handler("*")
33+
def process_event(event):
34+
if event["event_type"] not in ["pointer_move", "before_draw", "animate"]:
35+
print(event)
36+
37+
if event["event_type"] == "key_down":
38+
if event["key"] == "Escape":
39+
canvas.close()
40+
elif event["key"] == " ":
41+
etime = time.time() + 2
42+
i = 0
43+
while time.time() < etime:
44+
i += 1
45+
canvas.force_draw()
46+
print(f"force-drawed {i} frames in 2s.")
47+
elif event["event_type"] == "close":
48+
# Should see this exactly once, either when pressing escape, or
49+
# when pressing the window close button.
50+
print("Close detected!")
51+
assert canvas.is_closed()
52+
53+
54+
if __name__ == "__main__":
55+
loop.run()

examples/gui_multiple.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
Run triangle and cube examples two canvases.
3+
"""
4+
5+
# test_example = true
6+
7+
from rendercanvas.auto import WgpuCanvas, loop
8+
9+
from triangle import setup_drawing_sync as setup_drawing_sync_triangle
10+
from cube import setup_drawing_sync as setup_drawing_sync_cube
11+
12+
13+
canvas1 = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
14+
draw_frame1 = setup_drawing_sync_triangle(canvas1)
15+
canvas1.request_draw(draw_frame1)
16+
17+
canvas2 = WgpuCanvas(title=f"Cube example on {WgpuCanvas.__name__}")
18+
draw_frame2 = setup_drawing_sync_cube(canvas2)
19+
canvas2.request_draw(draw_frame2)
20+
21+
22+
if __name__ == "__main__":
23+
loop.run()

examples/gui_threading.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Example that renders frames in a separate thread.
3+
4+
This uses an offscreen canvas, the result is only used to print the
5+
frame shape. But one can see how one can e.g. render a movie this way.
6+
7+
Threaded rendering using a real GUI is not supported right now, since
8+
this is tricky to do with both Qt and glfw. Plus in general its a bad
9+
idea to run your UI in anything other than the main thread. In other
10+
words, you should probably only use threaded rendering for off-screen
11+
stuff.
12+
13+
"""
14+
15+
# test_example = true
16+
17+
import time
18+
import threading
19+
20+
from rendercanvas.offscreen import WgpuCanvas
21+
22+
from cube import setup_drawing_sync
23+
24+
25+
# create canvas
26+
canvas = WgpuCanvas()
27+
draw_frame = setup_drawing_sync(canvas)
28+
29+
30+
def main():
31+
frame_count = 0
32+
canvas.request_draw(draw_frame)
33+
34+
while not canvas.is_closed():
35+
image = canvas.draw()
36+
frame_count += 1
37+
print(f"Rendered {frame_count} frames, last shape is {image.shape}")
38+
39+
40+
if __name__ == "__main__":
41+
t1 = threading.Thread(target=main)
42+
t1.start()
43+
44+
# In the main thread, we wait a little
45+
time.sleep(1)
46+
47+
# ... then change the canvas size, and wait some more
48+
canvas.set_logical_size(200, 200)
49+
time.sleep(1)
50+
51+
# Close the canvas to stop the tread
52+
canvas.close()

pyproject.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ glfw = ["glfw>=1.9"]
2727
lint = ["ruff", "pre-commit"]
2828
examples = ["numpy", "wgpu", "glfw", "pyside6"]
2929
docs = ["sphinx>7.2", "sphinx_rtd_theme", "sphinx-gallery"]
30-
tests = ["pytest", "psutil", "glfw", "pyside6"]
30+
tests = ["pytest", "numpy", "psutil", "wgpu", "glfw"]
3131
dev = ["rendercanvas[lint,tests,examples,docs]"]
3232

3333
[project.entry-points."pyinstaller40"]
@@ -54,7 +54,8 @@ line-length = 88
5454
[tool.ruff.lint]
5555
select = ["F", "E", "W", "N", "B", "RUF"]
5656
ignore = [
57-
"E501", # Line too long
58-
"E731", # Do not assign a `lambda` expression, use a `def`
59-
"B007", # Loop control variable `x` not used within loop body
57+
"E501", # Line too long
58+
"E731", # Do not assign a `lambda` expression, use a `def`
59+
"B007", # Loop control variable `x` not used within loop body
60+
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
6061
]

rendercanvas/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
from ._version import __version__, version_info
88
from . import _gui_utils
9-
from .base import WgpuCanvasInterface, WgpuCanvasBase, WgpuAutoGui
9+
from ._events import WgpuEventType
10+
from .base import WgpuCanvasInterface, WgpuCanvasBase, WgpuLoop, WgpuTimer
1011

1112
__all__ = [
1213
"WgpuCanvasInterface",
1314
"WgpuCanvasBase",
14-
"WgpuAutoGui",
15+
"WgpuEventType",
16+
"WgpuLoop",
17+
"WgpuTimer",
1518
]

rendercanvas/_coreutils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def __getitem__(cls, key):
8686

8787
def __repr__(cls):
8888
if cls is BaseEnum:
89-
return "<wgpu.utils.BaseEnum>"
89+
return "<rendercanvas.BaseEnum>"
9090
pkg = cls.__module__.split(".")[0]
9191
name = cls.__name__
9292
options = []

0 commit comments

Comments
 (0)