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

Click and release behaviour #254

Merged
merged 7 commits into from
Nov 5, 2024
Merged
Changes from 1 commit
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
Next Next commit
Click and release behaviour
A button needs to be pressed first, and then on release, it triggers on_click
soerface committed Oct 27, 2024

Verified

This commit was signed with the committer’s verified signature.
soerface Sören Wegener
commit cbd4a46f1e102d5fb7eeb702ff434277d162b67b
49 changes: 45 additions & 4 deletions drinks_touch/elements/base_elm.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
class BaseElm(object):
def __init__(
self,
children: list["BaseElm"] | None = None,
pos=None,
height=None,
width=None,
@@ -13,8 +14,6 @@ def __init__(
padding: (
int | tuple[int, int] | tuple[int, int, int] | tuple[int, int, int, int]
) = 0,
*args,
**kwargs
):
if pos is None:
pos = (0, 0)
@@ -24,6 +23,10 @@ def __init__(
self.is_visible = True
self.align_right = align_right
self.align_bottom = align_bottom
self.focus = False
if children is None:
children = []
self.children = children
if not isinstance(padding, tuple):
self.padding_top = padding
self.padding_right = padding
@@ -74,14 +77,52 @@ def screen_pos(self):
def box(self):
return self.screen_pos + (self.width, self.height)

def events(self, events):
pass
def events(self, events, pos=None):
for event in events:
consumed = "consumed" in event.dict and event.consumed
if pos is None and hasattr(event, "pos"):
pos = event.pos
if pos is None:
continue
collides = self.collides_with(pos)
if collides:
transformed_pos = (
pos[0] - self.screen_pos[0],
pos[1] - self.screen_pos[1],
)
else:
transformed_pos = None

for child in self.children:
child.events(events, transformed_pos)

if event.type == pygame.MOUSEBUTTONDOWN:
if collides:
self.focus = True
else:
self.focus = False

elif event.type == pygame.MOUSEBUTTONUP:
if not hasattr(self, "on_click"):
continue

if not consumed and self.focus and collides:
self.on_click(*transformed_pos)
event.consumed = True
self.focus = False

@property
def visible(self):
# TODO get rid of is_visible
return self.is_visible

def collides_with(self, pos: tuple[int, int]) -> bool:
return (
self.box is not None
and self.visible
and pygame.Rect(self.box).collidepoint(pos[0], pos[1])
)

def render(self, *args, **kwargs) -> Surface:
surface = pygame.font.SysFont("monospace", 25).render(
"Return surface in render()", 1, (255, 0, 255)
19 changes: 4 additions & 15 deletions drinks_touch/elements/button.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
class Button(BaseElm):
def __init__(
self,
children: list["BaseElm"] | None = None,
font=FONTS["monospace"],
size=30,
text=None,
@@ -27,7 +28,7 @@ def __init__(
):
from . import Label

super().__init__(pos, size, size, *args, padding=padding, **kwargs)
super().__init__(children, pos, size, size, *args, padding=padding, **kwargs)

self.size = size
self.color = color
@@ -44,14 +45,6 @@ def __init__(
)
self.inner = inner

self.clicking = False

def pre_click(self):
self.clicking = True

def post_click(self):
self.clicking = False

def render(self, *args, **kwargs) -> pygame.Surface:
inner = self.inner.render(*args, **kwargs)

@@ -64,7 +57,7 @@ def render(self, *args, **kwargs) -> pygame.Surface:
self.height = size[1]

surface = pygame.Surface(size, pygame.SRCALPHA)
if self.clicking:
if self.focus:
surface.fill(tuple(c * 0.7 for c in self.color), (0, 0, *size))

if inner is not None:
@@ -75,8 +68,4 @@ def render(self, *args, **kwargs) -> pygame.Surface:
def on_click(self, x, y):
if self.on_click_handler is None:
raise NotImplementedError("No on_click handler defined")
self.pre_click()
try:
self.on_click_handler()
finally:
self.post_click()
self.on_click_handler()
11 changes: 9 additions & 2 deletions drinks_touch/elements/elm_list.py
Original file line number Diff line number Diff line change
@@ -4,8 +4,15 @@


class ElmList(BaseElm):
def __init__(self, height, width, pos=(0, 0), **kwargs):
super().__init__(pos, height, width)
def __init__(
self,
children: list["BaseElm"] | None = None,
height=None,
width=None,
pos=(0, 0),
**kwargs
):
super().__init__(children, pos, height, width)
self.pos = pos
self.elm_margin = kwargs.get("elm_margin", 5)
self.max_elm_count = kwargs.get("max_elm_count", 10)
27 changes: 13 additions & 14 deletions drinks_touch/elements/hbox.py
Original file line number Diff line number Diff line change
@@ -7,21 +7,20 @@ class HBox(BaseElm):

def __init__(
self,
elements: list[BaseElm],
children: list["BaseElm"] | None = None,
gap=5,
pos=(0, 0),
*args,
**kwargs,
):
super().__init__(pos, 0, 0, *args, **kwargs)
super().__init__(children, pos, 0, 0, *args, **kwargs)
self.pos = pos
self.elements = elements
self.gap = gap

def render(self, *args, **kwargs) -> pygame.Surface:
surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
x = self.padding_left
for element in self.elements:
for element in self.children:
element.pos = (x, self.padding_top)
element_surface = element.render(*args, **kwargs)
surface.blit(element_surface, element.pos)
@@ -31,31 +30,31 @@ def render(self, *args, **kwargs) -> pygame.Surface:
@property
def width(self):
return (
sum([element.width for element in self.elements])
+ (len(self.elements) - 1) * self.gap
sum([element.width for element in self.children])
+ (len(self.children) - 1) * self.gap
+ self.padding_left
+ self.padding_right
)

@property
def height(self):
return (
max([element.height for element in self.elements])
max([element.height for element in self.children])
+ self.padding_top
+ self.padding_bottom
)

def on_click(self, x, y):
for obj in self.elements:
if pygame.Rect(obj.box).collidepoint(x, y):
if hasattr(obj, "on_click"):
obj.on_click(x - obj.pos[0], y - obj.pos[1])
break
# def on_click(self, x, y):
# for obj in self.elements:
# if pygame.Rect(obj.box).collidepoint(x, y):
# if hasattr(obj, "on_click"):
# obj.on_click(x - obj.pos[0], y - obj.pos[1])
# break

def render_debug(self) -> pygame.Surface:
surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
surface.fill((0, 0, 255, 100))
for element in self.elements:
for element in self.children:
element_surface = element.render_debug()
surface.blit(element_surface, element.pos)
pygame.draw.rect(surface, (255, 0, 0), (0, 0, self.width, self.height), 1)
12 changes: 7 additions & 5 deletions drinks_touch/elements/image.py
Original file line number Diff line number Diff line change
@@ -7,13 +7,15 @@


class Image(BaseElm):
def __init__(self, *args, **kwargs):
self.src = kwargs.get("src", "drinks_touch/resources/images/test.jpg")
self.size = kwargs.get("size", None)
def __init__(
self, src="drinks_touch/resources/images/test.jpg", size=None, *args, **kwargs
):
self.src = src
self.size = size
self.img = pygame.image.load(self.src).convert_alpha()

if self.size:
self.img = pygame.transform.smoothscale(self.img, self.size)
if size:
self.img = pygame.transform.smoothscale(self.img, size)
super(Image, self).__init__(*args, **kwargs)

@property
43 changes: 26 additions & 17 deletions drinks_touch/elements/label.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from config import COLORS
import config
from .base_elm import BaseElm

import contextlib
@@ -12,25 +12,34 @@
class Label(BaseElm):
_font_cache = {}

def __init__(self, *args, **kwargs):
self.font_face = kwargs.get("font", "sans serif")
self.size = kwargs.get("size", 50)
self.max_width = kwargs.get("max_width", None)
# if True, pos marks top-right instead of top-left corner
self.align_right = kwargs.get("align_right", False)
self.text = kwargs.get("text", "<Label>")
self.color = kwargs.get("color", COLORS["infragelb"])
self.bg_color = kwargs.get("bg_color", None)
self.border_color = kwargs.get("border_color", None)
self.border_width = kwargs.get("border_width", 0)
self.padding = kwargs.get("padding", 0)
self.blink_frequency = kwargs.get("blink_frequency", 0)
def __init__(
self,
children: list["BaseElm"] | None = None,
text="<Label>",
font=config.FONTS["sans serif"],
size=35,
color=config.COLORS["infragelb"],
bg_color=None,
border_color=None,
border_width=0,
blink_frequency=0,
max_width=None,
*args,
**kwargs,
):
self.size = size
self.max_width = max_width
self.text = text
self.color = color
self.bg_color = bg_color
self.border_color = border_color
self.border_width = border_width
self.blink_frequency = blink_frequency

self.frame_counter = 0
pos = kwargs.pop("pos", (0, 0))
super().__init__(pos, self.size, self.size, *args, **kwargs)
super().__init__(children, height=self.size, width=self.size, *args, **kwargs)

self.font = Label.get_font(self.font_face, self.size)
self.font = Label.get_font(font, self.size)

@classmethod
def get_font(cls, font_face, size):
31 changes: 23 additions & 8 deletions drinks_touch/elements/progress.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import math

from config import COLORS
import config
from .base_elm import BaseElm

import contextlib
@@ -10,16 +10,31 @@


class Progress(BaseElm):
def __init__(self, pos=None, *args, **kwargs):
self.size = kwargs.get("size", 50)
self.color = kwargs.get("color", COLORS["infragelb"])
self.tick = kwargs.get("tick", self.__default_tick)
self.speed = kwargs.get("speed", 1 / 4.0) # 4 secs
self.on_elapsed = kwargs.get("on_elapsed", None)
def __init__(
self,
children: list["BaseElm"] | None = None,
pos=None,
size=50,
color=config.COLORS["infragelb"],
tick=None,
speed=1 / 4.0, # 4 secs
on_elapsed=None,
*args,
**kwargs,
):
self.size = size
self.color = color
if tick is None:
tick = self.__default_tick
self.tick = tick
self.speed = speed
self.on_elapsed = on_elapsed
self.value = 0
self.is_running = False

super(Progress, self).__init__(pos, self.size, self.size, *args, **kwargs)
super(Progress, self).__init__(
children, pos, self.size, self.size, *args, **kwargs
)
self.start()

def start(self):
24 changes: 15 additions & 9 deletions drinks_touch/elements/vbox.py
Original file line number Diff line number Diff line change
@@ -5,16 +5,22 @@

class VBox(BaseElm):

def __init__(self, elements: list[BaseElm], gap=5, pos=(0, 0), *args, **kwargs):
super().__init__(pos, 0, 0, *args, **kwargs)
def __init__(
self,
children: list["BaseElm"] | None = None,
gap=5,
pos=(0, 0),
*args,
**kwargs,
):
super().__init__(children, pos, 0, 0, *args, **kwargs)
self.pos = pos
self.elements = elements
self.gap = gap

def render(self, *args, **kwargs) -> pygame.Surface:
surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
y = self.padding_top
for element in self.elements:
for element in self.children:
element.pos = (self.padding_left, y)
element_surface = element.render(*args, **kwargs)
surface.blit(element_surface, element.pos)
@@ -24,22 +30,22 @@ def render(self, *args, **kwargs) -> pygame.Surface:
@property
def width(self):
return (
max([element.width for element in self.elements])
max([element.width for element in self.children])
+ self.padding_left
+ self.padding_right
)

@property
def height(self):
return (
sum([element.height for element in self.elements])
+ (len(self.elements) - 1) * self.gap
sum([element.height for element in self.children])
+ (len(self.children) - 1) * self.gap
+ self.padding_top
+ self.padding_bottom
)

def on_click(self, x, y):
for obj in self.elements:
for obj in self.children:
if pygame.Rect(obj.box).collidepoint(x, y):
if hasattr(obj, "on_click"):
obj.on_click(x - obj.pos[0], y - obj.pos[1])
@@ -48,7 +54,7 @@ def on_click(self, x, y):
def render_debug(self) -> pygame.Surface:
surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
surface.fill((0, 0, 255, 100))
for element in self.elements:
for element in self.children:
element_surface = element.render_debug()
surface.blit(element_surface, element.pos)
pygame.draw.rect(surface, (255, 0, 0), (0, 0, self.width, self.height), 1)
2 changes: 1 addition & 1 deletion drinks_touch/overlays.py
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ def events(self, events):
self.mouse_pos = event.pos
if self.mouse_pressed:
self.mouse_path.append(event.pos)
elif event.type == pygame.MOUSEBUTTONDOWN:
elif event.type == pygame.MOUSEBUTTONUP:
self.click_pos = event.pos
self.reset()

5 changes: 3 additions & 2 deletions drinks_touch/screens/enter_pin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import functools

from config import FONTS
from elements.button import Button
from elements.label import Label
@@ -81,8 +83,7 @@ def render_digit_btn(self, label, x, y):
Button(
text=label,
pos=(cord_x, cord_y),
click_func_param=self.add_char,
click_param=label,
on_click=functools.partial(self.add_char, label),
font=FONTS["monospace"],
size=50,
force_width=width,
2 changes: 1 addition & 1 deletion drinks_touch/screens/main.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ def on_start(self, *args, **kwargs):
self.objects = []
self.objects.append(Image(pos=(30, 20)))

self.objects.append(Label(text="member auswählen", pos=(65, 250), size=50))
self.objects.append(Label(text="member auswählen", pos=(65, 250), size=40))

i = 0

24 changes: 12 additions & 12 deletions drinks_touch/screens/profile.py
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ def on_start(self, *args, **kwargs):
Label(
text=self.account.name,
pos=(30, 120),
size=70,
size=40,
max_width=335 - 30 - 10, # balance.x - self.x - margin
)
)
@@ -71,24 +71,24 @@ def on_start(self, *args, **kwargs):
Label(
text="Guthaben",
pos=(330, 120),
size=30,
size=20,
)
)

self.label_verbrauch = Label(
text="Bisheriger Verbrauch:",
pos=(30, 180),
size=30,
size=15,
)
self.label_aufladungen = Label(
text="Aufladungen:",
pos=(30, 180),
size=30,
size=15,
)

self.processing = Label(
text="Moment bitte...",
size=40,
size=20,
pos=(150, 750),
)
self.processing.is_visible = False
@@ -98,22 +98,21 @@ def on_start(self, *args, **kwargs):
pos=(200, 50),
speed=1 / 30.0,
on_elapsed=self.time_elapsed,
click_func=self.btn_home,
)
self.objects.append(self.timeout)
self.timeout.start()

drink = DrinksManager.get_instance().get_selected_drink()
self.drink_info = Label(
text=drink["name"] if drink else "",
size=60,
size=30,
pos=(30, 630),
)

self.zuordnen = Button(
text="Trinken",
pos=(30, 690),
size=50,
size=40,
on_click=self.save_drink,
)
self.btn_aufladungen = Button(
@@ -129,13 +128,13 @@ def on_start(self, *args, **kwargs):
self.btn_abbrechen = Button(
text="Abbrechen",
pos=(290, 700),
size=30,
size=20,
on_click=self.btn_home,
)
self.btn_aufladen = Button(
text="Jetzt Aufladen",
pos=(210, 700),
size=30,
size=20,
on_click=functools.partial(
self.goto, RechargeScreen(self.screen, self.account)
),
@@ -214,7 +213,7 @@ def render_aufladungen(self):
if date != prev_date:
prev_date = date
self.elements_aufladungen.append(
Label(text=date, size=35, pos=(x, y + 15))
Label(text=date, size=30, pos=(x, y + 15))
)
y += 45
count_width = 120
@@ -223,7 +222,7 @@ def render_aufladungen(self):
Label(
text=time_text,
pos=(x + 10, y),
size=45,
size=25,
max_width=480 - x - margin_right - count_width,
)
)
@@ -233,6 +232,7 @@ def render_aufladungen(self):
align_right=True,
pos=(480 - margin_right, y - 5),
max_width=count_width,
size=25,
)
)
y += 35
12 changes: 6 additions & 6 deletions drinks_touch/screens/recharge_screen.py
Original file line number Diff line number Diff line change
@@ -79,24 +79,24 @@ def on_start(self, *args, **kwargs):
size=30,
on_click=partial(self.verify_payment, 50),
),
Label(text="Wirf Geld in die Kasse,", pos=(30, 100), size=50),
Label(text="und drück den passenden", pos=(30, 150), size=50),
Label(text="Knopf.", pos=(30, 200), size=50),
Label(text="Wirf Geld in die Kasse,", pos=(30, 100), size=40),
Label(text="und drück den passenden", pos=(30, 150), size=40),
Label(text="Knopf.", pos=(30, 200), size=40),
Image(src=qr_file, pos=(70, 470), size=(300, 330)),
]
self.objects.extend(self.select_objects)

