Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an example, whack_a_human.py #12

Merged
merged 7 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions examples/_uix/inapp_mouse_cursor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'''
In-app mouse cursor.
'''

__all__ = ('inapp_mouse_cursor', )

from functools import partial
from typing import Unpack

import pygame
from pygame.math import Vector2
from pygame import Color
import pygame.constants as C
import asyncpygame as apg


def generate_cursor_image(size: int, color) -> pygame.Surface:
cursor_img = pygame.Surface((size, size))
bgcolor = Color("black")
color = Color(color)
if color == bgcolor:
bgcolor = Color("white")
cursor_img.fill(bgcolor)
hs = size // 2
v = size // 10
points = (
(0, 0),
(hs, size),
(hs + v, hs + v),
(size, hs),
)
pygame.draw.polygon(cursor_img, color, points)
cursor_img = cursor_img.convert()
cursor_img.set_colorkey(bgcolor)
return cursor_img


async def inapp_mouse_cursor(*, color="white", size=60, initial_pos=(-0xFFFF, -0xFFFF), priority, **kwargs: Unpack[apg.CommonParams]):
img = generate_cursor_image(size, color)
pos = Vector2(initial_pos)

with (
kwargs["executor"].register(partial(kwargs["draw_target"].blit, img, pos), priority),
kwargs["sdlevent"].subscribe((C.MOUSEMOTION, ), lambda e, update=pos.update: update(e.pos), priority),
):
await apg.sleep_forever()
107 changes: 82 additions & 25 deletions examples/_uix/modal_dialog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
__all__ = ('show_messagebox', 'ask_yes_no_question', )
__all__ = ('show_messagebox', 'ask_yes_no_question', 'message_with_spinner', )

from typing import Unpack
from collections.abc import Sequence
from contextlib import asynccontextmanager
from functools import partial

Expand All @@ -12,6 +13,7 @@
from asyncpygame import CommonParams, block_input_events, Clock
from _uix.ripple_button import ripple_button
from _uix.anchor_layout import anchor_layout
from _uix.progress_spinner import progress_spinner


@asynccontextmanager
Expand All @@ -36,7 +38,7 @@ async def move_rects_vertically(clock: Clock, rects, movement, duration):


