diff --git a/arcade/__init__.py b/arcade/__init__.py index 3fd5a932d7..006f858746 100644 --- a/arcade/__init__.py +++ b/arcade/__init__.py @@ -58,29 +58,18 @@ def configure_logging(level: Optional[int] = None): # noinspection PyPep8 import pyglet -# TODO: Remove ASAP after pyglet >= 2.1dev2 is out -if pyglet.version == "2.1.dev2": - # Temporary monkeypatch via deletion since dev2 still includes - # overly-specific __eq__ behavior. Later pyglet commits restore - # equality with same-valued tuples by deleting the __eq__ methods. - from pyglet import math as _pyglet_math - - del _pyglet_math.Vec2.__eq__ - del _pyglet_math.Vec3.__eq__ - del _pyglet_math.Vec4.__eq__ - # Env variable shortcut for headless mode if os.environ.get("ARCADE_HEADLESS"): - pyglet.options["headless"] = True + pyglet.options.headless = True from arcade import utils # Disable shadow window on macs and in headless mode. if sys.platform == "darwin" or os.environ.get("ARCADE_HEADLESS") or utils.is_raspberry_pi(): - pyglet.options["shadow_window"] = False + pyglet.options.shadow_window = False # Use the old gdi fonts on windows until directwrite is fast/stable -# pyglet.options['win32_gdi_font'] = True +# pyglet.options.win32_gdi_font = True # Imports from modules that don't do anything circular @@ -152,7 +141,7 @@ def configure_logging(level: Optional[int] = None): from .screenshot import get_pixel # We don't have joysticks game controllers in headless mode -if not pyglet.options["headless"]: +if not pyglet.options.headless: from .joysticks import get_game_controllers from .joysticks import get_joysticks from .controller import ControllerManager @@ -420,11 +409,12 @@ def configure_logging(level: Optional[int] = None): load_font(":system:fonts/ttf/Kenney_Rocket_Square.ttf") # Load additional game controller mappings to Pyglet - if not pyglet.options["headless"]: + if not pyglet.options.headless: try: import pyglet.input.controller mappings_file = resources.resolve(":system:gamecontrollerdb.txt") - pyglet.input.controller.add_mappings_from_file(mappings_file) + # TODO: remove string conversion once fixed upstream + pyglet.input.controller.add_mappings_from_file(str(mappings_file)) except AssertionError: pass diff --git a/arcade/application.py b/arcade/application.py index 065c730494..eeba095c05 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -112,14 +112,14 @@ def __init__( self, width: int = 1280, height: int = 720, - title: Optional[str] = "Arcade Window", + title: str | None = "Arcade Window", fullscreen: bool = False, resizable: bool = False, update_rate: float = 1 / 60, antialiasing: bool = True, gl_version: tuple[int, int] = (3, 3), - screen: Optional[pyglet.display.Screen] = None, - style: Optional[str] = pyglet.window.Window.WINDOW_STYLE_DEFAULT, + screen: pyglet.display.Screen | None = None, + style: str | None = pyglet.window.Window.WINDOW_STYLE_DEFAULT, visible: bool = True, vsync: bool = False, gc_mode: str = "context_gc", @@ -129,7 +129,7 @@ def __init__( gl_api: str = "gl", draw_rate: float = 1 / 60, fixed_rate: float = 1.0 / 60.0, - fixed_frame_cap: Optional[int] = None, + fixed_frame_cap: int | None = None, ) -> None: # In certain environments we can't have antialiasing/MSAA enabled. # Detect replit environment @@ -142,7 +142,7 @@ def __init__( gl_api = "gles" #: Whether this is a headless window - self.headless: bool = pyglet.options.get("headless") is True + self.headless: bool = pyglet.options.headless is True config = None # Attempt to make window with antialiasing @@ -248,7 +248,7 @@ def __init__( if enable_polling: self.keyboard = pyglet.window.key.KeyStateHandler() - if pyglet.options["headless"]: + if pyglet.options.headless: self.push_handlers(self.keyboard) else: @@ -365,10 +365,10 @@ def close(self) -> None: def set_fullscreen( self, fullscreen: bool = True, - screen: Optional["Window"] = None, - mode: Optional[ScreenMode] = None, - width: Optional[float] = None, - height: Optional[float] = None, + screen: pyglet.window.Window | None = None, + mode: ScreenMode | None = None, + width: float | None = None, + height: float | None = None, ) -> None: """ Set if we are full screen or not. @@ -380,10 +380,18 @@ def set_fullscreen( have been obtained by enumerating `Screen.get_modes`. If None, an appropriate mode will be selected from the given `width` and `height`. - :param width: - :param height: - """ - super().set_fullscreen(fullscreen, screen, mode, width, height) + :param width: Although marked as py:class:`float`, will be + rounded via :py:class:`int` if not ``None``. + :param height: Although marked as py:class:`float`, will be + rounded via :py:class:`int` if not ``None``. + """ + # fmt: off + super().set_fullscreen( + fullscreen, screen, mode, + # TODO: resolve the upstream int / float screen coord issue + None if width is None else int(width), + None if height is None else int(height)) + # fmt: on def center_window(self) -> None: """ diff --git a/arcade/sound.py b/arcade/sound.py index 977217bc26..554badf140 100644 --- a/arcade/sound.py +++ b/arcade/sound.py @@ -11,15 +11,14 @@ from typing import Optional, Union import pyglet +from pyglet.media import Source from arcade.resources import resolve if os.environ.get("ARCADE_SOUND_BACKENDS"): - pyglet.options["audio"] = tuple( - v.strip() for v in os.environ["ARCADE_SOUND_BACKENDS"].split(",") - ) + pyglet.options.audio = tuple(v.strip() for v in os.environ["ARCADE_SOUND_BACKENDS"].split(",")) else: - pyglet.options["audio"] = ("openal", "xaudio2", "directsound", "pulse", "silent") + pyglet.options.audio = ("openal", "xaudio2", "directsound", "pulse", "silent") import pyglet.media as media @@ -39,7 +38,7 @@ def __init__(self, file_name: Union[str, Path], streaming: bool = False): raise FileNotFoundError(f"The sound file '{file_name}' is not a file or can't be read.") self.file_name = str(file_name) - self.source: Union[media.StaticSource, media.StreamingSource] = media.load( + self.source: Source = media.load( self.file_name, streaming=streaming ) diff --git a/arcade/text.py b/arcade/text.py index a90fbbbe22..b9a436a2f1 100644 --- a/arcade/text.py +++ b/arcade/text.py @@ -45,9 +45,8 @@ def load_font(path: Union[str, Path]) -> None: FontNameOrNames = Union[str, tuple[str, ...]] -def _attempt_font_name_resolution(font_name: FontNameOrNames) -> FontNameOrNames: - """ - Attempt to resolve a tuple of font names. +def _attempt_font_name_resolution(font_name: FontNameOrNames) -> str: + """Attempt to resolve a font name. Preserves the original logic of this section, even though it doesn't seem to make sense entirely. Comments are an attempt @@ -82,8 +81,12 @@ def _attempt_font_name_resolution(font_name: FontNameOrNames) -> FontNameOrNames except FileNotFoundError: pass - # failed to find it ourselves, hope pyglet can make sense of it - return font_name + # failed to find it ourselves, hope pyglet can make sense of it + # Note this is the best approximation of what I unerstand the old + # behavior to have been. + return pyglet.font.load(font_list).name + + raise ValueError(f"Couldn't find a font for {font_name!r}") def _draw_pyglet_label(label: pyglet.text.Label) -> None: @@ -131,7 +134,8 @@ class Text: :param width: A width limit in pixels :param align: Horizontal alignment; values other than "left" require width to be set :param Union[str, tuple[str, ...]] font_name: A font name, path to a font file, or list of names - :param bold: Whether to draw the text as bold + :param bold: Whether to draw the text as bold, and if a string, + how bold. See :py:attr:`.bold` to learn more. :param italic: Whether to draw the text as italic :param anchor_x: How to calculate the anchor point's x coordinate. Options: "left", "center", or "right" @@ -180,7 +184,7 @@ def __init__( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", anchor_y: str = "baseline", @@ -203,6 +207,7 @@ def __init__( ) adjusted_font = _attempt_font_name_resolution(font_name) + self._label = pyglet.text.Label( text=text, # pyglet is lying about what it takes here and float is entirely valid @@ -210,13 +215,14 @@ def __init__( y=y, # type: ignore z=z, # type: ignore font_name=adjusted_font, - font_size=font_size, + # TODO: Fix this upstream (Mac & Linux seem to allow float) + font_size=font_size, # type: ignore # use type: ignore since cast is slow & pyglet used Literal anchor_x=anchor_x, # type: ignore anchor_y=anchor_y, # type: ignore color=Color.from_iterable(color), width=width, - align=align, + align=align, # type: ignore bold=bold, italic=italic, multiline=multiline, @@ -385,7 +391,7 @@ def color(self) -> Color: """ Get or set the text color for the label """ - return self._label.color + return Color.from_iterable(self._label.color) @color.setter def color(self, color: RGBOrA255): @@ -484,14 +490,23 @@ def align(self, align: str): self._label.set_style("align", align) @property - def bold(self) -> bool: + def bold(self) -> bool | str: """ - Get or set bold state of the label + Get or set bold state of the label. + + The supported values include: + + * ``"black"`` + * ``"bold" (same as ``True``) + * ``"semibold"`` + * ``"semilight"`` + * ``"light"`` + """ return self._label.bold @bold.setter - def bold(self, bold: bool): + def bold(self, bold: bool | str): self._label.bold = bold @property @@ -587,7 +602,7 @@ def create_text_sprite( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", multiline: bool = False, @@ -675,7 +690,7 @@ def draw_text( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", anchor_y: str = "baseline", @@ -715,7 +730,8 @@ def draw_text( :param width: A width limit in pixels :param align: Horizontal alignment; values other than "left" require width to be set :param Union[str, tuple[str, ...]] font_name: A font name, path to a font file, or list of names - :param bold: Whether to draw the text as bold + :param bold: Whether to draw the text as bold, and if a :py:class:`str`, + how bold to draw it. See :py:attr:`.Text.bold` to learn more. :param italic: Whether to draw the text as italic :param anchor_x: How to calculate the anchor point's x coordinate :param anchor_y: How to calculate the anchor point's y coordinate diff --git a/doc/programming_guide/headless.rst b/doc/programming_guide/headless.rst index 485d9e6786..4824cb1768 100644 --- a/doc/programming_guide/headless.rst +++ b/doc/programming_guide/headless.rst @@ -34,7 +34,7 @@ This can be done in the following ways: # The above is a shortcut for import pyglet - pyglet.options["headless"] = True + pyglet.options.headless = True This of course also means you can configure headless externally. @@ -182,10 +182,10 @@ to a physical device (graphics card) or a virtual card/device. .. code:: py # Default setting - pyglet.options['headless_device'] = 0 + pyglet.options.headless_device = 0 # Use the second gpu/device - pyglet.options['headless_device'] = 1 + pyglet.options.headless_device = 1 Issues? ------- diff --git a/pyproject.toml b/pyproject.toml index 404087a7fa..7956e68381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "pyglet == 2.1dev2", + "pyglet == 2.1dev3", "pillow~=10.2.0", "pymunk~=6.6.0", "pytiled-parser~=2.2.5", diff --git a/tests/__init__.py b/tests/__init__.py index ca0a59529b..40bd75a955 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,4 +3,4 @@ # Headless mode if os.environ.get("ARCADE_HEADLESS_TEST"): import pyglet - pyglet.options["headless"] = True + pyglet.options.headless = True