self.verify_objects = [
Label(text="Hast du", pos=(30, 150), size=55),
Label(text="in die Kasse geworfen?", pos=(30, 250), size=55),
Label(text="Hast du", pos=(30, 150), size=35),
Label(text="in die Kasse geworfen?", pos=(30, 250), size=35),
Button(
text="Ja",
pos=(250, 400),
size=30,
on_click=self.save_payment,
),
]
self.verify_amount = Label(text="EUR X", pos=(30, 200), size=60)
self.verify_amount = Label(text="EUR X", pos=(30, 200), size=50)
self.verify_objects.append(self.verify_amount)

def back(self):
22 changes: 0 additions & 22 deletions drinks_touch/screens/screen.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import pygame

import config
from elements.base_elm import BaseElm
from screens.screen_manager import ScreenManager
@@ -24,26 +22,6 @@ def render(self, dt):
def events(self, events):
for obj in self.objects:
obj.events(events)
for event in events:
if "consumed" in event.dict and event.consumed:
continue

if event.type == pygame.MOUSEBUTTONUP:
if not hasattr(obj, "on_click"):
continue
pos = event.pos

if (
obj.box is not None
and obj.visible
and pygame.Rect(obj.box).collidepoint(pos[0], pos[1])
):
transformed_pos = (
pos[0] - obj.screen_pos[0],
pos[1] - obj.screen_pos[1],
)
obj.on_click(*transformed_pos)
event.consumed = True

@staticmethod
def back():
5 changes: 2 additions & 3 deletions drinks_touch/screens/wait_scan.py
Original file line number Diff line number Diff line change
@@ -108,7 +108,7 @@ def on_start(self, *args, **kwargs):

self.objects.append(
VBox(
elements=[
[
Label(
text="∑ = {}".format(total_balance_fmt),
size=25,
@@ -155,11 +155,10 @@ def on_start(self, *args, **kwargs):

self.objects.append(
HBox(
bottom_right_buttons,
pos=(480, 795),
align_right=True,
align_bottom=True,
elements=bottom_right_buttons,
right_to_left=True,
gap=5,
padding=(0, 5),
)

Unchanged files with check annotations Beta

# TODO: try if slim or alpine versions work.
FROM python:3.11.4-buster@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 AS development
ENV PYTHONUNBUFFERED 1

Check warning on line 6 in Dockerfile

GitHub Actions / Build / Docker

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
# old pygame dependency list
#