async def show_messagebox(
message, priority, *, dialog_size: Rect=None, font=None, text_ok='OK',
message, priority, *, dialog_size: Sequence=None, font=None, text_ok='OK',
**kwargs: Unpack[CommonParams]) -> bool:
'''
.. code-block::
Expand All @@ -53,33 +55,34 @@ async def show_messagebox(
async with darken(priority=priority, **kwargs), asyncgui.open_nursery() as nursery:
target_rect = draw_target.get_rect()
if dialog_size is None:
dialog_size = target_rect.inflate(-100, 0)
dialog_size.height = dialog_size.width // 2
dialog_dest = dialog_size.move_to(bottom=target_rect.top)
w = target_rect.width - 100
dialog_size = (w, w / 2)
del w
dest = Rect(0, 0, *dialog_size).move_to(midbottom=target_rect.midtop)
e_ok = asyncgui.Event()
with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dialog_dest), priority=priority + 1):
with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dest), priority=priority + 1):
s = nursery.start
s(anchor_layout(
font.render(message, True, "black", bgcolor).convert(draw_target),
label_dest := dialog_dest.scale_by(1.0, 0.7).move_to(top=dialog_dest.top).inflate(-10, -10),
label_dest := dest.scale_by(1.0, 0.7).move_to(top=dest.top).inflate(-10, -10),
priority + 2,
**kwargs), daemon=True)
s(ripple_button(
font.render(text_ok, True, "white"),
button_dest := dialog_dest.scale_by(0.5, 0.3).move_to(midbottom=dialog_dest.midbottom).inflate(-20, -20),
font.render(text_ok, True, "white").convert_alpha(),
button_dest := dest.scale_by(0.5, 0.3).move_to(midbottom=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
rects = (dest, label_dest, button_dest, )
y_movement = target_rect.centery - dest.centery
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, priority, *, dialog_size: Rect=None, font=None, text_yes='Yes', text_no='No',
question, priority, *, dialog_size: Sequence=None, font=None, text_yes='Yes', text_no='No',
**kwargs: Unpack[CommonParams]) -> bool:
'''
.. code-block::
Expand All @@ -96,33 +99,87 @@ async def ask_yes_no_question(
async with darken(priority=priority, **kwargs), asyncgui.open_nursery() as nursery:
target_rect = draw_target.get_rect()
if dialog_size is None:
dialog_size = target_rect.inflate(-100, 0)
dialog_size.height = dialog_size.width // 2
dialog_dest = dialog_size.move_to(bottom=target_rect.top)
w = target_rect.width - 100
dialog_size = (w, w / 2)
del w
dest = Rect(0, 0, *dialog_size).move_to(midbottom=target_rect.midtop)
e_yes = asyncgui.Event()
e_no = asyncgui.Event()
with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dialog_dest), priority=priority + 1):
spacing = 20
h = (dest.height - 3 * spacing) / 2
w = dest.width - 2 * spacing
label_size = (w, h)
button_size = ((w - spacing) / 2, h)
del w, h
with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dest), priority=priority + 1):
s = nursery.start
s(anchor_layout(
font.render(question, True, "black", bgcolor).convert(draw_target),
label_dest := dialog_dest.scale_by(1.0, 0.5).move_to(top=dialog_dest.top).inflate(-10, -10),
label_dest := Rect(dest.x + spacing, dest.y + spacing, *label_size),
priority + 2,
**kwargs), daemon=True)
s(ripple_button(
font.render(text_yes, True, "white"),
yes_button_dest := dialog_dest.scale_by(0.5, 0.5).move_to(bottomright=dialog_dest.bottomright).inflate(-20, -20),
font.render(text_no, True, "white").convert_alpha(),
no_button_dest := Rect(label_dest.x, label_dest.bottom + spacing, *button_size),
priority + 2,
on_click=e_yes.fire,
on_click=e_no.fire,
**kwargs), daemon=True)
s(ripple_button(
font.render(text_no, True, "white"),
no_button_dest := dialog_dest.scale_by(0.5, 0.5).move_to(bottomleft=dialog_dest.bottomleft).inflate(-20, -20),
font.render(text_yes, True, "white").convert_alpha(),
yes_button_dest := Rect(no_button_dest.right + spacing, no_button_dest.y, *button_size),
priority + 2,
on_click=e_no.fire,
on_click=e_yes.fire,
**kwargs), daemon=True)
rects = (dialog_dest, label_dest, yes_button_dest, no_button_dest, )
y_movement = target_rect.centery - dialog_dest.centery
rects = (dest, label_dest, yes_button_dest, no_button_dest, )
y_movement = target_rect.centery - dest.centery
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


@asynccontextmanager
async def message_with_spinner(
message, priority, *, dialog_size: Sequence=None, font=None,
**kwargs: Unpack[CommonParams]):
'''
.. code-block::

async with message_with_spinner("Hello World", priority=0xFFFFFA00, **kwargs):
...
'''
bgcolor = THECOLORS["grey90"]
clock = kwargs["clock"]
draw_target = kwargs["draw_target"]
if font is None:
font = SysFont(None, 40)

