Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement scheduling #6

Merged
merged 6 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading