diff --git a/arcade/__init__.py b/arcade/__init__.py index a4c812d3e..a27eb7e38 100644 --- a/arcade/__init__.py +++ b/arcade/__init__.py @@ -59,7 +59,7 @@ def configure_logging(level: int | None = None): import pyglet -# Enable HiDPI support +# Enable HiDPI support using stretch mode if os.environ.get("ARCADE_TEST"): pyglet.options.dpi_scaling = "real" else: diff --git a/arcade/application.py b/arcade/application.py index 9c5a16a58..275e44d43 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -8,12 +8,13 @@ import logging import os import time -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Sequence import pyglet import pyglet.gl as gl import pyglet.window.mouse from pyglet.display.base import Screen, ScreenMode +from pyglet.event import EVENT_HANDLE_STATE, EVENT_UNHANDLED from pyglet.window import MouseCursor import arcade @@ -29,7 +30,6 @@ from arcade.camera.default import DefaultProjector from arcade.start_finish_data import StartFinishRenderData - LOG = logging.getLogger(__name__) MOUSE_BUTTON_LEFT = 1 @@ -180,7 +180,7 @@ def __init__( # Attempt to make window with antialiasing if antialiasing: try: - config = pyglet.gl.Config( + config = gl.Config( major_version=gl_version[0], minor_version=gl_version[1], opengl_api=gl_api, # type: ignore # pending: upstream fix @@ -204,7 +204,7 @@ def __init__( antialiasing = False # If we still don't have a config if not config: - config = pyglet.gl.Config( + config = gl.Config( major_version=gl_version[0], minor_version=gl_version[1], opengl_api=gl_api, # type: ignore # pending: upstream fix @@ -239,7 +239,7 @@ def __init__( if antialiasing: try: gl.glEnable(gl.GL_MULTISAMPLE_ARB) - except pyglet.gl.GLException: + except gl.GLException: LOG.warning("Warning: Anti-aliasing not supported on this computer.") _setup_clock() @@ -338,7 +338,7 @@ def ctx(self) -> ArcadeContext: """ return self._ctx - def clear( + def clear( # type: ignore # not sure what to do here, BaseWindow.clear is static self, color: RGBOrA255 | None = None, color_normalized: RGBANormalized | None = None, @@ -554,7 +554,7 @@ def set_draw_rate(self, rate: float) -> None: pyglet.clock.unschedule(pyglet.app.event_loop._redraw_windows) pyglet.clock.schedule_interval(pyglet.app.event_loop._redraw_windows, self._draw_rate) - def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> bool | None: + def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> EVENT_HANDLE_STATE: """ Called repeatedly while the mouse is moving in the window area. @@ -568,7 +568,7 @@ def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> bool | None: """ pass - def on_mouse_press(self, x: int, y: int, button: int, modifiers: int) -> bool | None: + def on_mouse_press(self, x: int, y: int, button: int, modifiers: int) -> EVENT_HANDLE_STATE: """ Called once whenever a mouse button gets pressed down. @@ -596,7 +596,7 @@ def on_mouse_press(self, x: int, y: int, button: int, modifiers: int) -> bool | def on_mouse_drag( self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int - ) -> bool | None: + ) -> EVENT_HANDLE_STATE: """ Called repeatedly while the mouse moves with a button down. @@ -619,7 +619,7 @@ def on_mouse_drag( """ return self.on_mouse_motion(x, y, dx, dy) - def on_mouse_release(self, x: int, y: int, button: int, modifiers: int) -> bool | None: + def on_mouse_release(self, x: int, y: int, button: int, modifiers: int) -> EVENT_HANDLE_STATE: """ Called once whenever a mouse button gets released. @@ -642,9 +642,11 @@ def on_mouse_release(self, x: int, y: int, button: int, modifiers: int) -> bool Bitwise 'and' of all modifiers (shift, ctrl, num lock) active during this event. See :ref:`keyboard_modifiers`. """ - return False + return EVENT_UNHANDLED - def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> bool | None: + def on_mouse_scroll( + self, x: int, y: int, scroll_x: float, scroll_y: float + ) -> EVENT_HANDLE_STATE: """ Called repeatedly while a mouse scroll wheel moves. @@ -676,7 +678,7 @@ def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> bool scroll_y: Number of steps scrolled vertically since the last call of this function """ - return False + return EVENT_UNHANDLED def set_mouse_visible(self, visible: bool = True) -> None: """ @@ -724,7 +726,7 @@ def on_action(self, action_name: str, state) -> None: """ pass - def on_key_press(self, symbol: int, modifiers: int) -> bool | None: + def on_key_press(self, symbol: int, modifiers: int) -> EVENT_HANDLE_STATE: """ Called once when a key gets pushed down. @@ -741,9 +743,9 @@ def on_key_press(self, symbol: int, modifiers: int) -> bool | None: Bitwise 'and' of all modifiers (shift, ctrl, num lock) active during this event. See :ref:`keyboard_modifiers`. """ - return False + return EVENT_UNHANDLED - def on_key_release(self, symbol: int, modifiers: int) -> bool | None: + def on_key_release(self, symbol: int, modifiers: int) -> EVENT_HANDLE_STATE: """ Called once when a key gets released. @@ -763,9 +765,9 @@ def on_key_release(self, symbol: int, modifiers: int) -> bool | None: ctrl, num lock) active during this event. See :ref:`keyboard_modifiers`. """ - return False + return EVENT_UNHANDLED - def on_draw(self) -> bool | None: + def on_draw(self) -> EVENT_HANDLE_STATE: """ Override this function to add your custom drawing code. @@ -781,9 +783,9 @@ def on_draw(self) -> bool | None: self._start_finish_render_data.draw() return True - return False + return EVENT_UNHANDLED - def _on_resize(self, width: int, height: int) -> bool | None: + def _on_resize(self, width: int, height: int) -> EVENT_HANDLE_STATE: """ The internal method called when the window is resized. @@ -799,9 +801,9 @@ def _on_resize(self, width: int, height: int) -> bool | None: # Retain viewport self.viewport = (0, 0, width, height) - return False + return EVENT_UNHANDLED - def on_resize(self, width: int, height: int) -> bool | None: + def on_resize(self, width: int, height: int) -> EVENT_HANDLE_STATE: """ Override this method to add custom actions when the window is resized. @@ -855,7 +857,7 @@ def get_size(self) -> tuple[int, int]: def get_location(self) -> tuple[int, int]: """Get the current X/Y coordinates of the window.""" - return super().get_location() + return super().get_location() # type: ignore # Window typed at runtime def set_visible(self, visible: bool = True): """ @@ -1038,34 +1040,34 @@ def flip(self) -> None: num_collected = self.ctx.gc() LOG.debug("Garbage collected %s OpenGL resource(s)", num_collected) - super().flip() + super().flip() # type: ignore # Window typed at runtime def switch_to(self) -> None: """Switch the this window context. This is normally only used in multi-window applications. """ - super().switch_to() + super().switch_to() # type: ignore # Window typed at runtime def set_caption(self, caption) -> None: """Set the caption/title of the window.""" - super().set_caption(caption) + super().set_caption(caption) # type: ignore # Window typed at runtime def set_location(self, x, y) -> None: """Set location of the window.""" - super().set_location(x, y) + super().set_location(x, y) # type: ignore # Window typed at runtime def activate(self) -> None: """Activate this window.""" - super().activate() + super().activate() # type: ignore # Window typed at runtime def minimize(self) -> None: """Minimize the window.""" - super().minimize() + super().minimize() # type: ignore # Window typed at runtime def maximize(self) -> None: """Maximize the window.""" - super().maximize() + super().maximize() # type: ignore # Window typed at runtime def set_vsync(self, vsync: bool) -> None: """Set if we sync our draws to the monitors vertical sync rate.""" @@ -1097,9 +1099,9 @@ def get_system_mouse_cursor(self, name) -> MouseCursor: def dispatch_events(self) -> None: """Dispatch events""" - super().dispatch_events() + super().dispatch_events() # type: ignore # Window typed at runtime - def on_mouse_enter(self, x: int, y: int) -> bool | None: + def on_mouse_enter(self, x: int, y: int) -> EVENT_HANDLE_STATE: """ Called once whenever the mouse enters the window area on screen. @@ -1112,7 +1114,7 @@ def on_mouse_enter(self, x: int, y: int) -> bool | None: """ pass - def on_mouse_leave(self, x: int, y: int) -> bool | None: + def on_mouse_leave(self, x: int, y: int) -> EVENT_HANDLE_STATE: """ Called once whenever the mouse leaves the window area on screen. @@ -1183,6 +1185,15 @@ def fixed_delta_time(self) -> float: """The configured fixed update rate""" return self._fixed_rate + # required because pyglet marks the method as abstract methods, + # but resolves class during runtime + def _create(self) -> None: + """Internal method to create the window.""" + super()._create() # type: ignore + + def _recreate(self, changes: Sequence[str]) -> None: + super()._recreate(changes) # type: ignore + def open_window( width: int, diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index ee0e43e20..3dedb3856 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -544,10 +544,12 @@ def position(self) -> Vec2: """The 2D world position of the camera along the X and Y axes.""" return Vec2(self._camera_data.position[0], self._camera_data.position[1]) + # Setter with different signature will cause mypy issues + # https://github.com/python/mypy/issues/3004 @position.setter def position(self, _pos: Point) -> None: - x, y, *z = _pos - z = self._camera_data.position[2] if not z else z[0] + x, y, *_z = _pos + z = self._camera_data.position[2] if not _z else _z[0] self._camera_data.position = (x, y, z) @property @@ -900,7 +902,7 @@ def top_left(self, new_corner: Point2): left = self.left x, y = new_corner - self.position = (x - ux * top - rx * left, y - uy * top - ry * left) + self.position = (x - ux * top - rx * left, y - uy * top - ry * left) # type: ignore # top_center @property @@ -918,7 +920,7 @@ def top_center(self, new_top: Point2): top = self.top x, y = new_top - self.position = x - ux * top, y - uy * top + self.position = x - ux * top, y - uy * top # type: ignore # top_right @property @@ -942,7 +944,7 @@ def top_right(self, new_corner: Point2): right = self.right x, y = new_corner - self.position = (x - ux * top - rx * right, y - uy * top - ry * right) + self.position = (x - ux * top - rx * right, y - uy * top - ry * right) # type: ignore # center_right @property @@ -959,7 +961,7 @@ def center_right(self, new_right: Point2): right = self.right x, y = new_right - self.position = x - uy * right, y + ux * right + self.position = x - uy * right, y + ux * right # type: ignore # bottom_right @property @@ -985,7 +987,7 @@ def bottom_right(self, new_corner: Point2): self.position = ( x - ux * bottom - rx * right, y - uy * bottom - ry * right, - ) + ) # type: ignore # bottom_center @property @@ -1003,7 +1005,7 @@ def bottom_center(self, new_bottom: Point2): bottom = self.bottom x, y = new_bottom - self.position = x - ux * bottom, y - uy * bottom + self.position = x - ux * bottom, y - uy * bottom # type: ignore # bottom_left @property @@ -1027,7 +1029,7 @@ def bottom_left(self, new_corner: Point2): left = self.left x, y = new_corner - self.position = (x - ux * bottom - rx * left, y - uy * bottom - ry * left) + self.position = x - ux * bottom - rx * left, y - uy * bottom - ry * left # type: ignore # center_left @property @@ -1044,4 +1046,4 @@ def center_left(self, new_left: Point2): left = self.left x, y = new_left - self.position = x - uy * left, y + ux * left + self.position = Vec2(x - uy * left, y + ux * left) diff --git a/arcade/camera/default.py b/arcade/camera/default.py index fb149066f..fbf4b63a4 100644 --- a/arcade/camera/default.py +++ b/arcade/camera/default.py @@ -92,8 +92,8 @@ def unproject(self, screen_coordinate: Point) -> Vec3: Due to the nature of viewport projector this does not do anything. """ - x, y, *z = screen_coordinate - z = 0.0 if not z else z[0] + x, y, *_z = screen_coordinate + z = 0.0 if not _z else _z[0] return Vec3(x, y, z) diff --git a/arcade/camera/perspective.py b/arcade/camera/perspective.py index 43bcbd754..3d3bf336e 100644 --- a/arcade/camera/perspective.py +++ b/arcade/camera/perspective.py @@ -182,15 +182,15 @@ def project(self, world_coordinate: Point) -> Vec2: Returns: A 2D screen pixel coordinate. """ - x, y, *z = world_coordinate + x, y, *_z = world_coordinate z = ( ( 0.5 * self.viewport.height / tan(radians(0.5 * self._projection.fov / self._view.zoom)) ) - if not z - else z[0] + if not _z + else _z[0] ) _projection = generate_perspective_matrix(self._projection, self._view.zoom) @@ -214,15 +214,15 @@ def unproject(self, screen_coordinate: Point) -> Vec3: Returns: A 3D vector in world space. """ - x, y, *z = screen_coordinate + x, y, *_z = screen_coordinate z = ( ( 0.5 * self.viewport.height / tan(radians(0.5 * self._projection.fov / self._view.zoom)) ) - if not z - else z[0] + if not _z + else _z[0] ) _projection = generate_perspective_matrix(self._projection, self._view.zoom) diff --git a/arcade/camera/projection_functions.py b/arcade/camera/projection_functions.py index 5a96b9f31..2745fb90f 100644 --- a/arcade/camera/projection_functions.py +++ b/arcade/camera/projection_functions.py @@ -125,8 +125,8 @@ def project_orthographic( view_matrix: Mat4, projection_matrix: Mat4, ) -> Vec2: - x, y, *z = world_coordinate - z = 0.0 if not z else z[0] + x, y, *_z = world_coordinate + z = 0.0 if not _z else _z[0] world_position = Vec4(x, y, z, 1.0) @@ -144,8 +144,8 @@ def unproject_orthographic( view_matrix: Mat4, projection_matrix: Mat4, ) -> Vec3: - x, y, *z = screen_coordinate - z = 0.0 if not z else z[0] + x, y, *_z = screen_coordinate + z = 0.0 if not _z else _z[0] screen_x = 2.0 * (x - viewport[0]) / viewport[2] - 1 screen_y = 2.0 * (y - viewport[1]) / viewport[3] - 1 @@ -165,8 +165,8 @@ def project_perspective( view_matrix: Mat4, projection_matrix: Mat4, ) -> Vec2: - x, y, *z = world_coordinate - z = 1.0 if not z else z[0] + x, y, *_z = world_coordinate + z = 1.0 if not _z else _z[0] world_position = Vec4(x, y, z, 1.0) @@ -188,8 +188,8 @@ def unproject_perspective( view_matrix: Mat4, projection_matrix: Mat4, ) -> Vec3: - x, y, *z = screen_coordinate - z = 1.0 if not z else z[0] + x, y, *_z = screen_coordinate + z = 1.0 if not _z else _z[0] screen_x = 2.0 * (x - viewport[0]) / viewport[2] - 1 screen_y = 2.0 * (y - viewport[1]) / viewport[3] - 1 diff --git a/arcade/examples/gl/3d_sphere.py b/arcade/examples/gl/3d_sphere.py index 1b155f758..249bdcf39 100644 --- a/arcade/examples/gl/3d_sphere.py +++ b/arcade/examples/gl/3d_sphere.py @@ -16,7 +16,6 @@ class Sphere3D(arcade.Window): - def __init__(self, width, height, title): super().__init__(width, height, title, resizable=True) self.sphere = geometry.sphere(1.0, 32, 32, uvs=False) @@ -69,20 +68,36 @@ def __init__(self, width, height, title): self.text_batch = Batch() self.text_cull = arcade.Text( - "F2: Toggle cull face (True)", x=10, y=10, font_size=15, color=arcade.color.WHITE, - batch=self.text_batch + "F2: Toggle cull face (True)", + x=10, + y=10, + font_size=15, + color=arcade.color.WHITE, + batch=self.text_batch, ) self.text_depth = arcade.Text( - "F1: Toggle depth test (True)", x=10, y=30, font_size=15, color=arcade.color.WHITE, - batch=self.text_batch + "F1: Toggle depth test (True)", + x=10, + y=30, + font_size=15, + color=arcade.color.WHITE, + batch=self.text_batch, ) self.text_wireframe = arcade.Text( - "SPACE: Toggle wireframe (False)", x=10, y=50, font_size=15, color=arcade.color.WHITE, - batch=self.text_batch + "SPACE: Toggle wireframe (False)", + x=10, + y=50, + font_size=15, + color=arcade.color.WHITE, + batch=self.text_batch, ) self.text_fs = arcade.Text( - "F: Toggle fullscreen (False)", x=10, y=70, font_size=15, color=arcade.color.WHITE, - batch=self.text_batch + "F: Toggle fullscreen (False)", + x=10, + y=70, + font_size=15, + color=arcade.color.WHITE, + batch=self.text_batch, ) self.text_vert_count = arcade.Text( "Use mouse wheel to add/remove vertices", @@ -169,7 +184,7 @@ def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): def on_mouse_release(self, x, y, button, modifiers): self.drag_time = None - def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int): + def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float): self.vert_count = clamp(self.vert_count + scroll_y / 500, 0.0, 1.0) diff --git a/arcade/examples/gl/normal_mapping_simple.py b/arcade/examples/gl/normal_mapping_simple.py index 93f6d678e..3503cd69d 100644 --- a/arcade/examples/gl/normal_mapping_simple.py +++ b/arcade/examples/gl/normal_mapping_simple.py @@ -21,7 +21,6 @@ class NormalMapping(arcade.Window): - def __init__(self): super().__init__(512, 512, "Normal Mapping") @@ -130,7 +129,7 @@ def on_mouse_motion(self, x: int, y: int, dx: int, dy: int): # (0.0, 0.0) is bottom left, (1.0, 1.0) is top right self.mouse_x, self.mouse_y = x / self.width, y / self.height - def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int): + def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float): """Zoom in/out with the mouse wheel.""" self.mouse_z += scroll_y * 0.05 diff --git a/arcade/examples/gui/own_layout.py b/arcade/examples/gui/own_layout.py index ee3797498..8550e4389 100644 --- a/arcade/examples/gui/own_layout.py +++ b/arcade/examples/gui/own_layout.py @@ -11,10 +11,13 @@ from __future__ import annotations from math import cos, sin +from typing import TypeVar import arcade from arcade.gui import UIAnchorLayout, UIFlatButton, UILayout, UIView, UIWidget +W = TypeVar("W", bound="UIWidget") + class CircleLayout(UILayout): """A custom progress bar widget. @@ -27,7 +30,7 @@ def __init__(self, size_hint=(1, 1), **kwargs): super().__init__(size_hint=size_hint, **kwargs) self._time = 0 # used for rotation - def add(self, child: UIWidget, **kwargs) -> UIWidget: + def add(self, child: W, **kwargs) -> W: """Add a widget to the layout. The widget is placed in a circle around the center of the screen. diff --git a/arcade/experimental/crt_filter.py b/arcade/experimental/crt_filter.py index a26443453..10667a414 100644 --- a/arcade/experimental/crt_filter.py +++ b/arcade/experimental/crt_filter.py @@ -25,7 +25,7 @@ def __init__( resolution_down_scale: float = 6.0, hard_scan: float = -8.0, hard_pix: float = -3.0, - display_warp: Vec2 = (1.0 / 32.0, 1.0 / 24.0), + display_warp: Vec2 = Vec2(1.0 / 32.0, 1.0 / 24.0), mask_dark: float = 0.5, mask_light: float = 1.5, ): diff --git a/arcade/gl/framebuffer.py b/arcade/gl/framebuffer.py index 982ba667a..09b0ab4d1 100644 --- a/arcade/gl/framebuffer.py +++ b/arcade/gl/framebuffer.py @@ -2,7 +2,7 @@ import weakref from contextlib import contextmanager -from ctypes import c_int, string_at +from ctypes import Array, c_int, c_uint, string_at from typing import TYPE_CHECKING, Generator from pyglet import gl @@ -124,7 +124,7 @@ def __init__( # we use in the use() method to activate the different color attachment layers layers = [gl.GL_COLOR_ATTACHMENT0 + i for i, _ in enumerate(self._color_attachments)] # pyglet wants this as a ctypes thingy, so let's prepare it - self._draw_buffers = (gl.GLuint * len(layers))(*layers) + self._draw_buffers: Array[c_uint] | None = (gl.GLuint * len(layers))(*layers) # Restore the original bound framebuffer to avoid confusion self.ctx.active_framebuffer.use(force=True) diff --git a/arcade/gui/ui_manager.py b/arcade/gui/ui_manager.py index 4b047fb68..4f36c6cce 100644 --- a/arcade/gui/ui_manager.py +++ b/arcade/gui/ui_manager.py @@ -169,7 +169,7 @@ def clear(self): self.remove(widget) def get_widgets_at( - self, pos: Point2, cls: type[W] = UIWidget, layer=DEFAULT_LAYER + self, pos: Point2, cls: type[W] | type[UIWidget] = UIWidget, layer=DEFAULT_LAYER ) -> Iterable[W]: """Yields all widgets containing a position, returns first top laying widgets which is instance of cls. diff --git a/arcade/perf_info.py b/arcade/perf_info.py index c25451f1b..efb1b67e3 100644 --- a/arcade/perf_info.py +++ b/arcade/perf_info.py @@ -151,7 +151,7 @@ def enable_timings(max_history: int = 100) -> None: _pyglets_dispatch_event = pyglet.window.BaseWindow.dispatch_event # Override the pyglet dispatch event function - pyglet.window.BaseWindow.dispatch_event = _dispatch_event + pyglet.window.BaseWindow.dispatch_event = _dispatch_event # type: ignore _max_history = max_history diff --git a/arcade/physics_engines.py b/arcade/physics_engines.py index eeb65ac7a..9b7aa5c4f 100644 --- a/arcade/physics_engines.py +++ b/arcade/physics_engines.py @@ -349,7 +349,7 @@ def walls(self, walls: SpriteList | Iterable[SpriteList] | None = None) -> None: def walls(self) -> None: self._walls.clear() - def update(self) -> list[SpriteType]: + def update(self) -> list[BasicSprite]: """Move :py:attr:`player_sprite` and return any colliding sprites. Returns: diff --git a/arcade/sound.py b/arcade/sound.py index 443e551c3..7c38862cb 100644 --- a/arcade/sound.py +++ b/arcade/sound.py @@ -13,11 +13,9 @@ from arcade.resources import resolve if os.environ.get("ARCADE_SOUND_BACKENDS"): - pyglet.options.audio = tuple( # type: ignore - 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") # type: ignore + pyglet.options.audio = ("openal", "xaudio2", "directsound", "pulse", "silent") import pyglet.media as media @@ -143,7 +141,7 @@ def _on_player_eos(): # we need to delete this function. player.on_player_eos = None # type: ignore # pending https://github.com/pyglet/pyglet/issues/845 - player.on_player_eos = _on_player_eos + player.on_player_eos = _on_player_eos # type: ignore return player def stop(self, player: media.Player) -> None: diff --git a/arcade/sprite/base.py b/arcade/sprite/base.py index 614d0ae1d..d8ff67bc6 100644 --- a/arcade/sprite/base.py +++ b/arcade/sprite/base.py @@ -7,7 +7,7 @@ from arcade.exceptions import ReplacementWarning, warning from arcade.hitbox import HitBox from arcade.texture import Texture -from arcade.types import LRBT, RGBA255, AsFloat, Color, Point, Point2, PointList, Rect, RGBOrA255 +from arcade.types import LRBT, RGBA255, AsFloat, Color, Point, Point2, Point2List, Rect, RGBOrA255 from arcade.utils import copy_dunders_unimplemented if TYPE_CHECKING: @@ -437,7 +437,6 @@ def rgb(self) -> tuple[int, int, int]: @rgb.setter def rgb(self, color: RGBOrA255): - # Fast validation of size by unpacking channel values try: r, g, b, *_a = color @@ -775,7 +774,7 @@ def draw_hit_box(self, color: RGBA255 = BLACK, line_thickness: float = 2.0) -> N line_thickness: How thick the box should be """ - points: PointList = self.hit_box.get_adjusted_points() + points: Point2List = self.hit_box.get_adjusted_points() # NOTE: This is a COPY operation. We don't want to modify the points. points = tuple(points) + tuple(points[:-1]) arcade.draw_line_strip(points, color=color, line_width=line_thickness) diff --git a/arcade/sprite/sprite.py b/arcade/sprite/sprite.py index 59b9e6b1f..ece5d3c3f 100644 --- a/arcade/sprite/sprite.py +++ b/arcade/sprite/sprite.py @@ -150,8 +150,8 @@ def __init__( self._hit_box: RotatableHitBox = self._hit_box.create_rotatable(angle=self._angle) - self._width = self._texture.width * scale - self._height = self._texture.height * scale + self._width = self._texture.width * self._scale[0] + self._height = self._texture.height * self._scale[1] # --- Properties --- diff --git a/arcade/text.py b/arcade/text.py index fe404839f..e307bdba9 100644 --- a/arcade/text.py +++ b/arcade/text.py @@ -307,7 +307,7 @@ def __init__( color=Color.from_iterable(color), width=width, align=align, # type: ignore - bold=bold, + weight=pyglet.text.Weight.BOLD if bold else pyglet.text.Weight.NORMAL, italic=italic, multiline=multiline, rotation=rotation, @@ -585,11 +585,11 @@ def bold(self) -> bool | str: * ``"light"`` """ - return self._label.bold + return self._label.weight == pyglet.text.Weight.BOLD @bold.setter def bold(self, bold: bool | str): - self._label.bold = bold + self._label.weight = pyglet.text.Weight.BOLD if bold else pyglet.text.Weight.NORMAL @property def italic(self) -> bool | str: diff --git a/arcade/types/box.py b/arcade/types/box.py index 13fcae605..f22d0da0e 100644 --- a/arcade/types/box.py +++ b/arcade/types/box.py @@ -4,7 +4,7 @@ from __future__ import annotations -from typing import NamedTuple, TypedDict +from typing import Any, NamedTuple, TypedDict from pyglet.math import Vec3 @@ -352,12 +352,17 @@ def point_in_box(self, point: Point3) -> bool: and (self.near <= z <= self.far) ) - def __contains__(self, point: Point3) -> bool: + def __contains__(self, point: Point3 | Any) -> bool: """Shorthand for :py:meth:`Box.point_in_box(point) `. Args: point: A tuple of :py:class:`int` or :py:class:`float` values. """ + from arcade.utils import is_iterable + + if not is_iterable(point): + return False + return self.point_in_box(point) def to_points(self) -> tuple[Vec3, Vec3, Vec3, Vec3, Vec3, Vec3, Vec3, Vec3]: diff --git a/arcade/types/rect.py b/arcade/types/rect.py index cb4d1a18a..cac1acab1 100644 --- a/arcade/types/rect.py +++ b/arcade/types/rect.py @@ -3,7 +3,7 @@ from __future__ import annotations import math -from typing import NamedTuple, TypedDict +from typing import Any, NamedTuple, TypedDict from pyglet.math import Vec2 @@ -462,12 +462,17 @@ def point_in_bounds(self, point: Point2) -> bool: px, py = point return (self.left < px < self.right) and (self.bottom < py < self.top) - def __contains__(self, point: Point2) -> bool: + def __contains__(self, point: Point2 | Any) -> bool: """Shorthand for :py:meth:`rect.point_in_rect(point) `. Args: point: A tuple of :py:class:`int` or :py:class:`float` values. """ + from arcade.utils import is_iterable + + if not is_iterable(point): + return False + return self.point_in_rect(point) def distance_from_bounds(self, point: Point2) -> float: @@ -487,6 +492,7 @@ def point_on_bounds(self, point: Point2, tolerance: float) -> bool: Args: point: The point to check. + tolerance: The maximum distance the point can be from the bounds. """ return abs(self.distance_from_bounds(point)) < tolerance diff --git a/pyproject.toml b/pyproject.toml index 515e1b6f6..5f3120a0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ # at the cost of slow download and constant pip install -I -e .[dev] # "pyglet@git+https://github.com/pyglet/pyglet.git@development#egg=pyglet", # Expected future dev preview release on PyPI (not yet released) - "pyglet == 2.1rc1", + "pyglet~=2.1.0", "pillow~=11.0.0", "pymunk~=6.9.0", "pytiled-parser~=2.2.7", diff --git a/tests/unit/test_example_docstrings.py b/tests/unit/test_example_docstrings.py index 09f9e8137..9bda1cd8f 100644 --- a/tests/unit/test_example_docstrings.py +++ b/tests/unit/test_example_docstrings.py @@ -51,7 +51,7 @@ def check_submodules(parent_module_absolute_name: str) -> None: It is important to understand that module names and file paths are different things: * A module name is what Python sees the module's name as (``"arcade.color"``) - * A file path is the location on disk (``C:\\Users\\Reader\\python_project\game.py``) + * A file path is the location on disk (``C:\\Users\\Reader\\python_project\\game.py``) Args: parent_module_absolute_name: The absolute import name of the module to check.