diff --git a/frontend/views/main_view.py b/frontend/views/main_view.py index 727a935..4c8b0c9 100644 --- a/frontend/views/main_view.py +++ b/frontend/views/main_view.py @@ -9,7 +9,7 @@ from ..config import DISPLAY_TYPES, DISPLAY_MODES from . import NumberView, CircleView, TimerView from .button_interface import ButtonInterface from .buttons_manager import ButtonsManager -from .recipe_selection import RecipeSelection +from .recipes.recipe_selection import RecipeSelection from .draw_utils import draw_clock class MainView(tk.Frame, ButtonInterface): @@ -38,7 +38,7 @@ class MainView(tk.Frame, ButtonInterface): self.timer_view = TimerView(self.actions, self.im_size, self.center) - self.recipe_selection = RecipeSelection(self, self.im_size, self.center) + self.recipe_selection = RecipeSelection(self, self.im_size, self.center, deactivate_command=self.enter_main_mode) self.buttons = ButtonsManager(self, self.im_size, self.center, default=self, select_recipe=self.recipe_selection) @@ -66,7 +66,7 @@ class MainView(tk.Frame, ButtonInterface): 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') + draw.text((x + 6, y - 5), "0.0", fill='black') def render_right_press(self, draw, x, y): draw.text((x, y), "T", fill='black') @@ -75,6 +75,10 @@ class MainView(tk.Frame, ButtonInterface): draw.text((x, y - 5), "R", fill='black') + def enter_main_mode(self): + self.curr_mode = DISPLAY_MODES.MAIN + self.buttons.current_view = self + self.refresh(0.0) def enter_recipe_selection(self): self.curr_mode = DISPLAY_MODES.RECIPE_SELECTION diff --git a/frontend/views/recipe_selection.py b/frontend/views/recipe_selection.py deleted file mode 100644 index bf041d0..0000000 --- a/frontend/views/recipe_selection.py +++ /dev/null @@ -1,67 +0,0 @@ -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 diff --git a/frontend/views/recipes/__init__.py b/frontend/views/recipes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/views/recipes/add_recipe.py b/frontend/views/recipes/add_recipe.py new file mode 100644 index 0000000..a9a829e --- /dev/null +++ b/frontend/views/recipes/add_recipe.py @@ -0,0 +1,39 @@ +from typing import Tuple +from PIL import ImageDraw, Image + +from ..base import View +from ..button_interface import ButtonInterface + +class AddRecipe(View, ButtonInterface): + def __init__(self, parent, im_size, center, + save_command=None, cancel_command=None): + self.save_command = save_command + self.cancel_command = cancel_command + super().__init__(parent, im_size, center) + + def update_weight(self, weight: float) -> Image.Image: + im = self.bkg_im.copy() + draw = ImageDraw.Draw(im) + + draw.text((40, 30), "Add Recipe", fill='black') + draw.text((40, 60), "Save", fill='black') + draw.text((40, 90), "Cancel", fill='black') + + return im + + def left_press(self): + if self.save_command: + self.save_command() + + def right_press(self): + if self.cancel_command: + self.cancel_command() + + def has_button(self) -> Tuple[bool, bool, bool, bool]: + return True, False, True, False + + def render_left_press(self, draw, x, y): + draw.text((x, y), 'Save', fill='black') + + def render_right_press(self, draw, x, y): + draw.text((x, y), 'Cancel', fill='black') \ No newline at end of file diff --git a/frontend/views/recipes/recipe.py b/frontend/views/recipes/recipe.py new file mode 100644 index 0000000..5400ce5 --- /dev/null +++ b/frontend/views/recipes/recipe.py @@ -0,0 +1,61 @@ +from __future__ import annotations +from typing import Union + +from enum import Enum + +class Recipe: + def __init__(self, name: str, steps: list[Step]): + self.name = name + self.steps = steps + [Step(StepType.SECTION, "Enjoy")] + + def __str__(self): + return self.name + + +class StepType(Enum): + SECTION = 0 + WEIGH = 1 + START_TIME = 2 + WAIT_TIME_FINISHED = 3 + TARE = 4 + +class Step: + + def __init__(self, + step_type: StepType, + value: float | str = None): + self.step_type = step_type + self.value = value + + +###### example recipes ######### +V60 = Recipe( + "V60", + [ + Step(StepType.SECTION, "Grind"), + Step(StepType.TARE), + Step(StepType.WEIGH, 15), + + Step(StepType.SECTION, "Brew"), + Step(StepType.TARE), + Step(StepType.START_TIME, 45), + Step(StepType.WEIGH, 50), + Step(StepType.WAIT_TIME_FINISHED), + Step(StepType.START_TIME, -1), + Step(StepType.WEIGH, 250), + ] +) + +ESPRESSO = Recipe( + "Espresso", + [ + Step(StepType.SECTION, "Grind"), + Step(StepType.TARE), + Step(StepType.WEIGH, 18), + + Step(StepType.SECTION, "Brew"), + Step(StepType.TARE), + Step(StepType.START_TIME, -1), + Step(StepType.WEIGH, 40), + ] +) \ No newline at end of file diff --git a/frontend/views/recipes/recipe_selection.py b/frontend/views/recipes/recipe_selection.py new file mode 100644 index 0000000..26b3dd6 --- /dev/null +++ b/frontend/views/recipes/recipe_selection.py @@ -0,0 +1,88 @@ +from typing import Tuple + +from ..base import View +from ..button_interface import ButtonInterface + +from .recipe import V60, ESPRESSO + +from PIL import ImageDraw, Image + +class RecipeSelection(View, ButtonInterface): + + recipes = [ + V60, + ESPRESSO + ] + + def __init__(self, parent, im_size, center, deactivate_command=None): + self.selected_index = 0 + self.deactivate_command = deactivate_command + super().__init__(parent, im_size, center) + + def _get_visual_recipes(self): + recipes = self.recipes + ['+', 'BACK'] + if len(recipes) < 5: + return recipes, 0 + + start = max(0, self.selected_index - 2) + end = min(len(recipes), start + 5) + + recipes = recipes[start:end] + + 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: + r = 10 + offset = 15 + for i in range(0, 90, r // 2): + draw.circle((40 + i, offset + idx * 20), r, fill='black') + if str(recipe) != 'BACK': + draw.text((40, 10 + idx * 20), str(recipe), fill='white') + else: + draw.regular_polygon((40 + 5, 15 + idx * 20, 5), 3, fill='white', rotation=90) + elif str(recipe) == 'BACK': + draw.regular_polygon((40 + 5, 15 + idx * 20, 5), 3, fill='black', rotation=90) + else: + draw.text((40, 10 + idx * 20), str(recipe), fill='black') + + + return im + + def left_press(self): + self.selected_index = (self.selected_index - 1) % (len(self.recipes) + 2) + + def right_press(self): + self.selected_index = (self.selected_index + 1) % (len(self.recipes) + 2) + + def right_long_press(self): + if self.selected_index < len(self.recipes): + # activate selected recipe + print(f"Activating recipe: {self.recipes[self.selected_index]}") + elif self.selected_index == len(self.recipes): + # add new recipe + print("Adding new recipe") + else: + self.selected_index = 0 + self.deactivate_command() + + + 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): + draw.text((x, y-5), 'Edit', fill='black') + + 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): + draw.regular_polygon((x+2, y, 5), 3, fill='black', rotation=270) diff --git a/frontend/views/timer.py b/frontend/views/timer.py index 3b92f89..90c0c5b 100644 --- a/frontend/views/timer.py +++ b/frontend/views/timer.py @@ -73,6 +73,9 @@ class TimerView(View): text_y = self.center[1] - text_height // 2 draw.text((text_x, text_y), time_text, fill='black') + else: + self.reset_timer() + return im # No time to display # Draw progress circle if goal is set try: