From 0644bdc68f7a685972af0a8bf2d151e9a88fc445 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 27 Dec 2022 21:27:25 +0100 Subject: [PATCH] Refactor to generate the Widgets --- main.py | 86 +++++++++++++++++++++++++++------------ modules/__init__.py | 98 ++++++++++++++++++++++++++++----------------- 2 files changed, 122 insertions(+), 62 deletions(-) diff --git a/main.py b/main.py index 17293e4..aa67d9a 100644 --- a/main.py +++ b/main.py @@ -27,6 +27,7 @@ from datetime import datetime, timedelta import io import locale +import logging import os.path import time @@ -34,39 +35,52 @@ locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8') from PIL import Image, ImageDraw, ImageFont +class WidgetPlacement: + + def __init__(self, module, *args, size, position, **kwargs): + self.module = module + self.args = args + self.size = size + self.position = position + self.kwargs = kwargs + + def main(only_on_coming_evt=False): image = Image.new('1', (480, 800), 255) #image = Image.new('L', (480, 800), 'white') draw = ImageDraw.Draw(image) + shape = [] + import modules config = modules.Config() + # alerts + alerts = [] + # Weather # Current Weather from modules.weather import WeatherJumboCurrentModule - image.paste(WeatherJumboCurrentModule().draw_module(config, 480, 150), (0, 0)) + shape.append(WidgetPlacement(WeatherJumboCurrentModule, size=(480,150), position=(0,0))) # rule - image.paste(modules.RuleModule().draw_module(config, 450, 1), (30, 142)) + shape.append(WidgetPlacement(modules.RuleModule, size=(450, 1), position=(30, 142))) # pluie from modules.weather import WeatherRainModule - image.paste(WeatherRainModule().draw_module(config, 480-int(480/1.6), 94), (0, 155)) + shape.append(WidgetPlacement(WeatherRainModule, size=(480-int(480/1.6), 94), position=(0, 155))) # moon phase from modules.weather import WeatherMoonPhaseModule - moon = WeatherMoonPhaseModule().draw_module(config, 65, 65) - image.paste(moon, (0, 113), moon) + shape.append(WidgetPlacement(WeatherMoonPhaseModule, size=(65, 65), position=(0, 113))) # ical from modules.ical import IcalModule - ical = IcalModule(config) - cal = ical.draw_module(config, 480-int(480/1.6), 255) - image.paste(cal, (0, 250)) + shape.append(WidgetPlacement(IcalModule, config, size=(480-int(480/1.6), 255), position=(0, 250))) occuped_space = 0 + ical = IcalModule(config) evt_coming = ical.non_local_event_coming(config) or ical.local_event_ending(config) if evt_coming: from modules.ratp import RATPNextStopModule @@ -80,25 +94,51 @@ def main(only_on_coming_evt=False): if occuped_space < 250: # weekly weather from modules.weather import WeeklyWeatherModule - image.paste(WeeklyWeatherModule().draw_module(config, int(480/1.6), 275), (480-int(480/1.6), 255 + occuped_space)) + shape.append(WidgetPlacement(WeeklyWeatherModule, size=(int(480/1.6), 275), position=(480-int(480/1.6), 255 + occuped_space))) # RATP weather from modules.ratp import RATPWeatherModule - ratp = RATPWeatherModule().draw_module(config, int(480/1.6), 94) - image.paste(ratp, (480-int(480/1.6), 155)) + shape.append(WidgetPlacement(RATPWeatherModule, size=(int(480/1.6), 94), position=(480-int(480/1.6), 155))) + alerts.append(RATPWeatherModule) # Toolbar from modules.weather import WeatherToolbarModule - image.paste(WeatherToolbarModule().draw_module(config, 480, 50), (0, 530)) + shape.append(WidgetPlacement(WeatherToolbarModule, size=(480, 50), position=(0, 530))) - # alerts - alerts = [] - - alerts += [a for a in RATPWeatherModule().gen_alerts()] from modules.sncf import SNCFWeatherModule - alerts += SNCFWeatherModule().gen_alerts("normandie") + alerts.append({"module": SNCFWeatherModule, "args": "normandie"}) from modules.weather import WeatherAlerts - alerts += WeatherAlerts().gen_alerts() + alerts.append(WeatherAlerts) + + # sunrise/set + from modules.weather import WeatherSunModule + shape.append(WidgetPlacement(WeatherSunModule, size=(int(480/2), 20), position=(0, 780), start_align=5)) + + # Draw shapes + last_height = 0 + last_y = 0 + for s in shape: + try: + x,y = s.position + if y < 0: + y = last_height + last_y + width, height = s.size + if height < 0: + height = 480 - last_height - last_y + + img = s.module(*s.args).draw_module(config, width, height, **s.kwargs) + if img.mode == "RGBA": + image.paste(img, (x,y), img) + else: + image.paste(img, (x,y)) + except BaseException as e: + logging.exception(e) + alerts.append({ + "title": "Impossible de dessiner " + s.module.__name__, + "description": type(e).__name__ + ": " + (e.message if hasattr(e, 'message') else str(e)), + "icon": "wi-earthquake.png", + }) + from modules import AlertsModule mod = AlertsModule(alerts).draw_module(config, 480, 330) @@ -116,10 +156,6 @@ def main(only_on_coming_evt=False): else: image.paste(WeatherTemperatureModule().draw_module(config, 480, 200), (0, 580)) - # sunrise/set - from modules.weather import WeatherSunModule - image.paste(WeatherSunModule().draw_module(config, int(480/2), 20, start_align=5), (0, 780)) - fnt = ImageFont.truetype(config.fnt_R_path, 11) draw.text( (475, 798), @@ -127,17 +163,17 @@ def main(only_on_coming_evt=False): fill="black", anchor="rb", font=fnt ) + logging.info("image generated") try: import epd7in5 - #print("image generated") epd = epd7in5.EPD() epd.init() - #print("initialized") + logging.info("initialized") epd.display(epd.getbuffer(image)) - #print("time to sleep") + logging.info("time to sleep") epd.sleep() except: diff --git a/modules/__init__.py b/modules/__init__.py index 8639f06..d553eb0 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -1,4 +1,5 @@ import os +import logging from tempfile import NamedTemporaryFile from PIL import Image, ImageDraw, ImageFont @@ -90,6 +91,43 @@ class AlertsModule: self.icon_size = 50 self.alerts = alerts + def draw_alert(self, alert, width, image, draw, fnt_R, fnt_B, align, font_size): + if "icon" in alert and alert["icon"] is not None: + if callable(alert["icon"]): + icon_img = alert["icon"](size=int(font_size*self.icon_size/16)) + image.paste(icon_img, (int(self.icon_size / 2 - font_size*self.icon_size/32), align)) + else: + color_img = Image.new('RGB', (self.icon_size, self.icon_size), "#fff") + icon_img = Image.open("icons/" + alert["icon"]).resize((self.icon_size, self.icon_size)) + image.paste(color_img, (0, align - 5), icon_img) + + if "title" in alert: + draw.text( + ((self.icon_size if alert["icon"] is not None else 0) - 5, align), + alert["title"], + fill="white", anchor="lt", font=fnt_B + ) + if "subtitle" in alert and alert["subtitle"]: + draw.text( + ((self.icon_size if alert["icon"] is not None else 0) + (fnt_B.getsize(alert["title"])[0] if "title" in alert else 0), align + 3), + alert["subtitle"], + fill="white", anchor="lt", font=fnt_R + ) + + if "title" in alert: + align += fnt_B.getsize(alert["title"])[1] + + align += display_longtext( + draw, + (self.icon_size - 5, align), + alert["description"], + fill="white", font=fnt_R, + maxwidth=width-self.icon_size-5 + ) + + return align + 7 + + def draw_module(self, config, width, height, font_size=16): image = Image.new('RGBA', (width, height), "#000") draw = ImageDraw.Draw(image) @@ -97,46 +135,32 @@ class AlertsModule: more_alerts = 0 if len(self.alerts) > 0: - fnt_R = ImageFont.truetype(config.fnt_R_path, font_size) - fnt_B = ImageFont.truetype(config.fnt_RB_path, font_size) + fnt_R = ImageFont.truetype(config.fnt_R_path, font_size) + fnt_B = ImageFont.truetype(config.fnt_RB_path, font_size) - align = 9 - for alert in self.alerts: - if alert["icon"] is not None: - if callable(alert["icon"]): - icon_img = alert["icon"](size=int(font_size*self.icon_size/16)) - image.paste(icon_img, (int(self.icon_size / 2 - font_size*self.icon_size/32), align)) - else: - color_img = Image.new('RGB', (self.icon_size, self.icon_size), "#fff") - icon_img = Image.open("icons/" + alert["icon"]).resize((self.icon_size, self.icon_size)) - image.paste(color_img, (0, align), icon_img) + align = 7 + for alert in self.alerts: + args = [] + if isinstance(alert, dict) and "module" in alert: + args = alert["args"] if "args" in alert else [] + alert = alert["module"] - if "title" in alert: - draw.text( - ((self.icon_size if alert["icon"] is not None else 0) - 5, align), - alert["title"], - fill="white", anchor="lt", font=fnt_B - ) - if "subtitle" in alert and alert["subtitle"]: - draw.text( - ((self.icon_size if alert["icon"] is not None else 0) + (fnt_B.getsize(alert["title"])[0] if "title" in alert else 0), align + 3), - alert["subtitle"], - fill="white", anchor="lt", font=fnt_R - ) + if isinstance(alert, type): + try: + for alert in alert(*args).gen_alerts(): + align = self.draw_alert(alert, width, image, draw, fnt_R, fnt_B, align, font_size) + except BaseException as e: + logging.exception(e) + self.alerts.append({ + "title": "Impossible de générer les alertes de " + alert.__name__, + "description": type(e).__name__ + ": " + (e.message if hasattr(e, 'message') else str(e)), + "icon": "wi-earthquake.png", + }) + else: + align = self.draw_alert(alert, width, image, draw, fnt_R, fnt_B, align, font_size) - if "title" in alert: - align += fnt_B.getsize(alert["title"])[1] - - align += display_longtext( - draw, - (self.icon_size - 5, align), - alert["description"], - fill="white", font=fnt_R, - maxwidth=width-self.icon_size-5 - ) - align += 7 - if align > height: - more_alerts += 1 + if align > height: + more_alerts += 1 image = image.crop((0,0,width, min(align, height)))