with block_input_events(kwargs["sdlevent"], priority):
async with darken(priority=priority, **kwargs), asyncgui.open_nursery() as nursery:
target_rect = draw_target.get_rect()
if dialog_size is None:
w = target_rect.width - 100
dialog_size = (w, w / 2)
del w
dest = Rect(0, 0, *dialog_size).move_to(midbottom=target_rect.midtop)
with kwargs["executor"].register(partial(draw_target.fill, bgcolor, dest), priority=priority + 1):
s = nursery.start
s(anchor_layout(
font.render(message, True, "black", bgcolor).convert(draw_target),
label_dest := dest.scale_by(1.0, 0.2).move_to(top=dest.top).inflate(-10, -10),
priority + 2,
**kwargs), daemon=True)
spinner_dest = dest.scale_by(1.0, 0.8)
spinner_dest.size = (min(spinner_dest.size), ) * 2
spinner_dest.midbottom = dest.midbottom
spinner_dest.inflate_ip(-20, -20)
s(progress_spinner(
spinner_dest,
priority + 2,
color="black",
**kwargs), daemon=True)
rects = (dest, label_dest, spinner_dest, )
y_movement = target_rect.centery - dest.centery
await move_rects_vertically(clock, rects, y_movement, duration=200)
yield
await move_rects_vertically(clock, rects, -y_movement, duration=200)
22 changes: 15 additions & 7 deletions examples/_uix/touch_indicator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__all__ = ('touch_indicator', )

from functools import partial
from typing import Unpack, Self
from typing import Unpack

import pygame
from pygame import Color, Event
Expand All @@ -12,14 +12,22 @@
class Ring:
__slots__ = ('draw', 'pos', )

def __init__(self, draw_target, color, pos, radius, line_width):
self.draw = partial(self._draw, draw_target, color, radius, line_width, self)
self.pos = pos
def __init__(self, draw_target: pygame.Surface, color, initial_pos, radius, line_width):
ring_img = pygame.Surface((radius * 2, radius * 2)).convert(draw_target)
color = Color(color)
bgcolor = Color("black")
if color == bgcolor:
bgcolor = Color("white")
ring_img.fill(bgcolor)
ring_img.set_colorkey(bgcolor)
pygame.draw.circle(ring_img, color, (radius, radius), radius, line_width)

def _draw(pygame_draw_circle, draw_target, color, radius, line_width, self: Self):
pygame_draw_circle(draw_target, color, self.pos, radius, line_width)
self.draw = partial(self.__class__._draw, self, draw_target.blit, ring_img, ring_img.get_rect())
self.pos = initial_pos

_draw = partial(_draw, pygame.draw.circle)
def _draw(self, blit, ring_img, ring_dest):
ring_dest.center = self.pos
blit(ring_img, ring_dest)


async def touch_indicator(*, color="white", radius=60, line_width=4, priority, **kwargs: Unpack[apg.CommonParams]):
Expand Down
24 changes: 24 additions & 0 deletions examples/_uix_inapp_mouse_cursor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Unpack
from functools import partial

import pygame
from pygame.colordict import THECOLORS
import asyncpygame as apg
from _uix.inapp_mouse_cursor import inapp_mouse_cursor


async def main(**kwargs: Unpack[apg.CommonParams]):
pygame.init()
pygame.mouse.set_visible(False)
pygame.display.set_caption("In-app mouse cursor")
kwargs["draw_target"] = screen = pygame.display.set_mode((600, 600))

r = kwargs["executor"].register
r(partial(screen.fill, THECOLORS["black"]), priority=0)
r(pygame.display.flip, priority=0xFFFFFF00)

await inapp_mouse_cursor(priority=0x100, **kwargs)


if __name__ == "__main__":
apg.run(main)
Empty file added examples/_utils/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions examples/_utils/convert_sound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
__all__ = ("convert_sound", )


def convert_sound(source: bytes, *, in_format="wav", out_format="wav", out_codec="pcm_s16le") -> bytes:
'''
Converts an audio source to another format using ffmpeg.
'''
import subprocess

ffmpeg_cmd = (
"ffmpeg",
"-f", in_format,
"-i", "pipe:0", # stdin
"-f", out_format,
"-codec:a", out_codec,
"pipe:1", # stdout
)
p = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0)
return p.communicate(source)[0]
Loading
Loading