1
0
Fork 0

Configurable color schemes, set Nord as an example

This commit is contained in:
Michał Rudowicz 2024-09-10 17:39:53 +02:00
parent 3b9e279db1
commit 0ecf532464
2 changed files with 32 additions and 23 deletions

View File

@ -31,5 +31,12 @@
"action_message": "Doing GET example" "action_message": "Doing GET example"
} }
], ],
"bg_color": "lime" "color_scheme": {
"menu_bg_color": "#4c566a",
"message_bg_color": "#eceff4",
"message_text_color": "#2e3440",
"btn_bg_color": "#d8dee9",
"btn_hover_color": "#eceff4",
"btn_text_color": "#2e3440"
}
} }

44
menu.py
View File

@ -7,7 +7,7 @@ from enum import Enum, auto
from time import sleep from time import sleep
from threading import Thread from threading import Thread
from queue import Queue, Empty from queue import Queue, Empty
from typing import Tuple, Callable, TypedDict, List from typing import Tuple, Callable, TypedDict, List, Dict
import pygame import pygame
import pygame.freetype import pygame.freetype
@ -38,18 +38,19 @@ class Button:
PRESS_OFFSET = 2 PRESS_OFFSET = 2
HOVER_ANIMATION_STEPS = 5 HOVER_ANIMATION_STEPS = 5
def __init__(self, position: pygame.Rect, text: str, cbk: Callable[[], None]): def __init__(self, position: pygame.Rect, text: str, color_scheme: Dict, cbk: Callable[[], None]):
self.position: pygame.Rect self.position: pygame.Rect
self.set_position(position) self.set_position(position)
self.text = text self.text = text
self.color_scheme = color_scheme
self.cbk = cbk self.cbk = cbk
self.font = pygame.freetype.SysFont(name="Sans", size=20) self.font = pygame.freetype.SysFont(name="Sans", size=20)
text_rect = self.font.get_rect(self.text) text_rect = self.font.get_rect(self.text)
self.text_pos = pygame.Rect((self.position.left + (self.position.width - text_rect.width)/2), self.text_pos = pygame.Rect((self.position.left + (self.position.width - text_rect.width)/2),
(self.position.top + (self.position.height - text_rect.height)/2), (self.position.top + (self.position.height - text_rect.height)/2),
text_rect.width, text_rect.height) text_rect.width, text_rect.height)
self.bg_color = pygame.Color("black") self.bg_color = pygame.Color(self.color_scheme["btn_bg_color"])
self.hover_bg_color = pygame.Color("violetred4") self.hover_bg_color = pygame.Color(self.color_scheme["btn_hover_color"])
self.hover_animation_step = 0 self.hover_animation_step = 0
def get_bg_color(self): def get_bg_color(self):
@ -87,7 +88,7 @@ class Button:
def draw(self, screen: pygame.Surface): def draw(self, screen: pygame.Surface):
pygame.draw.rect(screen, self.get_bg_color(), self.get_position(), 0) pygame.draw.rect(screen, self.get_bg_color(), self.get_position(), 0)
self.font.render_to(screen, self.get_text_pos(), self.text, fgcolor="pink") self.font.render_to(screen, self.get_text_pos(), self.text, fgcolor=self.color_scheme["btn_text_color"])
def pos_is_inside(self, pos: Tuple) -> bool: def pos_is_inside(self, pos: Tuple) -> bool:
return (self.position.left < pos[0] and (self.position.left + self.position.width) > pos[0]) and \ return (self.position.left < pos[0] and (self.position.left + self.position.width) > pos[0]) and \
@ -100,14 +101,14 @@ class Button:
class MessageBoard(IBoard): class MessageBoard(IBoard):
def __init__(self, screen_rect: pygame.Rect, text: str, bg_color: str): def __init__(self, screen_rect: pygame.Rect, text: str, color_scheme: Dict):
self.text = text self.text = text
self.font = pygame.freetype.SysFont(name="Sans", size=30) self.font = pygame.freetype.SysFont(name="Sans", size=30)
text_rect = self.font.get_rect(self.text) text_rect = self.font.get_rect(self.text)
self.text_pos = pygame.Rect((screen_rect.left + (screen_rect.width - text_rect.width)/2), self.text_pos = pygame.Rect((screen_rect.left + (screen_rect.width - text_rect.width)/2),
(screen_rect.top + (screen_rect.height - text_rect.height)/2), (screen_rect.top + (screen_rect.height - text_rect.height)/2),
text_rect.width, text_rect.height) text_rect.width, text_rect.height)
self.bg_color = bg_color self.color_scheme = color_scheme
self.was_drawn_already = False self.was_drawn_already = False
def handle_event(self, _): def handle_event(self, _):
@ -115,23 +116,23 @@ class MessageBoard(IBoard):
def draw(self, screen: pygame.Surface, force_redraw: bool): def draw(self, screen: pygame.Surface, force_redraw: bool):
if not self.was_drawn_already or force_redraw: if not self.was_drawn_already or force_redraw:
screen.fill(self.bg_color) screen.fill(self.color_scheme["message_bg_color"])
self.font.render_to(screen, self.text_pos, self.text, fgcolor="black") self.font.render_to(screen, self.text_pos, self.text, fgcolor=self.color_scheme["message_text_color"])
self.was_drawn_already = True self.was_drawn_already = True
class MenuBoard(IBoard): class MenuBoard(IBoard):
BUTTON_MARGINS = 10 BUTTON_MARGINS = 10
def __init__(self, screen_rect: pygame.Rect, buttons: List[ButtonDef], bg_color: str): def __init__(self, screen_rect: pygame.Rect, buttons: List[ButtonDef], color_scheme: Dict):
self.rows = math.floor(math.sqrt(len(buttons))) self.rows = math.floor(math.sqrt(len(buttons)))
self.cols = math.ceil(math.sqrt(len(buttons))) self.cols = math.ceil(math.sqrt(len(buttons)))
if (self.rows * self.cols) < len(buttons): if (self.rows * self.cols) < len(buttons):
# extra row if buttons don't fit # extra row if buttons don't fit
self.rows += 1 self.rows += 1
button_positions = self.generate_button_positions(screen_rect) button_positions = self.generate_button_positions(screen_rect)
self.buttons = list(map(lambda d: Button(next(button_positions), d['text'], d['cbk']), buttons)) self.buttons = list(map(lambda d: Button(next(button_positions), d['text'], color_scheme, d['cbk']), buttons))
self.bg_color = bg_color self.color_scheme = color_scheme
def generate_button_positions(self, screen_rect: pygame.Rect): def generate_button_positions(self, screen_rect: pygame.Rect):
current_button_row = 0 current_button_row = 0
@ -153,7 +154,7 @@ class MenuBoard(IBoard):
def draw(self, screen: pygame.Surface, force_redraw: bool): def draw(self, screen: pygame.Surface, force_redraw: bool):
if force_redraw or any(map(Button.needs_redrawing, self.buttons)): if force_redraw or any(map(Button.needs_redrawing, self.buttons)):
screen.fill(self.bg_color) screen.fill(self.color_scheme["menu_bg_color"])
for b in self.buttons: for b in self.buttons:
b.draw(screen) b.draw(screen)
@ -161,7 +162,7 @@ class MenuBoard(IBoard):
class App: class App:
FPS = 30 FPS = 30
def __init__(self, urls: List[Action], bg_color: str, fullscreen: bool, screensaver_delay: int): def __init__(self, urls: List[Action], color_scheme: Dict, fullscreen: bool, screensaver_delay: int):
pygame.init() pygame.init()
info = pygame.display.Info() info = pygame.display.Info()
flags = 0 flags = 0
@ -170,10 +171,10 @@ class App:
self.screen = pygame.display.set_mode((info.current_w, info.current_h), flags=flags) self.screen = pygame.display.set_mode((info.current_w, info.current_h), flags=flags)
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
self.running = False self.running = False
self.bg_color = bg_color self.color_scheme = color_scheme
buttons = list(map(lambda u: ButtonDef(text=u['label'], cbk=self.button_press_handler(u)), urls)) buttons = list(map(lambda u: ButtonDef(text=u['label'], cbk=self.button_press_handler(u)), urls))
buttons.append(ButtonDef(text="Exit", cbk=self.quit)) buttons.append(ButtonDef(text="Exit", cbk=self.quit))
self.board: IBoard = MenuBoard(self.get_screen_rect(), buttons, bg_color) self.board: IBoard = MenuBoard(self.get_screen_rect(), buttons, color_scheme)
self.task_q = Queue() self.task_q = Queue()
self.screensaver = ClockScreensaver() self.screensaver = ClockScreensaver()
self.TICKS_UNTIL_SCREENSAVER = screensaver_delay * self.FPS self.TICKS_UNTIL_SCREENSAVER = screensaver_delay * self.FPS
@ -196,7 +197,8 @@ class App:
except Exception as e: except Exception as e:
def f(ex): def f(ex):
def x(): def x():
self.board = MessageBoard(self.get_screen_rect(), f"Exception caught: {ex}", bg_color="red") self.board = MessageBoard(self.get_screen_rect(), f"Exception caught: {ex}",
color_scheme=self.color_scheme)
return x return x
self.task_q.put(f(e)) self.task_q.put(f(e))
sleep(5) sleep(5)
@ -205,7 +207,7 @@ class App:
process_thr = Thread(target=thr_fun) process_thr = Thread(target=thr_fun)
process_thr.start() process_thr.start()
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], bg_color=self.bg_color) self.board = MessageBoard(self.get_screen_rect(), action['action_message'], color_scheme=self.color_scheme)
return impl return impl
def get_handler(self, action: Action) -> Callable[[], None]: def get_handler(self, action: Action) -> Callable[[], None]:
@ -222,7 +224,7 @@ class App:
process_thr = Thread(target=thr_fun) process_thr = Thread(target=thr_fun)
process_thr.start() process_thr.start()
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], bg_color=self.bg_color) self.board = MessageBoard(self.get_screen_rect(), action['action_message'], color_scheme=self.color_scheme)
return impl return impl
def button_press_handler(self, action: Action) -> Callable[[], None]: def button_press_handler(self, action: Action) -> Callable[[], None]:
@ -261,7 +263,7 @@ class App:
pygame.quit() pygame.quit()
def quit(self): def quit(self):
self.board = MessageBoard(self.get_screen_rect(), "Exiting...", bg_color=self.bg_color) self.board = MessageBoard(self.get_screen_rect(), "Exiting...", color_scheme=self.color_scheme)
self.running = False self.running = False
@ -288,6 +290,6 @@ if __name__ == '__main__':
data = json.load(f) data = json.load(f)
url_defs = get_url_defs(data) url_defs = get_url_defs(data)
app = App(url_defs, bg_color=data["bg_color"], fullscreen=not args.no_fullscreen, app = App(url_defs, color_scheme=data["color_scheme"], fullscreen=not args.no_fullscreen,
screensaver_delay=args.screensaver_delay) screensaver_delay=args.screensaver_delay)
app.loop() app.loop()