Skip to content

Commit d25edb1

Browse files
Vipitisalmarklein
andauthored
Update examples to use rendercanvas (#714)
* update examples * make cube reuseable * fix screenshot testing * fix unlimited fps * update subprocess example * update remaining examples * ruff format * add pause example * cleanup * add subprocess loop * use DSL for title * ruff format * import canvas from main * ruff format again --------- Co-authored-by: Almar Klein <[email protected]>
1 parent 470838c commit d25edb1

22 files changed

+188
-143
lines changed

examples/cube.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import wgpu
1616
import numpy as np
1717

18-
from wgpu.gui.auto import WgpuCanvas, run
18+
from rendercanvas.auto import RenderCanvas, loop
1919

2020

2121
# %% Entrypoints (sync and async)
@@ -459,17 +459,15 @@ async def draw_frame_async():
459459
for a in wgpu.gpu.enumerate_adapters_sync():
460460
print(a.summary)
461461

462-
canvas = WgpuCanvas(size=(640, 480), title="wgpu cube example")
463-
464-
draw_frame = setup_drawing_sync(canvas)
465-
466-
467-
def animate():
468-
draw_frame()
469-
canvas.request_draw()
470-
471-
472-
canvas.request_draw(animate)
473462

474463
if __name__ == "__main__":
475-
run()
464+
canvas = RenderCanvas(
465+
size=(640, 480),
466+
title="wgpu cube example at $fps using $backend",
467+
update_mode="continuous",
468+
max_fps=60,
469+
vsync=True,
470+
)
471+
draw_frame = setup_drawing_sync(canvas)
472+
canvas.request_draw(draw_frame)
473+
loop.run()

examples/gui_auto.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
# test_example = true
66

7-
from wgpu.gui.auto import WgpuCanvas, run
7+
from rendercanvas.auto import RenderCanvas, loop
88

99
try:
1010
from .triangle import setup_drawing_sync
1111
except ImportError:
1212
from triangle import setup_drawing_sync
1313

14-
canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
14+
canvas = RenderCanvas(title="Triangle example on $backend")
1515
draw_frame = setup_drawing_sync(canvas)
1616

1717

@@ -22,4 +22,4 @@ def animate():
2222

2323

2424
if __name__ == "__main__":
25-
run()
25+
loop.run()

examples/gui_direct.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
import glfw
1515

1616
from wgpu.backends.wgpu_native import GPUCanvasContext
17-
from wgpu.gui.glfw import get_glfw_present_methods, poll_glfw_briefly
17+
from rendercanvas.glfw import get_glfw_present_methods, poll_glfw_briefly, enable_glfw
1818

1919
# from triangle import setup_drawing_sync
2020
from cube import setup_drawing_sync
2121

2222
# Setup glfw
23-
glfw.init()
23+
enable_glfw()
2424
atexit.register(glfw.terminate)
2525

2626

examples/gui_events.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
A simple example to demonstrate events.
33
"""
44

5-
from wgpu.gui.auto import WgpuCanvas, run
5+
from rendercanvas.auto import RenderCanvas, loop
66

77

8-
canvas = WgpuCanvas(size=(640, 480), title="wgpu events")
8+
canvas = RenderCanvas(size=(640, 480), title="wgpu events")
99

1010

1111
@canvas.add_event_handler("*")
@@ -15,4 +15,4 @@ def process_event(event):
1515

1616

1717
if __name__ == "__main__":
18-
run()
18+
loop.run()

examples/gui_glfw.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""
2-
Run triangle/cube example in the glfw GUI backend.
2+
Run triangle/cube example in the glfw rendercanvas backend.
33
"""
44

55
# run_example = false
66

7-
from wgpu.gui.glfw import WgpuCanvas, run
7+
from rendercanvas.glfw import GlfwRenderCanvas, loop
88

9-
from triangle import setup_drawing_sync
10-
# from cube import setup_drawing_sync
9+
# from triangle import setup_drawing_sync
10+
from cube import setup_drawing_sync
1111

1212

13-
canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
13+
canvas = GlfwRenderCanvas(title=f"Triangle example on {GlfwRenderCanvas.__name__}")
1414
draw_frame = setup_drawing_sync(canvas)
1515

1616

@@ -21,4 +21,4 @@ def animate():
2121

2222

2323
if __name__ == "__main__":
24-
run()
24+
loop.run()

examples/gui_qt.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@
1111
for lib in ("PySide6", "PyQt6", "PySide2", "PyQt5"):
1212
try:
1313
QtWidgets = importlib.import_module(".QtWidgets", lib)
14+
print(f"Using {lib} for Qt GUI")
1415
break
1516
except ModuleNotFoundError:
1617
pass
1718

1819

19-
from wgpu.gui.qt import WgpuCanvas # noqa: E402
20+
from rendercanvas.qt import RenderCanvas # noqa: E402
2021

2122
from triangle import setup_drawing_sync # noqa: E402
2223

2324

2425
app = QtWidgets.QApplication([])
25-
canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
26+
canvas = RenderCanvas(title="Triangle example on $backend")
2627

2728
draw_frame = setup_drawing_sync(canvas)
2829

examples/gui_qt_asyncio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import asyncio
1919

2020
from PySide6 import QtWidgets, QtAsyncio
21-
from wgpu.gui.qt import WgpuWidget
21+
from rendercanvas.qt import QRenderWidget
2222
from triangle import setup_drawing_sync
2323

2424

@@ -49,7 +49,7 @@ def __init__(self):
4949
splitter = QtWidgets.QSplitter()
5050

5151
self.button = QtWidgets.QPushButton("Hello world", self)
52-
self.canvas = WgpuWidget(splitter)
52+
self.canvas = QRenderWidget(splitter)
5353
self.output = QtWidgets.QTextEdit(splitter)
5454

5555
# self.button.clicked.connect(self.whenButtonClicked) # see above :(

examples/gui_qt_embed.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import time
1111
import importlib
1212

13-
from triangle import setup_drawing_sync
13+
from cube import setup_drawing_sync
1414

1515
# For the sake of making this example Just Work, we try multiple QT libs
1616
for lib in ("PySide6", "PyQt6", "PySide2", "PyQt5"):
@@ -20,33 +20,39 @@
2020
except ModuleNotFoundError:
2121
pass
2222

23-
from wgpu.gui.qt import WgpuWidget # noqa: E402
23+
from rendercanvas.qt import QRenderWidget # noqa: E402
2424

2525

2626
class ExampleWidget(QtWidgets.QWidget):
2727
def __init__(self):
2828
super().__init__()
2929
self.resize(640, 480)
30-
self.setWindowTitle("wgpu triangle embedded in a qt app")
30+
self.setWindowTitle("wgpu cube embedded in a qt app")
3131

3232
splitter = QtWidgets.QSplitter()
3333

3434
self.button = QtWidgets.QPushButton("Hello world", self)
35-
self.canvas = WgpuWidget(splitter)
35+
self.button2 = QtWidgets.QPushButton("pause", self)
36+
self.canvas = QRenderWidget(splitter, update_mode="continuous")
3637
self.output = QtWidgets.QTextEdit(splitter)
3738

3839
self.button.clicked.connect(self.whenButtonClicked)
40+
self.button2.clicked.connect(self.whenButton2Clicked)
3941

4042
splitter.addWidget(self.canvas)
4143
splitter.addWidget(self.output)
4244
splitter.setSizes([400, 300])
4345

46+
button_layout = QtWidgets.QVBoxLayout()
47+
button_layout.addWidget(self.button)
48+
button_layout.addWidget(self.button2)
4449
layout = QtWidgets.QHBoxLayout()
45-
layout.addWidget(self.button, 0)
50+
layout.addLayout(button_layout)
4651
layout.addWidget(splitter, 1)
4752
self.setLayout(layout)
4853

4954
self.show()
55+
self._paused = False
5056

5157
def addLine(self, line):
5258
t = self.output.toPlainText()
@@ -56,6 +62,19 @@ def addLine(self, line):
5662
def whenButtonClicked(self):
5763
self.addLine(f"Clicked at {time.time():0.1f}")
5864

65+
def whenButton2Clicked(self):
66+
# showcases how rendercanvas allows changes to sheduling interactively
67+
if self._paused:
68+
self.canvas.set_update_mode("continuous", max_fps=60)
69+
self.button2.setText("pause")
70+
self._paused = False
71+
else:
72+
# note: the cube example bases rotation on unix time, which we don't pause with this button
73+
# with "ondemand", resize events such as the window or the splitter will still trigger a draw!
74+
self.canvas.set_update_mode("ondemand")
75+
self.button2.setText("resume")
76+
self._paused = True
77+
5978

6079
app = QtWidgets.QApplication([])
6180
example = ExampleWidget()

examples/gui_subprocess.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,72 +16,85 @@
1616
import time
1717
import subprocess
1818

19-
from wgpu.gui import WgpuCanvasBase
19+
from rendercanvas.asyncio import loop
20+
from rendercanvas.base import BaseRenderCanvas, BaseCanvasGroup
2021

2122
# Import the function that we must call to run the visualization
22-
from triangle import setup_drawing_sync
23-
# from cube import setup_drawing_sync
23+
# from triangle import setup_drawing_sync
24+
from cube import setup_drawing_sync
2425

2526

2627
code = """
2728
import sys
2829
import json
29-
from PySide6 import QtWidgets # Use either PySide6 or PyQt6
30-
from wgpu.gui.qt import WgpuCanvas
30+
from PyQt6 import QtWidgets # Use either PySide6 or PyQt6
31+
from rendercanvas.qt import RenderCanvas
3132
3233
app = QtWidgets.QApplication([])
33-
canvas = WgpuCanvas(title="wgpu triangle in Qt subprocess")
34+
canvas = RenderCanvas(title="wgpu cube in Qt subprocess", update_mode="ondemand")
3435
35-
print(json.dumps(canvas.get_present_methods()))
36+
print(json.dumps(canvas._subwidget._rc_get_present_methods()))
3637
print(canvas.get_physical_size())
3738
sys.stdout.flush()
3839
39-
app.exec_()
40+
app.exec()
4041
"""
4142

4243

43-
class ProxyCanvas(WgpuCanvasBase):
44+
class ProxyCanvasGroup(BaseCanvasGroup):
45+
pass
46+
47+
48+
class ProxyCanvas(BaseRenderCanvas):
49+
_rc_canvas_group = ProxyCanvasGroup(loop)
50+
4451
def __init__(self):
4552
super().__init__()
4653
self._present_methods = json.loads(p.stdout.readline().decode())
54+
print(self._present_methods)
4755
self._psize = tuple(
4856
int(x) for x in p.stdout.readline().decode().strip().strip("()").split(",")
4957
)
5058
print(self._psize)
5159
time.sleep(0.2)
5260

53-
def get_present_methods(self):
61+
def _rc_get_present_methods(self):
5462
return self._present_methods
5563

56-
def get_physical_size(self):
64+
def _rc_request_draw(self):
65+
loop = self._rc_canvas_group.get_loop()
66+
loop.call_soon(self._draw_frame_and_present)
67+
68+
def _rc_get_physical_size(self):
5769
return self._psize
5870

59-
def get_pixel_ratio(self):
71+
def _rc_get_pixel_ratio(self):
6072
return 1
6173

62-
def get_logical_size(self):
74+
def _rc_get_logical_size(self):
6375
return self._psize
6476

65-
def set_logical_size(self, width, height):
77+
def _rc_set_logical_size(self, width, height):
6678
pass
6779

68-
def close(self):
80+
def _rc_close(self):
6981
p.kill()
7082

71-
def is_closed(self):
72-
raise NotImplementedError()
73-
74-
def _request_draw(self):
75-
self.draw_frame()
83+
def _rc_get_closed(self):
84+
return_code = p.poll()
85+
return return_code is not None
7686

7787

7888
# Create subprocess
7989
p = subprocess.Popen([sys.executable, "-c", code], stdout=subprocess.PIPE)
8090

8191
# Create a canvas that maps to the window of that subprocess
8292
canvas = ProxyCanvas()
93+
canvas.set_update_mode("continuous")
8394

84-
# Go!
95+
# Register our draw function
8596
draw_frame = setup_drawing_sync(canvas)
8697
canvas.request_draw(draw_frame)
87-
time.sleep(3)
98+
99+
# Start the event loop
100+
loop.run()

examples/gui_wx.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
# run_example = false
66

77
import wx
8-
from wgpu.gui.wx import WgpuCanvas
8+
from rendercanvas.wx import RenderCanvas
99

1010
from triangle import setup_drawing_sync
1111
# from cube import setup_drawing_sync
1212

1313

1414
app = wx.App()
15-
canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
15+
canvas = RenderCanvas(title="Triangle example on $backend")
1616

1717
draw_func = setup_drawing_sync(canvas)
1818
canvas.request_draw(draw_func)

examples/gui_wx_embed.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@
55
# run_example = false
66

77
import wx
8-
from wgpu.gui.wx import WgpuWidget
8+
from rendercanvas.wx import WxRenderWidget
99

10-
from triangle import setup_drawing_sync
10+
from triangle import setup_drawing_sync as setup_drawing_sync_triangle
11+
from cube import setup_drawing_sync as setup_drawing_sync_cube
1112

1213

1314
class Example(wx.Frame):
1415
def __init__(self):
15-
super().__init__(None, title="wgpu triangle embedded in a wx app")
16+
super().__init__(None, title="wgpu triangle and cube embedded in a wx app")
1617
self.SetSize(640, 480)
1718

1819
splitter = wx.SplitterWindow(self)
1920

2021
self.button = wx.Button(self, -1, "Hello world")
21-
self.canvas1 = WgpuWidget(splitter)
22-
self.canvas2 = WgpuWidget(splitter)
22+
self.canvas1 = WxRenderWidget(splitter)
23+
self.canvas2 = WxRenderWidget(splitter, update_mode="continuous")
2324

2425
splitter.SplitVertically(self.canvas1, self.canvas2)
2526
splitter.SetSashGravity(0.5)
@@ -35,8 +36,8 @@ def __init__(self):
3536
app = wx.App()
3637
example = Example()
3738

38-
draw_frame1 = setup_drawing_sync(example.canvas1)
39-
draw_frame2 = setup_drawing_sync(example.canvas2)
39+
draw_frame1 = setup_drawing_sync_triangle(example.canvas1)
40+
draw_frame2 = setup_drawing_sync_cube(example.canvas2)
4041

4142
example.canvas1.request_draw(draw_frame1)
4243
example.canvas2.request_draw(draw_frame2)

0 commit comments

Comments
 (0)