start implementation of recipe selection

This commit is contained in:
Jannes Magnusson
2025-10-18 17:26:46 +02:00
parent 7a3329e8f2
commit d619ec1859
7 changed files with 207 additions and 76 deletions

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Main",
"type": "debugpy",
"request": "launch",
"module": "frontend"
}
]
}

View File

@@ -56,9 +56,6 @@ class WeightApp(tk.Tk):
# Checkboxes for multiple view selection # Checkboxes for multiple view selection
self.view_vars = {} self.view_vars = {}
for display_type in DISPLAY_TYPES: for display_type in DISPLAY_TYPES:
if display_type == DISPLAY_TYPES.TIMER:
continue
var = tk.BooleanVar() var = tk.BooleanVar()
var.set(True) # Default to all enabled var.set(True) # Default to all enabled

View File

@@ -1,4 +1,4 @@
from enum import Flag from enum import Flag, Enum
# DEFAULT_CALIB = 307333.83 # DEFAULT_CALIB = 307333.83
DEFAULT_CALIB = -105030.71880199667 DEFAULT_CALIB = -105030.71880199667
@@ -14,4 +14,8 @@ MOV_AVG_DEFAULTS = {
class DISPLAY_TYPES(Flag): class DISPLAY_TYPES(Flag):
NUMBER = 1 NUMBER = 1
CIRCLE = 2 CIRCLE = 2
TIMER = 4
class DISPLAY_MODES(Enum):
MAIN = 1
SETTINGS = 2
RECIPE_SELECTION = 3

View File

@@ -0,0 +1,34 @@
from typing import Tuple
class ButtonInterface:
def has_button(self) -> Tuple[bool, bool, bool, bool]:
return False, False, False, False
def left_press(self):
pass
def left_long_press(self):
pass
def right_press(self):
pass
def right_long_press(self):
pass
def both_long_press(self):
pass
def render_left_press(self, draw, x, y):
pass
def render_left_long_press(self, draw, x, y):
pass
def render_right_press(self, draw, x, y):
pass
def render_right_long_press(self, draw, x, y):
pass

View File

@@ -6,22 +6,9 @@ from .draw_utils import draw_clock, draw_long_press
class ButtonsManager(View): class ButtonsManager(View):
# left, left long, right, right long, both long
config = {
'default': {
'left_button_press': 'start_timer_command',
'left_button_long_press': 'reset_timer_command',
'right_button_press': 'tare_command',
'right_button_long_press': 'select_recipe_command',
'both_buttons_long_press': 'open_settings_command',
},
'select_recipe': {},
'settings': {},
}
def __init__(self, parent, im_size, center, def __init__(self, parent, im_size, center,
**actions): **actions):
self.current_config = self.config['default'] self.current_view = actions['default']
self.long_press_threshold = 1000 # milliseconds self.long_press_threshold = 1000 # milliseconds
self.left_press_start = None self.left_press_start = None
self.right_press_start = None self.right_press_start = None
@@ -61,57 +48,44 @@ class ButtonsManager(View):
im = self.bkg_im.copy() im = self.bkg_im.copy()
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
has_buttons = self.current_view.has_button()
# Draw left button # Draw left button
if self.current_config.get('left_button_press', None): if has_buttons[0]:
draw.circle((10, 10), 2, fill='black') draw.circle((10, 10), 2, fill='black')
if self.current_config['left_button_press'] == 'start_timer_command': self.current_view.render_left_press(draw, 20, 10)
draw_clock(draw, (20, 10), radius=3)
if self.current_config.get('left_button_long_press', None): if has_buttons[1]:
y = self.size[1] - 10 y = self.size[1] - 10
draw_long_press(draw, (10, y)) draw_long_press(draw, (10, y))
if self.current_config['left_button_long_press'] == 'reset_timer_command': self.current_view.render_left_long_press(draw, 24, y)
draw_clock(draw, (24, y), radius=3)
draw.text((30, y - 5), "R", fill='black')
# Draw right button # Draw right button
if self.current_config.get('right_button_press', None): if has_buttons[2]:
draw.circle((self.size[0] - 10, 10), 2, fill='black') draw.circle((self.size[0] - 10, 10), 2, fill='black')
if self.current_config['right_button_press'] == 'tare_command': self.current_view.render_right_press(draw, self.size[0] - 20, 4)
draw.text((self.size[0] - 20, 4), "T", fill='black')
if self.current_config.get('right_button_long_press', None): if has_buttons[3]:
y = self.size[1] - 10 y = self.size[1] - 10
draw_long_press(draw, (self.size[0] - 10, y)) draw_long_press(draw, (self.size[0] - 10, y))
if self.current_config['right_button_long_press'] == 'select_recipe_command': self.current_view.render_right_long_press(draw, self.size[0] - 24, y)
draw.text((self.size[0] - 22, y - 5), "R", fill='black')
return im return im
############ BUTTON ACTIONS ########### ############ BUTTON ACTIONS ###########
def left_button_press(self): def left_button_press(self):
action = self.current_config.get('left_button_press', None) self.current_view.left_press()
if action:
self.__getattribute__(action)()
def left_button_long_press(self): def left_button_long_press(self):
action = self.current_config.get('left_button_long_press', None) self.current_view.left_long_press()
if action:
self.__getattribute__(action)()
def right_button_press(self): def right_button_press(self):
action = self.current_config.get('right_button_press', None) self.current_view.right_press()
if action:
self.__getattribute__(action)()
def right_button_long_press(self): def right_button_long_press(self):
action = self.current_config.get('right_button_long_press', None) self.current_view.right_long_press()
if action:
self.__getattribute__(action)()
def both_buttons_long_press(self): def both_buttons_long_press(self):
action = self.current_config.get('both_buttons_long_press', None) self.current_view.both_long_press()
if action:
self.__getattribute__(action)()
############ BUTTON PRESS HANDLERS ########### ############ BUTTON PRESS HANDLERS ###########
def _left_button_press_start(self, event): def _left_button_press_start(self, event):
@@ -162,7 +136,7 @@ class ButtonsManager(View):
"""Handle both buttons press start""" """Handle both buttons press start"""
self.both_press_start = self.ui.after_idle(lambda: None) # Get current time reference self.both_press_start = self.ui.after_idle(lambda: None) # Get current time reference
# Schedule long press detection # Schedule long press detection
self.both_press_job = self.ui.after(self.long_press_threshold, self._both_long_press_detected) self.both_press_job = self.ui.after(self.long_press_threshold, self._both_buttons_long_press_detected)
def _both_buttons_press_end(self, event): def _both_buttons_press_end(self, event):
"""Handle both buttons press end""" """Handle both buttons press end"""

View File

@@ -1,14 +1,18 @@
import io import io
import tkinter as tk import tkinter as tk
from tkinter import Frame, Canvas, ttk, PhotoImage from tkinter import Frame, Canvas, ttk, PhotoImage
from enum import Enum
from PIL import ImageChops from PIL import ImageChops
from ..config import DISPLAY_TYPES from ..config import DISPLAY_TYPES, DISPLAY_MODES
from . import NumberView, CircleView, TimerView from . import NumberView, CircleView, TimerView
from .button_interface import ButtonInterface
from .buttons_manager import ButtonsManager from .buttons_manager import ButtonsManager
from .recipe_selection import RecipeSelection
from .draw_utils import draw_clock
class MainView(tk.Frame): class MainView(tk.Frame, ButtonInterface):
def __init__(self, parent, def __init__(self, parent,
tare_command=None, tare_command=None,
calibrate_command=None, calibrate_command=None,
@@ -24,23 +28,58 @@ class MainView(tk.Frame):
self.calibrate_button = ttk.Button(self.actions, text="Calibrate", command=calibrate_command) self.calibrate_button = ttk.Button(self.actions, text="Calibrate", command=calibrate_command)
self.calibrate_button.pack() self.calibrate_button.pack()
# self.tare_button = ttk.Button(self.actions, text="Tare", command=tare_command)
# self.tare_button.pack()
self.im_size = (168, 144) self.im_size = (168, 144)
self.center = (168 // 2, 144 // 2) self.center = (168 // 2, 144 // 2)
# Create timer view that's always active # Create timer view that's always active
self.timer_view = TimerView(self.actions, self.im_size, self.center)
self.canvas = Canvas(self, width=168, height=144, background='white', self.canvas = Canvas(self, width=168, height=144, background='white',
highlightthickness=1, highlightbackground="black") highlightthickness=1, highlightbackground="black")
self.canvas.pack() self.canvas.pack()
self.timer_view = TimerView(self.actions, self.im_size, self.center)
self.recipe_selection = RecipeSelection(self, self.im_size, self.center)
self.buttons = ButtonsManager(self, self.im_size, self.center, self.buttons = ButtonsManager(self, self.im_size, self.center,
tare_command=self.tare_command, default=self,
start_timer_command=self.timer_view.toggle_timer, select_recipe=self.recipe_selection)
reset_timer_command=self.timer_view.reset_timer)
self.curr_mode = DISPLAY_MODES.MAIN
def has_button(self):
return True, True, True, True
def left_press(self):
self.timer_view.toggle_timer()
def left_long_press(self):
self.timer_view.reset_timer()
def right_press(self):
self.tare_command()
def right_long_press(self):
self.enter_recipe_selection()
def render_left_press(self, draw, x, y):
draw_clock(draw, (x, y), radius=3)
def render_left_long_press(self, draw, x, y):
draw_clock(draw, (x, y), radius=3)
draw.text((x + 6, y - 5), "R", fill='black')
def render_right_press(self, draw, x, y):
draw.text((x, y), "T", fill='black')
def render_right_long_press(self, draw, x, y):
draw.text((x, y - 5), "R", fill='black')
def enter_recipe_selection(self):
self.curr_mode = DISPLAY_MODES.RECIPE_SELECTION
self.buttons.current_view = self.recipe_selection
self.refresh(0.0)
################ VIEW MANAGEMENT ################ ################ VIEW MANAGEMENT ################
@@ -64,17 +103,25 @@ class MainView(tk.Frame):
def refresh(self, weight: float): def refresh(self, weight: float):
ims = [] ims = []
# Always include timer and button view if self.curr_mode == DISPLAY_MODES.MAIN:
if self.timer_view: # Always include timer and button view
timer_im = self.timer_view.update_weight(weight) if self.timer_view:
button_im = self.buttons.update_weight(weight) timer_im = self.timer_view.update_weight(weight)
ims.append(timer_im) button_im = self.buttons.update_weight(weight)
ims.append(button_im) ims.append(timer_im)
ims.append(button_im)
# Add other selected views
for view in self.views:
im = view.update_weight(weight)
ims.append(im)
elif self.curr_mode == DISPLAY_MODES.RECIPE_SELECTION:
button_im = self.buttons.update_weight(weight)
ims.append(button_im)
recipe_im = self.recipe_selection.update_weight(weight)
ims.append(recipe_im)
# Add other selected views
for view in self.views:
im = view.update_weight(weight)
ims.append(im)
self.canvas.delete("all") self.canvas.delete("all")
# Combine images by logical_and # Combine images by logical_and
@@ -91,10 +138,3 @@ class MainView(tk.Frame):
# Load into PhotoImage and display on canvas # Load into PhotoImage and display on canvas
self.photo = PhotoImage(data=buffer.getvalue()) self.photo = PhotoImage(data=buffer.getvalue())
self.canvas.create_image(0, 0, anchor="nw", image=self.photo) self.canvas.create_image(0, 0, anchor="nw", image=self.photo)
########### BUTTON PRESS HANDLING ###########

View File

@@ -0,0 +1,67 @@
from typing import Tuple
from .base import View
from .button_interface import ButtonInterface
from PIL import ImageDraw, Image
class RecipeSelection(View, ButtonInterface):
recipes = [
"Recipe 1",
"Recipe 2",
]
def __init__(self, parent, im_size, center):
self.selected_index = 0
super().__init__(parent, im_size, center)
def _get_visual_recipes(self):
recipes = self.recipes + ['+']
if len(self.recipes) < 5:
return recipes, 0
start = max(0, self.selected_index - 2)
end = min(len(recipes), start + 5)
recipes = recipes[start:end]
if len(recipes) < 5:
recipes += ['+']
return recipes, start
def update_weight(self, weight: float) -> Image.Image:
im = self.bkg_im.copy()
draw = ImageDraw.Draw(im)
recipes, start = self._get_visual_recipes()
for idx, recipe in enumerate(recipes):
if idx + start == self.selected_index:
draw.rectangle((35, 8 + idx * 20, 130, 25 + idx * 20), fill='black')
draw.text((40, 10 + idx * 20), recipe, fill='white')
else:
draw.text((40, 10 + idx * 20), recipe, fill='black')
return im
def left_press(self):
self.selected_index = (self.selected_index - 1) % (len(self.recipes) + 1)
def right_press(self):
self.selected_index = (self.selected_index + 1) % (len(self.recipes) + 1)
def has_button(self) -> Tuple[bool, bool, bool, bool]:
return True, True, True, True
def render_left_press(self, draw, x, y):
draw.regular_polygon((x, y+2, 5), 3, fill='black')
def render_left_long_press(self, draw, x, y):
pass
def render_right_press(self, draw, x, y):
draw.regular_polygon((x, y+4, 5), 3, fill='black', rotation=180)
def render_right_long_press(self, draw, x, y):
pass