diff --git a/pyglet/app/__init__.py b/pyglet/app/__init__.py index e232d6008..b671234c4 100644 --- a/pyglet/app/__init__.py +++ b/pyglet/app/__init__.py @@ -65,7 +65,7 @@ class AppException(Exception): """ -def run(interval=1 / 60): +def run(interval: float = 1/60) -> None: """Begin processing events, scheduled functions and window updates. This is a convenience function, equivalent to:: @@ -76,12 +76,12 @@ def run(interval=1 / 60): event_loop.run(interval) -def exit(): +def exit() -> None: """Exit the application event loop. Causes the application event loop to finish, if an event loop is currently running. The application may not necessarily exit (for example, there may - be additional code following the `run` invocation). + be additional code following the ``run`` invocation). This is a convenience function, equivalent to:: diff --git a/pyglet/app/base.py b/pyglet/app/base.py index 24a1ce3d3..4a7bbf3a4 100644 --- a/pyglet/app/base.py +++ b/pyglet/app/base.py @@ -1,11 +1,19 @@ +from __future__ import annotations + import sys import queue import threading +from typing import TYPE_CHECKING, Any, Callable + from pyglet import app from pyglet import clock from pyglet import event +if TYPE_CHECKING: + from pyglet.event import EventDispatcher + from pyglet.window import BaseWindow + _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run @@ -14,19 +22,17 @@ class PlatformEventLoop: .. versionadded:: 1.2 """ - def __init__(self): + def __init__(self) -> None: self._event_queue = queue.Queue() self._is_running = threading.Event() - def is_running(self): + def is_running(self) -> bool: """Return True if the event loop is currently processing, or False if it is blocked or not activated. - - :rtype: bool """ return self._is_running.is_set() - def post_event(self, dispatcher, event, *args): + def post_event(self, dispatcher: EventDispatcher, event: str, *args: Any) -> None: """Post an event into the main application thread. The event is queued internally until the :py:meth:`run` method's thread @@ -37,20 +43,11 @@ def post_event(self, dispatcher, event, *args): example, from within an event handler), the event may be dispatched within the same runloop iteration or the next one; the choice is nondeterministic. - - :Parameters: - `dispatcher` : EventDispatcher - Dispatcher to process the event. - `event` : str - Event name. - `args` : sequence - Arguments to pass to the event handlers. - """ self._event_queue.put((dispatcher, event, args)) self.notify() - def dispatch_posted_events(self): + def dispatch_posted_events(self) -> None: """Immediately dispatch all pending events. Normally this is called automatically by the runloop iteration. @@ -65,25 +62,25 @@ def dispatch_posted_events(self): # weakly-referenced object no longer exists pass - def notify(self): + def notify(self) -> None: """Notify the event loop that something needs processing. If the event loop is blocked, it will unblock and perform an iteration immediately. If the event loop is running, another iteration is - scheduled for immediate execution afterwards. + scheduled for immediate execution afterward. """ raise NotImplementedError('abstract') - def start(self): + def start(self) -> None: pass - def step(self, timeout=None): + def step(self, timeout: None | float = None): raise NotImplementedError('abstract') - def set_timer(self, func, interval): + def set_timer(self, func: Callable, interval: float) -> None: pass - def stop(self): + def stop(self) -> None: pass @@ -105,7 +102,7 @@ class EventLoop(event.EventDispatcher): _has_exit_condition = None _has_exit = False - def __init__(self): + def __init__(self) -> None: self._has_exit_condition = threading.Condition() self.clock = clock.get_default() self.is_running = False @@ -114,30 +111,34 @@ def __init__(self): def _redraw_windows(dt): # Redraw all windows for window in app.windows: - window.switch_to() - window.dispatch_event('on_draw') - window.dispatch_event('on_refresh', dt) - window.flip() + window.draw(dt) - def run(self, interval=1/60): + def run(self, interval: None | float = 1/60): """Begin processing events, scheduled functions and window updates. - :Parameters: - `interval` : float or None [default: 1/60] - Windows redraw interval, in seconds (framerate). - If `interval == 0`, windows will redraw at maximum rate. - If `interval is None`, Pyglet will not call its redraw function. - The user must schedule (or call on demand) a custom redraw - function for each window, allowing a custom framerate per window. - (see example in documentation) + This method enters into the main event loop and, if the ``interval`` + argument is not changed, schedules calling the :py:meth:`pyglet.window.Window.draw` + method. You can change the ``interval`` argument to suit your needs. - This method returns when :py:attr:`has_exit` is set to True. - - Developers are discouraged from overriding this method, as the + Args: + interval: + Windows redraw interval, in seconds (framerate). + If ``interval == 0``, windows will redraw as fast as possible. + This can saturate a CPU core, so do not do this unless GPU bound. + If ``interval is None``, pyglet will not schedule calls to the + :py:meth:`pyglet.window.Window.draw` method. Users must schedule + this themselves for each Window (or call it on-demand). This allows + setting a custom framerate per window, or changing framerate during + runtime (see example in the documentation). + + This method returns when :py:attr:`has_exit` is set to True. IE: when + :py:meth:`exit` is called. + + Developers are discouraged from overriding the ``run`` method, as the implementation is platform-specific. """ if interval is None: - # User application will manage a custom _redraw_windows() method + # User must call Window.draw() themselves. pass elif interval == 0: self.clock.schedule(self._redraw_windows) @@ -167,7 +168,7 @@ def run(self, interval=1/60): self.dispatch_event('on_exit') platform_event_loop.stop() - def enter_blocking(self): + def enter_blocking(self) -> None: """Called by pyglet internal processes when the operating system is about to block due to a user interaction. For example, this is common when the user begins resizing or moving a window. @@ -185,7 +186,7 @@ def enter_blocking(self): app.platform_event_loop.set_timer(self._blocking_timer, timeout) @staticmethod - def exit_blocking(): + def exit_blocking() -> None: """Called by pyglet internal processes when the blocking operation completes. See :py:meth:`enter_blocking`. """ @@ -195,7 +196,7 @@ def _blocking_timer(self): timeout = self.idle() app.platform_event_loop.set_timer(self._blocking_timer, timeout) - def idle(self): + def idle(self) -> None | float: """Called during each iteration of the event loop. The method is called immediately after any window events (i.e., after @@ -216,9 +217,9 @@ def idle(self): code execute at regular intervals, use the :py:func:`pyglet.clock.schedule` methods. - :rtype: float - :return: The number of seconds before the idle method should - be called again, or `None` to block for user input. + Returns: + The number of seconds before the idle method should + be called again, or ``None`` to block for user input. """ dt = self.clock.update_time() self.clock.call_scheduled_functions(dt) @@ -227,15 +228,12 @@ def idle(self): return self.clock.get_sleep_time(True) @property - def has_exit(self): + def has_exit(self) -> bool: """Flag indicating if the event loop will exit in the next iteration. When set, all waiting threads are interrupted (see :py:meth:`sleep`). Thread-safe since pyglet 1.2. - - :see: `exit` - :type: bool """ self._has_exit_condition.acquire() result = self._has_exit @@ -243,13 +241,13 @@ def has_exit(self): return result @has_exit.setter - def has_exit(self, value): + def has_exit(self, value: bool) -> None: self._has_exit_condition.acquire() self._has_exit = value self._has_exit_condition.notify() self._has_exit_condition.release() - def exit(self): + def exit(self) -> None: """Safely exit the event loop at the end of the current iteration. This method is a thread-safe equivalent for setting @@ -259,20 +257,16 @@ def exit(self): self.has_exit = True app.platform_event_loop.notify() - def sleep(self, timeout): + def sleep(self, timeout: float) -> bool: """Wait for some amount of time, or until the :py:attr:`has_exit` flag is set or :py:meth:`exit` is called. This method is thread-safe. - :Parameters: - `timeout` : float - Time to wait, in seconds. + Args: + timeout: Time to sleep, in seconds. .. versionadded:: 1.2 - - :rtype: bool - :return: ``True`` if the `has_exit` flag is set, otherwise ``False``. """ self._has_exit_condition.acquire() self._has_exit_condition.wait(timeout) @@ -280,13 +274,15 @@ def sleep(self, timeout): self._has_exit_condition.release() return result - def on_window_close(self, window): + def on_window_close(self, window: BaseWindow): """Default window close handler.""" if len(app.windows) == 0: self.exit() if _is_pyglet_doc_run: - def on_window_close(self, window): + # Events + + def on_window_close(self, window: BaseWindow): """A window was closed. This event is dispatched when a window is closed. It is not @@ -296,8 +292,6 @@ def on_window_close(self, window): The default handler calls :py:meth:`exit` if no more windows are open. You can override this handler to base your application exit on some other policy. - - :event: """ def on_enter(self): @@ -306,8 +300,6 @@ def on_enter(self): This is dispatched when the event loop is prepared to enter the main run loop, and represents the last chance for an application to initialise itself. - - :event: """ def on_exit(self): @@ -316,8 +308,6 @@ def on_exit(self): After dispatching this event, the :py:meth:`run` method returns (the application may not actually exit if you have more code following the :py:meth:`run` invocation). - - :event: """ diff --git a/pyglet/input/base.py b/pyglet/input/base.py index 0391d8214..5868b84b0 100644 --- a/pyglet/input/base.py +++ b/pyglet/input/base.py @@ -718,7 +718,7 @@ def _initialize_controls(self) -> None: warnings.warn(f"Could not find control '{name}' with index '{relation.index}'.") continue - def open(self, window: None | BaseWindow, exclusive: bool = False) -> None: + def open(self, window: None | BaseWindow = None, exclusive: bool = False) -> None: """Open the controller. See `Device.open`. """ self.device.open(window, exclusive)