Source code for arcade.window_commands

"""
This submodule has functions that control opening, closing, rendering,
and otherwise managing windows.
It also has commands for scheduling pauses and scheduling interval functions.
"""

from __future__ import annotations

import gc
import os
from typing import TYPE_CHECKING, Callable

import pyglet

from arcade.types import RGBA255, Color

if TYPE_CHECKING:
    from arcade import Window
    from arcade.application import View

_window: Window | None = None

__all__ = [
    "get_display_size",
    "get_window",
    "set_window",
    "close_window",
    "run",
    "exit",
    "start_render",
    "finish_render",
    "set_background_color",
    "schedule",
    "unschedule",
    "schedule_once",
]


[docs] def get_display_size(screen_id: int = 0) -> tuple[int, int]: """ Return the width and height of a monitor. The size of the primary monitor is returned by default. Args: screen_id: The screen number Returns: Tuple containing the width and height of the screen """ display = pyglet.display.Display() # type: ignore # pending: pyglet fixes import tricks screen = display.get_screens()[screen_id] return screen.width, screen.height
[docs] def get_window() -> "Window": """ Return a handle to the current window. :return: Handle to the current window. """ if _window is None: raise RuntimeError( ("No window is active. " "It has not been created yet, or it was closed.") ) return _window
[docs] def set_window(window: Window | None) -> None: """ Set a handle to the current window. Args: window: Handle to the current window. """ global _window _window = window
[docs] def close_window() -> None: """ Closes the current window, and then runs garbage collection. The garbage collection is necessary to prevent crashing when opening/closing windows rapidly (usually during unit tests). """ global _window if _window is None: return _window.close() _window = None # Have to do a garbage collection or Python will crash # if we do a lot of window open and closes. Like for # unit tests. gc.collect()
[docs] def run(view: View | None = None) -> None: """ Run the main loop. Optionally start with a specified view. After the window has been set up, and the event hooks are in place, this is usually one of the last commands on the main program. This is a blocking function starting pyglet's event loop meaning it will start to dispatch events such as ``on_draw`` and ``on_update``. Args: view: The view to display when starting the run. Defaults to None. """ window = get_window() if view is not None: window.show_view(view) # Used in some unit test if os.environ.get("ARCADE_TEST"): window.on_update(1.0 / 60.0) window.on_draw() elif window.headless: # We are entering headless more an will emulate an event loop import time # Ensure the initial delta time is not 0 to be # more in line with how a normal window works. delta_time = window._draw_rate last_time = time.perf_counter() # As long as we have a context -- while window.context: # Select active view or window active = window.current_view or window active.on_update(delta_time) if window.context: active.on_draw() # windwow could be closed in on_draw if window.context: window.flip() now = time.perf_counter() delta_time, last_time = now - last_time, now else: import sys if sys.platform != "win32": # For non windows platforms, just do pyglet run pyglet.app.run(window._draw_rate) else: # Ok, some Windows platforms have a timer resolution > 15 ms. That can # drop our FPS to 32 FPS or so. This reduces resolution so we can keep # FPS up. import contextlib import ctypes from ctypes import wintypes winmm = ctypes.WinDLL("winmm") class TIMECAPS(ctypes.Structure): _fields_ = (("wPeriodMin", wintypes.UINT), ("wPeriodMax", wintypes.UINT)) def _check_time_err(err, func, args): if err: raise WindowsError("%s error %d" % (func.__name__, err)) return args winmm.timeGetDevCaps.errcheck = _check_time_err winmm.timeBeginPeriod.errcheck = _check_time_err winmm.timeEndPeriod.errcheck = _check_time_err @contextlib.contextmanager def timer_resolution(msecs=0): caps = TIMECAPS() winmm.timeGetDevCaps(ctypes.byref(caps), ctypes.sizeof(caps)) msecs = min(max(msecs, caps.wPeriodMin), caps.wPeriodMax) winmm.timeBeginPeriod(msecs) yield winmm.timeEndPeriod(msecs) with timer_resolution(msecs=10): pyglet.app.run(window._draw_rate)
[docs] def exit() -> None: """ Exits the application. """ pyglet.app.exit()
[docs] def start_render(pixelated=False, blend=True) -> None: """ Start recording drawing functions into an offscreen buffer. Call :py:func:`arcade.finish_render` to stop recording. The start_render/finish_render calls can only be called once. When running Arcade this buffer will be presented to the screen. A few configuration options are available in this function. Args: pixelated: If True, the buffer will be be pixelated when resized. Otherwise, it will be smooth. blend: If alpha blending """ from arcade.start_finish_data import StartFinishRenderData window = get_window() if window._start_finish_render_data is not None: raise RuntimeError( ( "start_render() can only be called once during the application's lifetime " "and should only be used when calling draw functions at module level in " "a simple script to produce a static image. If you are seeing this error " "you likely intended to call clear() instead." ) ) window._start_finish_render_data = StartFinishRenderData(pixelated=pixelated, blend=blend) window._start_finish_render_data.begin()
[docs] def finish_render() -> None: """ Stop recording drawing functions into an offscreen buffer. :py:func:`arcade.start_render` should be called before this function. :py:func:`arcade.run` can be called after this function to present the buffer. """ window = get_window() if window._start_finish_render_data is None: raise RuntimeError("finish_render() was called without a matching start_render() call.") if window._start_finish_render_data.completed: raise RuntimeError("finish_render() was called more than once.") window._start_finish_render_data.end()
[docs] def set_background_color(color: RGBA255) -> None: """ Set the color :py:meth:`arcade.Window.clear()` will use when clearing the window. This only needs to be called when the background color changes. .. Note:: A shorter and faster way to set background color is using :py:attr:`arcade.Window.background_color`. Examples:: # Use Arcade's built in color values arcade.set_background_color(arcade.color.AMAZON) # Specify RGB value directly (red) arcade.set_background_color((255, 0, 0)) Args: color: List of 3 or 4 values in RGB/RGBA format. """ get_window().background_color = Color.from_iterable(color)
[docs] def schedule(function_pointer: Callable, interval: float): """ Schedule a function to be automatically called every ``interval`` seconds. The function/callable needs to take a delta time argument similar to ``on_update``. This is a float representing the number of seconds since it was scheduled or called. A function can be scheduled multiple times, but this is not recommended. .. Warning:: Scheduled functions should **always** be unscheduled using :py:func:`arcade.unschedule`. Having lingering scheduled functions will lead to crashes. Example:: def some_action(delta_time): print(delta_time) # Call the function every second arcade.schedule(some_action, 1) # Unschedule Args: function_pointer: Pointer to the function to be called. interval: Interval to call the function (float or integer) """ pyglet.clock.schedule_interval(function_pointer, interval)
[docs] def unschedule(function_pointer: Callable): """ Unschedule a function being automatically called. Example:: def some_action(delta_time): print(delta_time) arcade.schedule(some_action, 1) arcade.unschedule(some_action) Args: function_pointer: Pointer to the function to be unscheduled. """ pyglet.clock.unschedule(function_pointer)
[docs] def schedule_once(function_pointer: Callable, delay: float): """ Schedule a function to be automatically called once after ``delay`` seconds. The function/callable needs to take a delta time argument similar to ``on_update``. This is a float representing the number of seconds since it was scheduled or called. Example:: def some_action(delta_time): print(delta_time) # Call the function once after 1 second arcade.schedule_one(some_action, 1) Args: function_pointer: Pointer to the function to be called. delay: Delay in seconds """ pyglet.clock.schedule_once(function_pointer, delay)