diff --git a/examples/01_event_handling.py b/examples/01_event_handling.py index 79a73d2..7ed61ba 100644 --- a/examples/01_event_handling.py +++ b/examples/01_event_handling.py @@ -8,7 +8,7 @@ async def main(*, sdlevent: apg.SDLEvent, **kwargs): pygame.display.set_mode((400, 400)) while True: - e = await sdlevent.wait(pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP) + e = await sdlevent.wait(pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, priority=0x100) print(e) diff --git a/examples/03_countdown_on_gui.py b/examples/03_countdown_on_gui.py index ac47ccb..14dea8b 100644 --- a/examples/03_countdown_on_gui.py +++ b/examples/03_countdown_on_gui.py @@ -1,6 +1,6 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg @@ -10,8 +10,8 @@ async def main(*, clock: apg.Clock, **kwargs): screen = pygame.display.set_mode((400, 400)) screen_center = screen.get_rect().center font = pygame.font.SysFont(None, 400) - fgcolor = COLORS["black"] - bgcolor = COLORS["white"] + fgcolor = THECOLORS["black"] + bgcolor = THECOLORS["white"] count_from = 3 for i in range(count_from, -1, -1): diff --git a/examples/04_countdown_in_a_more_practical_way.py b/examples/04_countdown_in_a_more_practical_way.py index 1a33978..6c3021d 100644 --- a/examples/04_countdown_in_a_more_practical_way.py +++ b/examples/04_countdown_in_a_more_practical_way.py @@ -1,7 +1,7 @@ from functools import partial import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg @@ -11,10 +11,10 @@ async def main(*, clock: apg.Clock, executor: apg.PriorityExecutor, **kwargs): screen = pygame.display.set_mode((400, 400)) screen_center = screen.get_rect().center font = pygame.font.SysFont(None, 400) - fgcolor = COLORS["black"] + fgcolor = THECOLORS["black"] # Functions with a lower priority will be called earlier. - executor.register(partial(screen.fill, COLORS["white"]), priority=0) + executor.register(partial(screen.fill, THECOLORS["white"]), priority=0) req = executor.register(None, priority=0x100) executor.register(pygame.display.flip, priority=0xFFFFFF00) diff --git a/examples/05_countdown_in_an_even_more_practical_way.py b/examples/05_countdown_in_an_even_more_practical_way.py index ae023dc..930c1a1 100644 --- a/examples/05_countdown_in_an_even_more_practical_way.py +++ b/examples/05_countdown_in_an_even_more_practical_way.py @@ -1,32 +1,33 @@ +from typing import Unpack from functools import partial import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg -async def countdown(*, count_from: int, draw_target: pygame.Surface, clock: apg.Clock, executor: apg.PriorityExecutor, priority, **kwargs): +async def countdown(*, count_from: int, draw_target: pygame.Surface, clock: apg.Clock, executor: apg.PriorityExecutor, priority, **__): center = draw_target.get_rect().center font = pygame.font.SysFont(None, 400) - fgcolor = COLORS["black"] + fgcolor = THECOLORS["black"] - with executor.register(None, priority=priority) as req: + with executor.register(None, priority) as req: for i in range(count_from, -1, -1): img = font.render(str(i), True, fgcolor).convert_alpha() req.callback = partial(draw_target.blit, img, img.get_rect(center=center)) await clock.sleep(1000) -async def main(**kwargs): +async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() pygame.display.set_caption("Countdown") - screen = pygame.display.set_mode((400, 400)) + kwargs["draw_target"] = screen = pygame.display.set_mode((400, 400)) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["white"]), priority=0) + r(partial(screen.fill, THECOLORS["white"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) - await countdown(count_from=3, draw_target=screen, priority=0x100, **kwargs) + await countdown(count_from=3, priority=0x100, **kwargs) if __name__ == "__main__": diff --git a/examples/06_full_blown_app_structure.py b/examples/06_full_blown_app_structure.py index 32fa2f6..8564420 100644 --- a/examples/06_full_blown_app_structure.py +++ b/examples/06_full_blown_app_structure.py @@ -3,14 +3,14 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import pygame.constants as C import asyncpygame as apg from asyncpygame.scene_switcher import SceneSwitcher, FadeTransition from _uix.touch_indicator import touch_indicator -from _uix.anchor_layout import AnchorLayout -from _uix.ripple_button import RippleButton +from _uix.anchor_layout import anchor_layout +from _uix.ripple_button import ripple_button from _uix.modal_dialog import ask_yes_no_question @@ -19,7 +19,7 @@ async def main(**kwargs: Unpack[apg.CommonParams]): pygame.display.set_caption("") kwargs["draw_target"] = screen = pygame.display.set_mode((800, 600)) - bgcolor = COLORS["black"] + bgcolor = THECOLORS["black"] r = kwargs["executor"].register r(partial(screen.fill, bgcolor), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) @@ -47,17 +47,20 @@ async def title_scene(*, scene_switcher, userdata, **kwargs: Unpack[apg.CommonPa target_rect = draw_target.get_rect() font = userdata['font'] async with apg.open_nursery() as nursery: - AnchorLayout( - nursery, + e_start = apg.Event() + s = nursery.start + s(anchor_layout( font.render("", True, "white", userdata["bgcolor"]).convert(draw_target), target_rect.scale_by(1.0, 0.5).move_to(y=target_rect.y), - priority=0x100, **kwargs) - start_button = RippleButton( - nursery, + priority=0x100, + **kwargs)) + s(ripple_button( button_image := font.render("Start", True, "white").convert_alpha(), button_image.get_rect(center=target_rect.scale_by(1.0, 0.5).move_to(bottom=target_rect.bottom).center).inflate(80, 80), - priority=0x100, **kwargs) - await start_button.to_be_clicked() + on_click=e_start.fire, + priority=0x100, + **kwargs)) + await e_start.wait() scene_switcher.switch_to(menu_scene, FadeTransition()) await apg.sleep_forever() @@ -67,20 +70,20 @@ async def menu_scene(*, scene_switcher, userdata, **kwargs: Unpack[apg.CommonPar target_rect = draw_target.get_rect() font = userdata['font'] async with apg.open_nursery() as nursery: - play_button = RippleButton( - nursery, + e_play = apg.Event() + e_back = apg.Event() + s = nursery.start + s(ripple_button( button_image := font.render("Play Game", True, "white").convert_alpha(), button_image.get_rect(center=target_rect.scale_by(1.0, 0.5).move_to(y=target_rect.y).center).inflate(80, 80), - priority=0x100, **kwargs) - back_button = RippleButton( - nursery, + on_click=e_play.fire, + priority=0x100, **kwargs)) + s(ripple_button( button_image := font.render("Back to Title", True, "white").convert_alpha(), button_image.get_rect(center=target_rect.scale_by(1.0, 0.5).move_to(bottom=target_rect.bottom).center).inflate(80, 80), - priority=0x100, **kwargs) - tasks = await apg.wait_any( - play_button.to_be_clicked(), - back_button.to_be_clicked(), - ) + on_click=e_back.fire, + priority=0x100, **kwargs)) + tasks = await apg.wait_any(e_play.wait(), e_back.wait()) next_scene = title_scene if tasks[1].finished else game_scene scene_switcher.switch_to(next_scene, FadeTransition()) await apg.sleep_forever() diff --git a/examples/_uix/anchor_layout.py b/examples/_uix/anchor_layout.py index cbb0586..58437ff 100644 --- a/examples/_uix/anchor_layout.py +++ b/examples/_uix/anchor_layout.py @@ -1,65 +1,30 @@ -__all__ = ('AnchorLayout', ) +__all__ = ('anchor_layout', ) -from typing import Self from functools import partial -from asyncgui import Nursery, sleep_forever +import asyncgui from pygame import Surface, Rect -class AnchorLayout: +async def anchor_layout( + image: Surface, dest: Rect, priority, *, anchor_image="center", anchor_dest="center", + executor, draw_target, **__): ''' .. code-block:: async with asyncpygame.open_nursery() as nursery: image = Surface(...) dest = Rect(...) - layout = AnchorLayout(nursery, image, dest, **common_params) - # Aligns the right edge of the image with the right edge of the layout. - layout.anchor_src = "right" - layout.anchor_dest = "right" - - # Aligns the center of the image with the midtop of the layout. - layout.anchor_src = "center" - layout.anchor_dest = "midtop" - - # You can change its image anytime. - layout.image = another_image - - # You can move or resize the layout by updating the ``dest``. - dest.right = ... - dest.width = ... - - # but you cannot assign another Rect instance to the layout. - layout.dest = another_rect # NOT ALLOWED + nursery.start(anchor_layout(image, dest, priority=..., **common_params)) ''' - def __init__(self, owner: Nursery, image: Surface, dest: Rect, - *, anchor_src="center", anchor_dest="center", **common_params): - ''' - :param owner: AnchorLayout cannot outlive its owner. When the owner is closed, the sprite is destroyed. - :param anchor_src: This must be any of the ``Rect``s positional attribute names. (e.g. "topleft", "bottomleft", ...) - :param anchor_dest: Same as ``anchor_src``. - ''' - self._dest = dest - self.image = image - self.anchor_src = anchor_src - self.anchor_dest = anchor_dest - self._main_task = owner.start(self._main(**common_params), daemon=True) - - def kill(self): - self._main_task.cancel() + with executor.register(partial(_draw, draw_target.blit, image, image.get_rect(), dest, anchor_image, anchor_dest), priority): + await asyncgui.sleep_forever() - @property - def dest(self) -> Rect: - return self._dest - async def _main(self, *, priority, draw_target, executor, **unused): - with executor.register(partial(self._draw, draw_target.blit, self._dest, self), priority=priority): - await sleep_forever() +def _draw(getattr, setattr, blit, image, src: Rect, dest: Rect, anchor_image, anchor_dest): + setattr(src, anchor_image, getattr(dest, anchor_dest)) + blit(image, src) - def _draw(getattr, blit, dest, self: Self): - image = self.image - blit(image, image.get_rect(**{self.anchor_src: getattr(dest, self.anchor_dest)})) - _draw = partial(_draw, getattr) +_draw = partial(_draw, getattr, setattr) diff --git a/examples/_uix/modal_dialog.py b/examples/_uix/modal_dialog.py index b400d22..8071aa8 100644 --- a/examples/_uix/modal_dialog.py +++ b/examples/_uix/modal_dialog.py @@ -5,22 +5,22 @@ from functools import partial import asyncgui -from pygame import Rect +from pygame.colordict import THECOLORS +from pygame import Rect, Surface from pygame.font import SysFont from asyncpygame import CommonParams, block_input_events, Clock -from _uix.ripple_button import RippleButton -from _uix.anchor_layout import AnchorLayout +from _uix.ripple_button import ripple_button +from _uix.anchor_layout import anchor_layout @asynccontextmanager -async def darken(**kwargs: Unpack[CommonParams]): +async def darken(*, priority, **kwargs: Unpack[CommonParams]): interpolate = kwargs["clock"].interpolate_scalar draw_target = kwargs["draw_target"] - overlay_surface = draw_target.copy() - overlay_surface.fill("black") + overlay_surface = Surface(draw_target.size) set_alpha = overlay_surface.set_alpha - with kwargs["executor"].register(partial(draw_target.blit, overlay_surface), kwargs["priority"]): + with kwargs["executor"].register(partial(draw_target.blit, overlay_surface), priority): async for v in interpolate(0, 180, duration=200): set_alpha(v) yield @@ -28,7 +28,7 @@ async def darken(**kwargs: Unpack[CommonParams]): set_alpha(v) -async def translate_rects_vertically(clock: Clock, rects, movement, duration): +async def move_rects_vertically(clock: Clock, rects, movement, duration): org_ys = tuple(rect.y for rect in rects) async for v in clock.interpolate_scalar(0, movement, duration=duration): for rect, org_y in zip(rects, org_ys): @@ -36,14 +36,14 @@ async def translate_rects_vertically(clock: Clock, rects, movement, duration): async def show_messagebox( - message, *, dialog_size: Rect=None, font=None, text_ok='OK', - priority, **kwargs: Unpack[CommonParams]) -> bool: + message, priority, *, dialog_size: Rect=None, font=None, text_ok='OK', + **kwargs: Unpack[CommonParams]) -> bool: ''' .. code-block:: await show_messagebox("Hello World", priority=0xFFFFFA00, **kwargs) ''' - bgcolor = "grey90" + bgcolor = THECOLORS["grey90"] clock = kwargs["clock"] draw_target = kwargs["draw_target"] if font is None: @@ -56,33 +56,37 @@ async def show_messagebox( dialog_size = target_rect.inflate(-100, 0) dialog_size.height = dialog_size.width // 2 dialog_dest = dialog_size.move_to(bottom=target_rect.top) + e_ok = asyncgui.Event() with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dialog_dest), priority=priority + 1): - label = AnchorLayout( - nursery, + s = nursery.start + s(anchor_layout( font.render(message, True, "black", bgcolor).convert(draw_target), - dialog_dest.scale_by(1.0, 0.7).move_to(top=dialog_dest.top).inflate(-10, -10), - priority=priority + 2, **kwargs) - ok_button = RippleButton( - nursery, + label_dest := dialog_dest.scale_by(1.0, 0.7).move_to(top=dialog_dest.top).inflate(-10, -10), + priority + 2, + **kwargs), daemon=True) + s(ripple_button( font.render(text_ok, True, "white"), - dialog_dest.scale_by(0.5, 0.3).move_to(midbottom=dialog_dest.midbottom).inflate(-20, -20), - priority=priority + 2, **kwargs) - rects = (dialog_dest, label.dest, ok_button.dest, ) + button_dest := dialog_dest.scale_by(0.5, 0.3).move_to(midbottom=dialog_dest.midbottom).inflate(-20, -20), + priority + 2, + on_click=e_ok.fire, + **kwargs), daemon=True) + rects = (dialog_dest, label_dest, button_dest, ) y_movement = target_rect.centery - dialog_dest.centery - await translate_rects_vertically(clock, rects, y_movement, duration=200) - await ok_button.to_be_clicked() - await translate_rects_vertically(clock, rects, -y_movement, duration=200) + await move_rects_vertically(clock, rects, y_movement, duration=200) + await e_ok.wait() + await move_rects_vertically(clock, rects, -y_movement, duration=200) + return async def ask_yes_no_question( - question, *, dialog_size: Rect=None, font=None, text_yes='Yes', text_no='No', - priority, **kwargs: Unpack[CommonParams]) -> bool: + question, priority, *, dialog_size: Rect=None, font=None, text_yes='Yes', text_no='No', + **kwargs: Unpack[CommonParams]) -> bool: ''' .. code-block:: result = await ask_yes_no_question("Do you like PyGame?", priority=0xFFFFFA00, **kwargs) ''' - bgcolor = "grey90" + bgcolor = THECOLORS["grey90"] clock = kwargs["clock"] draw_target = kwargs["draw_target"] if font is None: @@ -95,25 +99,30 @@ async def ask_yes_no_question( dialog_size = target_rect.inflate(-100, 0) dialog_size.height = dialog_size.width // 2 dialog_dest = dialog_size.move_to(bottom=target_rect.top) + e_yes = asyncgui.Event() + e_no = asyncgui.Event() with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dialog_dest), priority=priority + 1): - label = AnchorLayout( - nursery, + s = nursery.start + s(anchor_layout( font.render(question, True, "black", bgcolor).convert(draw_target), - dialog_dest.scale_by(1.0, 0.5).move_to(top=dialog_dest.top).inflate(-10, -10), - priority=priority + 2, **kwargs) - yes_button = RippleButton( - nursery, + label_dest := dialog_dest.scale_by(1.0, 0.5).move_to(top=dialog_dest.top).inflate(-10, -10), + priority + 2, + **kwargs), daemon=True) + s(ripple_button( font.render(text_yes, True, "white"), - dialog_dest.scale_by(0.5, 0.5).move_to(bottomright=dialog_dest.bottomright).inflate(-20, -20), - priority=priority + 2, **kwargs) - no_button = RippleButton( - nursery, + yes_button_dest := dialog_dest.scale_by(0.5, 0.5).move_to(bottomright=dialog_dest.bottomright).inflate(-20, -20), + priority + 2, + on_click=e_yes.fire, + **kwargs), daemon=True) + s(ripple_button( font.render(text_no, True, "white"), - dialog_dest.scale_by(0.5, 0.5).move_to(bottomleft=dialog_dest.bottomleft).inflate(-20, -20), - priority=priority + 2, **kwargs) - rects = (dialog_dest, label.dest, yes_button.dest, no_button.dest, ) + no_button_dest := dialog_dest.scale_by(0.5, 0.5).move_to(bottomleft=dialog_dest.bottomleft).inflate(-20, -20), + priority + 2, + on_click=e_no.fire, + **kwargs), daemon=True) + rects = (dialog_dest, label_dest, yes_button_dest, no_button_dest, ) y_movement = target_rect.centery - dialog_dest.centery - await translate_rects_vertically(clock, rects, y_movement, duration=200) - tasks = await asyncgui.wait_any(yes_button.to_be_clicked(), no_button.to_be_clicked()) - await translate_rects_vertically(clock, rects, -y_movement, duration=200) + await move_rects_vertically(clock, rects, y_movement, duration=200) + tasks = await asyncgui.wait_any(e_yes.wait(), e_no.wait()) + await move_rects_vertically(clock, rects, -y_movement, duration=200) return tasks[0].finished diff --git a/examples/_uix/progress_spinner.py b/examples/_uix/progress_spinner.py index 355a823..92c8740 100644 --- a/examples/_uix/progress_spinner.py +++ b/examples/_uix/progress_spinner.py @@ -23,7 +23,7 @@ def _draw(pygame_draw_arc, draw_target, color, rect, line_width, self: Self): _draw = partial(_draw, pygame.draw.arc) -async def progress_spinner(dest: Rect, *, color="black", line_width=20, min_arc_angle=0.3, speed=1.0, **kwargs): +async def progress_spinner(dest: Rect, priority, *, color="white", line_width=20, min_arc_angle=0.3, speed=1.0, **kwargs): R1 = 0.4 R2 = math.tau - min_arc_angle * 2 next_start = itertools.accumulate(itertools.cycle((R1, R1, R1 + R2, R1, )), initial=0).__next__ @@ -32,6 +32,6 @@ async def progress_spinner(dest: Rect, *, color="black", line_width=20, min_arc_ anim_attrs = kwargs["clock"].anim_attrs arc = Arc(kwargs["draw_target"], Color(color), dest, next_start(), next_stop(), line_width) - with kwargs["executor"].register(arc.draw, kwargs["priority"]): + with kwargs["executor"].register(arc.draw, priority): while True: await anim_attrs(arc, start_angle=next_start(), stop_angle=next_stop(), duration=d) diff --git a/examples/_uix/ripple_button.py b/examples/_uix/ripple_button.py index 35a9cdd..322979e 100644 --- a/examples/_uix/ripple_button.py +++ b/examples/_uix/ripple_button.py @@ -1,14 +1,13 @@ -__all__ = ('RippleButton', ) +__all__ = ('ripple_button', ) from typing import Self, ContextManager from contextlib import contextmanager from functools import partial -from collections.abc import Awaitable import math -from asyncgui import Nursery, Event, run_as_main +import asyncgui from pygame import Surface, Rect, Color -from pygame import constants as C +import pygame.constants as C import pygame.draw @@ -20,11 +19,11 @@ def out_quad(p): @contextmanager -def clip(surface, rect): +def tmp_clip(surface, rect): ''' .. code-block:: - with clip(surface, rect): + with tmp_clip(surface, rect): ... ''' prev = surface.get_clip() @@ -73,108 +72,80 @@ def block_touch_down_events(sdlevent, priority, *, filter=lambda e: True) -> Con class RippleEffect: __slots__ = ('radius', 'draw', ) - def __init__(self, pos, radius, color): + def __init__(self): + self.draw = do_nothing + + def enable(self, draw_target, pos, radius, color): self.radius = radius - self.draw = partial(self._draw, color, pos, self) + self.draw = partial(self._draw, draw_target, pos, color, self) + + def disable(self): + self.draw = do_nothing - def _draw(pygame_draw_circle, color, pos, self: Self, draw_target): + def _draw(pygame_draw_circle, draw_target, pos, color, self: Self): pygame_draw_circle(draw_target, color, pos, self.radius) _draw = partial(_draw, pygame.draw.circle) -class RippleButton: +def do_nothing(*args, **kwargs): + pass + + +async def ripple_button( + image: Surface, dest: Rect, priority, *, bgcolor="fuchsia", ripple_color=(80, 80, 80, 0), + on_click=do_nothing, executor, sdlevent, clock, draw_target, **__): ''' .. code-block:: async with asyncpygame.open_nursery() as nursery: image = Surface(...) dest = Rect(...) - button = RippleButton(nursery, image, dest, **common_params) + click_event = asyncpygame.Event() - # Waits for the button to be clicked. - await button.to_be_clicked() - - # You can change its image anytime. - button.image = another_image + nursery.start(ripple_button(image, dest, priority=..., on_click=click_event.fire, **common_params)) - # You can move or resize the button by updating the ``dest``. - dest.width = ... - dest.right = ... - - # but you cannot assign another Rect instance to the button. - button.dest = another_rect # NOT ALLOWED + # Waits for the button to be clicked. + args, kwargs = await click_event.wait() + # The events that caused the button to be clicked. These are either a pair of FINGERDOWN + # and FINGERUP events or a pair of MOUSEBUTTONDOWN and MOUSEBUTTONUP events. + e_down, e_up = args ''' - def __init__(self, owner: Nursery, image: Surface, dest: Rect, - *, bgcolor="fuchsia", ripple_color=(80, 80, 80, 0), **common_params): - ''' - :param owner: RippleButton cannot outlive its owner. When the owner is closed, the button is destroyed. - ''' - self._click_event = Event() - self._draw_ripple_effect = None - self._dest = dest - self.image = image - self._main_task = owner.start(self._main(bgcolor, ripple_color, **common_params), daemon=True) - - def kill(self): - self._main_task.cancel() - - @property - def dest(self) -> Rect: - return self._dest - - async def to_be_clicked(self) -> Awaitable[tuple[Event, Event]]: - ''' - Waits for the button to be clicked. - - .. code-block:: - - e_down, e_up = await button.to_be_clicked() - - The ``e_down`` and ``e_up`` above are :class:`pygame.event.Event` instances that caused the click, and - they are either a pair of ``MOUSEBUTTONDOWN`` and ``MOUSEBUTTONUP`` or a pair of ``FINGERDOWN`` and ``FINGERUP``. - ''' - return (await self._click_event.wait())[0] - - async def _main(self, bgcolor, ripple_color, *, priority, draw_target, executor, clock, sdlevent, **unused): - bgcolor = Color(bgcolor) - ripple_color = Color(ripple_color) + Color(bgcolor) - dest = self._dest - click_event = self._click_event - - touch_down = partial( - sdlevent.wait, C.MOUSEBUTTONDOWN, C.FINGERDOWN, priority=priority + 1, consume=True, - filter=lambda e, getattr=getattr, dest=dest: (not getattr(e, 'touch', False)) and dest.collidepoint(e.pos)) - mouse_button_up = partial( - sdlevent.wait, C.MOUSEBUTTONUP, priority=priority + 1, consume=True, - filter=lambda e: e.button == e_down.button) - finger_up = partial( - sdlevent.wait, C.FINGERUP, priority=priority + 1, consume=True, - filter=lambda e: e.finger_id == e_down.finger_id) - - with ( - executor.register(partial(self._draw, bgcolor, draw_target, dest, self), priority=priority), - block_touch_down_events(sdlevent, priority, filter=lambda e, dest=dest: dest.collidepoint(e.pos)), - ): - while True: - e_down = await touch_down() - effect = RippleEffect(e_down.pos, 0, ripple_color) - self._draw_ripple_effect = effect.draw - touch_up = mouse_button_up if e_down.type == C.MOUSEBUTTONDOWN else finger_up - async with run_as_main(touch_up()) as touch_up_tracker: - await clock.anim_attrs(effect, radius=calc_minimum_enclosing_circle_radius(e_down.pos, dest), duration=600, transition=out_quad) - e_up = touch_up_tracker.result - if dest.collidepoint(e_up.pos): - click_event.fire(e_down, e_up) - self._draw_ripple_effect = None - - def _draw(clip, bgcolor, draw_target, dest, self: Self): - image = self.image - with clip(draw_target, dest): - draw_target.fill(bgcolor) - if (draw := self._draw_ripple_effect) is not None: - draw(draw_target) - draw_target.blit(image, image.get_rect(center=dest.center)) - - _draw = partial(_draw, clip) + bgcolor = Color(bgcolor) + ripple_color = Color(ripple_color) + Color(bgcolor) + collidepoint = dest.collidepoint + effect = RippleEffect() + + touch_down = partial( + sdlevent.wait, C.MOUSEBUTTONDOWN, C.FINGERDOWN, priority=priority + 1, consume=True, + filter=lambda e: (not getattr(e, 'touch', False)) and collidepoint(e.pos)) + mouse_button_up = partial( + sdlevent.wait, C.MOUSEBUTTONUP, priority=priority + 1, consume=True, + filter=lambda e: e.button == e_down.button) + finger_up = partial( + sdlevent.wait, C.FINGERUP, priority=priority + 1, consume=True, + filter=lambda e: e.finger_id == e_down.finger_id) + + with ( + executor.register(partial(_draw, tmp_clip, image, image.get_rect(), bgcolor, draw_target, dest, effect), priority), + block_touch_down_events(sdlevent, priority, filter=lambda e: collidepoint(e.pos)), + ): + while True: + e_down = await touch_down() + effect.enable(draw_target, e_down.pos, 0, ripple_color) + touch_up = mouse_button_up if e_down.type == C.MOUSEBUTTONDOWN else finger_up + async with asyncgui.run_as_main(touch_up()) as touch_up_tracker: + await clock.anim_attrs(effect, radius=calc_minimum_enclosing_circle_radius(e_down.pos, dest), duration=600, transition=out_quad) + e_up = touch_up_tracker.result + if collidepoint(e_up.pos): + on_click(e_down, e_up) + effect.disable() + + +def _draw(tmp_clip, image, image_rect, bgcolor, draw_target, dest, effect): + with tmp_clip(draw_target, dest): + draw_target.fill(bgcolor) + effect.draw() + image_rect.center = dest.center + draw_target.blit(image, image_rect) diff --git a/examples/_uix/touch_indicator.py b/examples/_uix/touch_indicator.py index c394ac8..9783fc1 100644 --- a/examples/_uix/touch_indicator.py +++ b/examples/_uix/touch_indicator.py @@ -22,13 +22,13 @@ def _draw(pygame_draw_circle, draw_target, color, radius, line_width, self: Self _draw = partial(_draw, pygame.draw.circle) -async def touch_indicator(*, color="black", radius=60, line_width=4, **kwargs: Unpack[apg.CommonParams]): +async def touch_indicator(*, color="white", radius=60, line_width=4, priority, **kwargs: Unpack[apg.CommonParams]): color = Color(color) draw_target = kwargs["draw_target"] async with ( apg.open_nursery() as nursery, kwargs["sdlevent"].wait_freq( - C.MOUSEBUTTONDOWN, C.FINGERDOWN, priority=kwargs["priority"], + C.MOUSEBUTTONDOWN, C.FINGERDOWN, priority=priority, filter=lambda e: not getattr(e, 'touch', False) ) as touch_down, ): @@ -38,10 +38,10 @@ async def touch_indicator(*, color="black", radius=60, line_width=4, **kwargs: U f = draw_ring_under_mouse_cursor else: f = draw_ring_under_finger - nursery.start(f(e_down, ring=Ring(draw_target, color, e_down.pos, radius, line_width), **kwargs)) + nursery.start(f(e_down, priority=priority, ring=Ring(draw_target, color, e_down.pos, radius, line_width), **kwargs)) -async def draw_ring_under_mouse_cursor(e_down: Event, *, priority, executor, sdlevent, ring, **unsued): +async def draw_ring_under_mouse_cursor(e_down: Event, *, priority, executor, sdlevent, ring, **__): with executor.register(ring.draw, priority): async with ( apg.move_on_when(sdlevent.wait(C.MOUSEBUTTONUP, filter=lambda e: e.button == e_down.button, priority=priority)), @@ -52,7 +52,7 @@ async def draw_ring_under_mouse_cursor(e_down: Event, *, priority, executor, sdl ring.pos = e.pos -async def draw_ring_under_finger(e_down: Event, *, priority, executor, sdlevent, ring, **unused): +async def draw_ring_under_finger(e_down: Event, *, priority, executor, sdlevent, ring, **__): with executor.register(ring.draw, priority): async with ( apg.move_on_when(sdlevent.wait(C.FINGERUP, filter=lambda e: e.finger_id == e_down.finger_id, priority=priority)), diff --git a/examples/_uix_anchor_layout.py b/examples/_uix_anchor_layout.py index 5a9496a..55ff7a4 100644 --- a/examples/_uix_anchor_layout.py +++ b/examples/_uix_anchor_layout.py @@ -1,31 +1,29 @@ +from typing import Unpack from functools import partial import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg -from _uix.anchor_layout import AnchorLayout +from _uix.anchor_layout import anchor_layout -async def main(**kwargs): +async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() pygame.display.set_caption("Anchor Layout") - screen = pygame.display.set_mode((800, 600)) + kwargs["draw_target"] = screen = pygame.display.set_mode((800, 600)) font = pygame.font.SysFont(None, 30) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) - kwargs["draw_target"] = screen - - async with apg.open_nursery() as nursery: - dest = screen.get_rect().inflate(-20, -20) - for anchor in "topleft midtop topright midleft center midright bottomleft midbottom bottomright".split(): - AnchorLayout( - nursery, font.render(anchor, True, "white"), dest, - anchor_src=anchor, anchor_dest=anchor, priority=0x100, **kwargs) - await apg.sleep_forever() + + dest = screen.get_rect().inflate(-20, -20) + await apg.wait_all(*( + anchor_layout(font.render(anchor, True, "white"), dest, priority=0x100, anchor_image=anchor, anchor_dest=anchor, **kwargs) + for anchor in "topleft midtop topright midleft center midright bottomleft midbottom bottomright".split() + )) if __name__ == "__main__": diff --git a/examples/_uix_progress_spinner.py b/examples/_uix_progress_spinner.py index 728ccd6..5868449 100644 --- a/examples/_uix_progress_spinner.py +++ b/examples/_uix_progress_spinner.py @@ -1,23 +1,24 @@ +from typing import Unpack from functools import partial import pygame -from pygame.colordict import THECOLORS as COLORS -import asyncpygame +from pygame.colordict import THECOLORS +import asyncpygame as apg from _uix.progress_spinner import progress_spinner -async def main(**kwargs): +async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() pygame.display.set_caption("Progress Spinner") - screen = pygame.display.set_mode((600, 600)) + kwargs["draw_target"] = screen = pygame.display.set_mode((600, 600)) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["white"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) - await progress_spinner(screen.get_rect().scale_by(0.8), priority=0x100, draw_target=screen, **kwargs) + await progress_spinner(screen.get_rect().scale_by(0.8), priority=0x100, **kwargs) if __name__ == "__main__": - asyncpygame.run(main) + apg.run(main) diff --git a/examples/_uix_ripple_button.py b/examples/_uix_ripple_button.py index 9e1cc38..cc94e5d 100644 --- a/examples/_uix_ripple_button.py +++ b/examples/_uix_ripple_button.py @@ -1,29 +1,39 @@ +from typing import Unpack from functools import partial import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS from pygame import Rect import asyncpygame as apg -from _uix.ripple_button import RippleButton +from _uix.ripple_button import ripple_button -async def main(**kwargs): +async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() pygame.display.set_caption("Ripple Button") - screen = pygame.display.set_mode((800, 600)) + kwargs["draw_target"] = screen = pygame.display.set_mode((800, 600)) font = pygame.font.SysFont(None, 100) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) - kwargs["draw_target"] = screen - async with apg.open_nursery() as nursery: - RippleButton(nursery, font.render("PyGame", True, "white"), Rect(100, 100, 300, 200), priority=0x100, **kwargs) - RippleButton(nursery, font.render("Python", True, "white"), Rect(280, 240, 300, 200), priority=0x200, bgcolor="darkgreen", **kwargs) - await apg.sleep_forever() + await apg.wait_all( + ripple_button( + font.render("PyGame", True, "white"), + Rect(100, 100, 300, 200), + on_click=lambda *_: print("PyGame"), + priority=0x100, **kwargs), + ripple_button( + font.render("Python", True, "white"), + Rect(280, 240, 300, 200), + on_click=lambda *_: print("Python"), + bgcolor="darkgreen", + priority=0x200, + **kwargs), + ) if __name__ == "__main__": diff --git a/examples/_uix_touch_indicator.py b/examples/_uix_touch_indicator.py index 9ca014b..2bfbb05 100644 --- a/examples/_uix_touch_indicator.py +++ b/examples/_uix_touch_indicator.py @@ -1,22 +1,23 @@ +from typing import Unpack from functools import partial import pygame -from pygame.colordict import THECOLORS as COLORS -import asyncpygame +from pygame.colordict import THECOLORS +import asyncpygame as apg from _uix.touch_indicator import touch_indicator -async def main(**kwargs): +async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() pygame.display.set_caption("Touch Indicator") - screen = pygame.display.set_mode((600, 600)) + kwargs["draw_target"] = screen = pygame.display.set_mode((600, 600)) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["white"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) - await touch_indicator(draw_target=screen, priority=0x100, **kwargs) + await touch_indicator(priority=0x100, **kwargs) if __name__ == "__main__": - asyncpygame.run(main) + apg.run(main) diff --git a/examples/asking_question.py b/examples/asking_question.py index e8e1198..9f654bd 100644 --- a/examples/asking_question.py +++ b/examples/asking_question.py @@ -3,11 +3,11 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg from _uix.touch_indicator import touch_indicator -from _uix.ripple_button import RippleButton +from _uix.ripple_button import ripple_button from _uix.modal_dialog import ask_yes_no_question, show_messagebox @@ -19,19 +19,22 @@ async def main(**kwargs: Unpack[apg.CommonParams]): button_font = pygame.font.SysFont(None, 50) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) async with apg.open_nursery() as nursery: - nursery.start(touch_indicator(color="darkgreen", priority=0xFFFFFE00, **kwargs)) - button = RippleButton( - nursery, - button_image := button_font.render("open dialog", True, COLORS["white"]).convert_alpha(), - button_image.get_rect(centerx=screen_rect.centerx).inflate(40, 40).move_to(bottom=screen_rect.bottom - 20), - priority=0x100, **kwargs) + e_open = apg.Event() + s = nursery.start + s(touch_indicator(color="darkgreen", priority=0xFFFFFE00, **kwargs)) + s(ripple_button( + image := button_font.render("open dialog", True, THECOLORS["white"]).convert_alpha(), + image.get_rect(centerx=screen_rect.centerx).inflate(40, 40).move_to(bottom=screen_rect.bottom - 20), + priority=0x100, + on_click=e_open.fire, + **kwargs)) while True: - await button.to_be_clicked() + await e_open.wait() answer = await ask_yes_no_question("Do you like PyGame?", priority=0xFFFFFA00, **kwargs) await show_messagebox(f"You answered '{'Yes' if answer else 'No'}'.", priority=0xFFFFFA00, **kwargs) diff --git a/examples/loop_animation (skippable).py b/examples/loop_animation (skippable).py index 054e9bf..9225bd7 100644 --- a/examples/loop_animation (skippable).py +++ b/examples/loop_animation (skippable).py @@ -2,7 +2,7 @@ from functools import partial import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg @@ -12,17 +12,17 @@ async def main(**kwargs: Unpack[apg.CommonParams]): screen = pygame.display.set_mode((1280, 720)) font = pygame.font.SysFont(None, 200) - img = font.render("(^.^)", True, COLORS["white"]).convert_alpha() + img = font.render("(^.^)", True, THECOLORS["white"]).convert_alpha() img_rect = img.get_rect() screen_rect = screen.get_rect() r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(partial(screen.blit, img, img_rect), priority=0x100) r(pygame.display.flip, priority=0xFFFFFF00) del r - mouse_button_down = partial(kwargs["sdlevent"].wait, pygame.MOUSEBUTTONDOWN) + mouse_button_down = partial(kwargs["sdlevent"].wait, pygame.MOUSEBUTTONDOWN, priority=0x100) move_sprite = partial(kwargs["clock"].anim_attrs, img_rect, duration=1000) wait_any = apg.wait_any diff --git a/examples/loop_animation.py b/examples/loop_animation.py index 76e45b1..cd5308d 100644 --- a/examples/loop_animation.py +++ b/examples/loop_animation.py @@ -3,7 +3,7 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg @@ -13,12 +13,12 @@ async def main(**kwargs: Unpack[apg.CommonParams]): screen = pygame.display.set_mode((1280, 720)) font = pygame.font.SysFont(None, 200) - img = font.render("(^.^)", True, COLORS["white"]).convert_alpha() + img = font.render("(^.^)", True, THECOLORS["white"]).convert_alpha() img_rect = img.get_rect() screen_rect = screen.get_rect() r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(partial(screen.blit, img, img_rect), priority=0x100) r(pygame.display.flip, priority=0xFFFFFF00) del r diff --git a/examples/network_io.py b/examples/network_io.py deleted file mode 100644 index 53517ed..0000000 --- a/examples/network_io.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Unpack -from functools import partial - -import pygame -import pygame.font -from pygame.colordict import THECOLORS as COLORS -from pygame import Rect -import asyncpygame as apg -import requests - -from _uix.touch_indicator import touch_indicator -from _uix.ripple_button import RippleButton -from _uix.anchor_layout import AnchorLayout - - -async def main(**kwargs: Unpack[apg.CommonParams]): - pygame.init() - pygame.display.set_caption("Network I/O") - kwargs["draw_target"] = screen = pygame.display.set_mode((600, 800)) - font = pygame.font.SysFont(None, 50) - bgcolor = COLORS["black"] - fgcolor = COLORS["white"] - - r = kwargs["executor"].register - r(partial(screen.fill, bgcolor), priority=0) - r(pygame.display.flip, priority=0xFFFFFF00) - - async with apg.open_nursery() as nursery: - nursery.start(touch_indicator(color=fgcolor, priority=0xFFFFFE00, **kwargs)) - button = RippleButton( - nursery, - font.render("start", True, fgcolor).convert_alpha(), - Rect(0, 600, 600, 200).inflate(-40, -40), - priority=0x100, **kwargs) - await button.to_be_clicked() - - label = AnchorLayout( - nursery, - font.render("waiting for the server to respond", True, fgcolor, bgcolor).convert(screen), - Rect(0, 0, 600, 200).inflate(-40, -40), - priority=0x100, **kwargs) - button.image = font.render("cancel", True, fgcolor).convert_alpha() - tasks = await apg.wait_any( - kwargs["clock"].run_in_thread(lambda: requests.get("https://httpbin.org/delay/4"), polling_interval=200), - button.to_be_clicked(), - ) - - if tasks[0].finished: - text = tasks[0].result.json()['headers']['User-Agent'] - else: - text = "cancelled" - label.image = font.render(text, True, fgcolor, bgcolor).convert(screen) - button.image = font.render("Quit the App", True, fgcolor).convert_alpha() - await button.to_be_clicked() - apg.quit() - - -if __name__ == "__main__": - apg.run(main) diff --git a/examples/network_io_with_animation.py b/examples/network_io_with_animation.py index 621c4b5..c3671bc 100644 --- a/examples/network_io_with_animation.py +++ b/examples/network_io_with_animation.py @@ -3,60 +3,76 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS from pygame import Rect import asyncpygame as apg import requests from _uix.touch_indicator import touch_indicator from _uix.progress_spinner import progress_spinner -from _uix.ripple_button import RippleButton -from _uix.anchor_layout import AnchorLayout +from _uix.ripple_button import ripple_button +from _uix.anchor_layout import anchor_layout async def main(**kwargs: Unpack[apg.CommonParams]): pygame.init() - pygame.display.set_caption("Network I/O with Animation") + pygame.display.set_caption("Network I/O + Animation") kwargs["draw_target"] = screen = pygame.display.set_mode((600, 800)) font = pygame.font.SysFont(None, 50) - bgcolor = COLORS["black"] - fgcolor = COLORS["white"] + bgcolor = THECOLORS["black"] + fgcolor = THECOLORS["white"] r = kwargs["executor"].register r(partial(screen.fill, bgcolor), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) + del r - async with apg.open_nursery() as nursery: - nursery.start(touch_indicator(color=fgcolor, priority=0xFFFFFE00, **kwargs)) - button = RippleButton( - nursery, - font.render("start", True, fgcolor).convert_alpha(), - Rect(0, 600, 600, 200).inflate(-40, -40), - priority=0x100, **kwargs) - await button.to_be_clicked() + async with apg.run_as_main(touch_indicator(color=fgcolor, priority=0xFFFFFE00, **kwargs)): + e_click = apg.Event() + await apg.wait_any( + e_click.wait(), + ripple_button( + font.render("start", True, fgcolor).convert_alpha(), + button_dest := Rect(0, 600, 600, 200).inflate(-40, -40), + priority=0x100, on_click=e_click.fire, **kwargs), + ) - async with apg.run_as_daemon(progress_spinner( - Rect(0, 0, 400, 400).move_to(center=screen.get_rect().center), - color=fgcolor, priority=0x100, **kwargs, - )): - label = AnchorLayout( - nursery, + tasks = await apg.wait_any( + kwargs["clock"].run_in_thread( + lambda: requests.get("https://httpbin.org/delay/4"), + polling_interval=200, daemon=True), + e_click.wait(), + progress_spinner( + Rect(0, 0, 400, 400).move_to(center=screen.get_rect().center), + color=fgcolor, + priority=0x100, **kwargs), + ripple_button( + font.render("cancel", True, fgcolor).convert_alpha(), + button_dest, + on_click=e_click.fire, + priority=0x100, **kwargs), + anchor_layout( font.render("waiting for the server to respond", True, fgcolor, bgcolor).convert(screen), - Rect(0, 0, 600, 200).inflate(-40, -40), + label_dest := Rect(0, 0, 600, 200).inflate(-40, -40), priority=0x100, **kwargs) - button.image = font.render("cancel", True, fgcolor).convert_alpha() - tasks = await apg.wait_any( - kwargs["clock"].run_in_thread(lambda: requests.get("https://httpbin.org/delay/4"), polling_interval=200), - button.to_be_clicked(), - ) - + ) if tasks[0].finished: text = tasks[0].result.json()['headers']['User-Agent'] else: text = "cancelled" - label.image = font.render(text, True, fgcolor, bgcolor).convert(screen) - button.image = font.render("Quit the App", True, fgcolor).convert_alpha() - await button.to_be_clicked() + + await apg.wait_any( + e_click.wait(), + ripple_button( + font.render("Quit the App", True, fgcolor).convert_alpha(), + button_dest, + on_click=e_click.fire, + priority=0x100, **kwargs), + anchor_layout( + font.render(text, True, fgcolor, bgcolor).convert(screen), + label_dest, + priority=0x100, **kwargs) + ) apg.quit() diff --git a/examples/painter.py b/examples/painter.py index 9516523..4a4e142 100644 --- a/examples/painter.py +++ b/examples/painter.py @@ -9,14 +9,14 @@ import pygame from pygame import Event -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import pygame.constants as C import asyncpygame as apg from _uix.touch_indicator import touch_indicator async def painter(*, priority, **kwargs: Unpack[apg.CommonParams]): - colors = tuple(COLORS.values()) + colors = tuple(THECOLORS.values()) button2command = { 1: draw_rect, @@ -53,7 +53,7 @@ async def draw_ellipse(e_down: Event, *, draw_target, color, executor, sdlevent, ox, oy = e_down.pos rect = pygame.Rect(ox, oy, 0, 0) executor.register(partial(pygame.draw.ellipse, draw_target, color, rect, line_width), priority) - bbox_req = executor.register(partial(pygame.draw.rect, draw_target, COLORS["black"], rect, 1), priority) + bbox_req = executor.register(partial(pygame.draw.rect, draw_target, THECOLORS["black"], rect, 1), priority) async with ( apg.move_on_when(sdlevent.wait(C.MOUSEBUTTONUP, filter=lambda e: e.button == e_down.button, priority=priority)), sdlevent.wait_freq(C.MOUSEMOTION, priority=priority) as mouse_motion, @@ -73,7 +73,7 @@ async def main(**kwargs: Unpack[apg.CommonParams]): kwargs["draw_target"] = screen = pygame.display.set_mode((800, 600)) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["white"]), priority=0) + r(partial(screen.fill, THECOLORS["white"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) async with apg.open_nursery() as nursery: diff --git a/examples/scene_transition_showcase.py b/examples/scene_transition_showcase.py index 52909b2..97278c8 100644 --- a/examples/scene_transition_showcase.py +++ b/examples/scene_transition_showcase.py @@ -4,7 +4,7 @@ import pygame import pygame.font -from pygame.colordict import THECOLORS as COLORS +from pygame.colordict import THECOLORS import asyncpygame as apg from asyncpygame.scene_switcher import SceneSwitcher, FadeTransition, SlideTransition, no_transition @@ -15,7 +15,7 @@ async def main(**kwargs: Unpack[apg.CommonParams]): kwargs["draw_target"] = screen = pygame.display.set_mode((800, 600)) r = kwargs["executor"].register - r(partial(screen.fill, COLORS["black"]), priority=0) + r(partial(screen.fill, THECOLORS["black"]), priority=0) r(pygame.display.flip, priority=0xFFFFFF00) userdata = { 'font': pygame.font.SysFont(None, 50), @@ -31,12 +31,12 @@ async def main(**kwargs: Unpack[apg.CommonParams]): await SceneSwitcher().run(show_transition, priority=0xFFFFFD00, userdata=userdata, **kwargs) -async def show_transition(*, scene_switcher, userdata, executor, sdlevent, draw_target, **unused): +async def show_transition(*, scene_switcher, userdata, executor, sdlevent, draw_target, **__): font = userdata['font'] text, transition = next(userdata['transitions']) img = font.render(text, True, "white").convert_alpha() with executor.register(partial(draw_target.blit, img, img.get_rect(center=draw_target.get_rect().center)), priority=0x100): - await sdlevent.wait(pygame.MOUSEBUTTONDOWN) + await sdlevent.wait(pygame.MOUSEBUTTONDOWN, priority=0) scene_switcher.switch_to(show_transition, transition) await apg.sleep_forever() diff --git a/src/asyncpygame/__init__.py b/src/asyncpygame/__init__.py index 528d9ab..d925b36 100644 --- a/src/asyncpygame/__init__.py +++ b/src/asyncpygame/__init__.py @@ -1,12 +1,11 @@ __all__ = ( - 'DEFAULT_PRIORITY', 'run', 'quit', 'Clock', 'SDLEvent', 'PriorityExecutor', + 'run', 'quit', 'Clock', 'SDLEvent', 'PriorityExecutor', 'CommonParams', 'capture_current_frame', 'block_input_events', ) from asyncgui import * from asyncgui_ext.clock import Clock from ._runner import run, quit -from .constants import DEFAULT_PRIORITY from ._sdlevent import SDLEvent from ._priority_executor import PriorityExecutor from ._utils import CommonParams, capture_current_frame, block_input_events diff --git a/src/asyncpygame/_priority_executor.py b/src/asyncpygame/_priority_executor.py index 3e73335..44b2bed 100644 --- a/src/asyncpygame/_priority_executor.py +++ b/src/asyncpygame/_priority_executor.py @@ -1,5 +1,4 @@ from heapq import merge as heapq_merge -from .constants import DEFAULT_PRIORITY class ExecutionRequest: @@ -95,7 +94,7 @@ def __call__(self, *args): self._reqs = reqs2 self._reqs_2 = reqs - def register(self, func, priority=DEFAULT_PRIORITY) -> ExecutionRequest: + def register(self, func, priority) -> ExecutionRequest: req = ExecutionRequest(priority, func) self._reqs_to_be_added.append(req) return req diff --git a/src/asyncpygame/_runner.py b/src/asyncpygame/_runner.py index 50f91e5..261fae6 100644 --- a/src/asyncpygame/_runner.py +++ b/src/asyncpygame/_runner.py @@ -20,8 +20,8 @@ def run(main_func, *, fps=30, auto_quit=True): main_task = ap.start(main_func(clock=clock, sdlevent=sdlevent, executor=executor, pygame_clock=pygame_clock)) if auto_quit: - sdlevent.subscribe((pygame.QUIT, ), quit) - sdlevent.subscribe((pygame.KEYDOWN, ), lambda e, K=pygame.K_ESCAPE: e.key == K and quit()) + sdlevent.subscribe((pygame.QUIT, ), quit, priority=0) + sdlevent.subscribe((pygame.KEYDOWN, ), lambda e, K=pygame.K_ESCAPE: e.key == K and quit(), priority=0) # LOAD_FAST pygame_event_get = pygame.event.get diff --git a/src/asyncpygame/_sdlevent.py b/src/asyncpygame/_sdlevent.py index d455916..e5e1bce 100644 --- a/src/asyncpygame/_sdlevent.py +++ b/src/asyncpygame/_sdlevent.py @@ -8,8 +8,6 @@ from pygame.event import Event from asyncgui import _current_task, _sleep_forever, current_task -from .constants import DEFAULT_PRIORITY - def _callback(filter, consume, task_step, event: Event): if filter(event): @@ -141,7 +139,7 @@ def dispatch(self, event: Event): self._subs = subs2 self._subs_2 = subs - def subscribe(self, topics, callback, priority=DEFAULT_PRIORITY) -> Subscriber: + def subscribe(self, topics, callback, priority) -> Subscriber: ''' async型APIの礎となっているコールバック型API。直接触るべきではない。 ''' @@ -150,7 +148,7 @@ def subscribe(self, topics, callback, priority=DEFAULT_PRIORITY) -> Subscriber: return sub @types.coroutine - def wait(self, *event_types, filter=_accept_any, priority=DEFAULT_PRIORITY, consume=False) -> Awaitable[Event]: + def wait(self, *event_types, filter=_accept_any, priority, consume=False) -> Awaitable[Event]: ''' Waits for any of the specified types of events to occur. ''' @@ -162,7 +160,7 @@ def wait(self, *event_types, filter=_accept_any, priority=DEFAULT_PRIORITY, cons sub.cancel() @asynccontextmanager - async def wait_freq(self, *event_types, filter=_accept_any, priority=DEFAULT_PRIORITY, consume=False): + async def wait_freq(self, *event_types, filter=_accept_any, priority, consume=False): ''' ``MOUSEMOTION`` や ``FINGERMOTION`` などの頻りに起こりうるイベントを効率良く捌けるかもしれない機能。 以下のようなコードは diff --git a/src/asyncpygame/_utils.py b/src/asyncpygame/_utils.py index 9bd2eeb..fbc03e4 100644 --- a/src/asyncpygame/_utils.py +++ b/src/asyncpygame/_utils.py @@ -29,7 +29,6 @@ async def some_func(**kwargs: Unpack[CommonParams]): clock: Clock pygame_clock: pygame.time.Clock draw_target: Surface - priority: int async def capture_current_frame(executor: PriorityExecutor, priority, source: Surface) -> Awaitable[Surface]: diff --git a/src/asyncpygame/constants.py b/src/asyncpygame/constants.py index 7436b09..bc524e8 100644 --- a/src/asyncpygame/constants.py +++ b/src/asyncpygame/constants.py @@ -1,11 +1,10 @@ __all__ = ( - 'DEFAULT_PRIORITY', 'INPUT_EVENTS', + 'INPUT_EVENTS', ) import pygame.constants as C -DEFAULT_PRIORITY = 0 INPUT_EVENTS = { C.KEYDOWN, C.KEYUP, C.MOUSEMOTION, C.MOUSEBUTTONDOWN, C.MOUSEBUTTONUP, C.MOUSEWHEEL, diff --git a/tests/test_priority_executor.py b/tests/test_priority_executor.py index 394f0cc..9c6ff90 100644 --- a/tests/test_priority_executor.py +++ b/tests/test_priority_executor.py @@ -9,7 +9,7 @@ def executor(): def test_basis(executor): values = [] - req = executor.register(lambda: values.append('A')) + req = executor.register(lambda: values.append('A'), priority=0) assert values == [] executor() assert values == ['A', ] @@ -22,8 +22,8 @@ def test_basis(executor): def test_same_priority(executor): values = [] - req = executor.register(lambda: values.append('A')) - executor.register(lambda: values.append('B')) + req = executor.register(lambda: values.append('A'), priority=0) + executor.register(lambda: values.append('B'), priority=0) assert values == [] executor() assert sorted(values) == ['A', 'B', ] diff --git a/tests/test_sdlevent.py b/tests/test_sdlevent.py index 43ffd0d..a0845c1 100644 --- a/tests/test_sdlevent.py +++ b/tests/test_sdlevent.py @@ -10,7 +10,7 @@ def se(): def test_subscribe(se): received = [] - sub = se.subscribe((1, 2), received.append) + sub = se.subscribe((1, 2), received.append, priority=0) se.dispatch(E(1)) assert len(received) == 1 se.dispatch(E(0)) @@ -30,10 +30,10 @@ def test_two_subscribers_with_the_same_priority(se): def store_value(e): value_list.append(e.value) - sub = se.subscribe((1, ), store_value) + sub = se.subscribe((1, ), store_value, priority=0) se.dispatch(E(1, value='A')) assert value_list == ['A', ] - se.subscribe((1, ), store_value) + se.subscribe((1, ), store_value, priority=0) se.dispatch(E(1, value='B')) assert value_list == ['A', 'B', 'B',] sub.cancel() @@ -77,7 +77,7 @@ def test_stop_dispatching(se): def test_wait(se): from asyncgui import start - task = start(se.wait(1, 2)) + task = start(se.wait(1, 2, priority=0)) se.dispatch(E(0)) assert not task.finished se.dispatch(E(1)) @@ -87,7 +87,7 @@ def test_wait(se): def test_wait_with_filter(se): from asyncgui import start - task = start(se.wait(1, 2, filter=lambda e: e.value == 'A')) + task = start(se.wait(1, 2, filter=lambda e: e.value == 'A', priority=0)) se.dispatch(E(0, value='B')) assert not task.finished se.dispatch(E(0, value='A')) @@ -115,7 +115,7 @@ def test_wait_freq(se): from asyncgui import start async def async_fn(): - async with se.wait_freq(1, 2) as event: + async with se.wait_freq(1, 2, priority=0) as event: return await event() task = start(async_fn()) @@ -129,7 +129,7 @@ def test_wait_freq_with_filter(se): from asyncgui import start async def async_fn(): - async with se.wait_freq(1, 2, filter=lambda e: e.value == 'A') as event: + async with se.wait_freq(1, 2, filter=lambda e: e.value == 'A', priority=0) as event: return await event() task = start(async_fn()) @@ -168,7 +168,7 @@ def test_wait_freq_mixed_with_another_type_of_awaitable(se): from asyncgui import start, Event async def async_fn(): - async with se.wait_freq(1, 2) as event: + async with se.wait_freq(1, 2, priority=0) as event: await event() await ae.wait() await event()