Skip to content

Refactor initialization and re-order methods #11

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

Merged
merged 5 commits into from
Nov 11, 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
2 changes: 1 addition & 1 deletion examples/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from rendercanvas.auto import RenderCanvas, loop

from cube import setup_drawing_sync
from rendercanvas.utils.cube import setup_drawing_sync


canvas = RenderCanvas(
Expand Down
2 changes: 1 addition & 1 deletion examples/qt_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self):
splitter = QtWidgets.QSplitter()

self.button = QtWidgets.QPushButton("Hello world", self)
self.canvas = QRenderWidget(splitter)
self.canvas = QRenderWidget(splitter, update_mode="continuous")
self.output = QtWidgets.QTextEdit(splitter)

self.button.clicked.connect(self.whenButtonClicked)
Expand Down
2 changes: 1 addition & 1 deletion examples/qt_app_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self):

# todo: use update_mode = 'continuous' when that feature has arrived
self.button = QtWidgets.QPushButton("Hello world", self)
self.canvas = QRenderWidget(splitter)
self.canvas = QRenderWidget(splitter, update_mode="continuous")
self.output = QtWidgets.QTextEdit(splitter)

# self.button.clicked.connect(self.whenButtonClicked) # see above :(
Expand Down
33 changes: 20 additions & 13 deletions examples/wx_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
An example demonstrating a wx app with a wgpu viz inside.
"""

import time

import wx
from rendercanvas.wx import RenderWidget

Expand All @@ -13,30 +15,35 @@ def __init__(self):
super().__init__(None, title="wgpu triangle embedded in a wx app")
self.SetSize(640, 480)

splitter = wx.SplitterWindow(self)

# Using present_method 'image' because it reports "The surface texture is suboptimal"
self.canvas = RenderWidget(
self, update_mode="continuous", present_method="image"
)
self.button = wx.Button(self, -1, "Hello world")
self.canvas1 = RenderWidget(splitter)
self.canvas2 = RenderWidget(splitter)

splitter.SplitVertically(self.canvas1, self.canvas2)
splitter.SetSashGravity(0.5)
self.output = wx.StaticText(self)

sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.button, 0, wx.EXPAND)
sizer.Add(splitter, 1, wx.EXPAND)
sizer.Add(self.canvas, 1, wx.EXPAND)
sizer.Add(self.output, 1, wx.EXPAND)
self.SetSizer(sizer)

self.button.Bind(wx.EVT_BUTTON, self.OnClicked)

# Force the canvas to be shown, so that it gets a valid handle.
# Otherwise GetHandle() is initially 0, and getting a surface will fail.
self.Show()

def OnClicked(self, event): # noqa: N802
t = self.output.GetLabel()
t += f"\nClicked at {time.time():0.1f}"
self.output.SetLabel(t)


app = wx.App()
example = Example()

draw_frame1 = setup_drawing_sync(example.canvas1)
draw_frame2 = setup_drawing_sync(example.canvas2)

example.canvas1.request_draw(draw_frame1)
example.canvas2.request_draw(draw_frame2)
draw_frame = setup_drawing_sync(example.canvas)
example.canvas.request_draw(draw_frame)

app.MainLoop()
16 changes: 8 additions & 8 deletions rendercanvas/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,14 @@ class Scheduler:
# Note that any extra draws, e.g. via force_draw() or due to window resizes,
# don't affect the scheduling loop; they are just extra draws.

def __init__(self, canvas, loop, *, mode="ondemand", min_fps=1, max_fps=30):
def __init__(self, canvas, events, loop, *, mode="ondemand", min_fps=1, max_fps=30):
assert loop is not None

# We don't keep a ref to the canvas to help gc. This scheduler object can be
# referenced via a callback in an event loop, but it won't prevent the canvas
# from being deleted!
self._canvas_ref = weakref.ref(canvas)
self._events = canvas._events
self._events = events
# ... = canvas.get_context() -> No, context creation should be lazy!

# Scheduling variables
Expand All @@ -329,8 +331,6 @@ def __init__(self, canvas, loop, *, mode="ondemand", min_fps=1, max_fps=30):
# Keep track of fps
self._draw_stats = 0, time.perf_counter()

assert loop is not None

# Initialise the timer that runs our scheduling loop.
# Note that the backend may do a first draw earlier, starting the loop, and that's fine.
self._last_tick_time = -0.1
Expand Down Expand Up @@ -390,22 +390,22 @@ def _tick(self):

if self._mode == "fastest":
# fastest: draw continuously as fast as possible, ignoring fps settings.
canvas._request_draw()
canvas._rc_request_draw()

elif self._mode == "continuous":
# continuous: draw continuously, aiming for a steady max framerate.
canvas._request_draw()
canvas._rc_request_draw()

elif self._mode == "ondemand":
# ondemand: draw when needed (detected by calls to request_draw).
# Aim for max_fps when drawing is needed, otherwise min_fps.
if self._draw_requested:
canvas._request_draw()
canvas._rc_request_draw()
elif (
self._min_fps > 0
and time.perf_counter() - self._last_draw_time > 1 / self._min_fps
):
canvas._request_draw()
canvas._rc_request_draw()
else:
self._schedule_next_tick()

Expand Down
Loading