From 1b4d4ad815ae0a0641a4e97616cd03a44569c60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9sir=C3=A9=20Werner=20Menrath?= Date: Sun, 10 Aug 2025 21:48:45 +0200 Subject: [PATCH] fix ui size --- animated_icon.py | 970 +++++++++++++++++++++++++++++++++++++++++++++++ wirepy.py | 1 + wp_app_config.py | 2 +- 3 files changed, 972 insertions(+), 1 deletion(-) create mode 100644 animated_icon.py diff --git a/animated_icon.py b/animated_icon.py new file mode 100644 index 0000000..8d34e31 --- /dev/null +++ b/animated_icon.py @@ -0,0 +1,970 @@ +""" +A Tkinter widget for displaying animated icons. + +This module provides the AnimatedIcon class, a custom Tkinter Canvas widget +that can display various types of animations. It supports both native Tkinter +drawing and Pillow (PIL) for anti-aliased graphics if available. +""" +import tkinter as tk +from math import sin, cos, pi +from typing import Tuple, Optional + + +try: + from PIL import Image, ImageDraw, ImageTk + PIL_AVAILABLE = True +except ImportError: + PIL_AVAILABLE = False + + +def _hex_to_rgb(hex_color: str) -> Tuple[int, int, int]: + """Converts a hex color string to an RGB tuple.""" + hex_color = hex_color.lstrip('#') + return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) + + +class AnimatedIcon(tk.Canvas): + """A custom Tkinter Canvas widget for displaying animations.""" + + def __init__(self, master: tk.Misc, width: int = 20, height: int = 20, animation_type: str = "counter_arc", color: str = "#2a6fde", highlight_color: str = "#5195ff", use_pillow: bool = False, bg: Optional[str] = None) -> None: + """ + Initializes the AnimatedIcon widget. + + Args: + master: The parent widget. + width (int): The width of the icon. + height (int): The height of the icon. + animation_type (str): The type of animation to display. + Options: "counter_arc", "double_arc", "line", "blink". + color (str): The primary color of the icon. + highlight_color (str): The highlight color of the icon. + use_pillow (bool): Whether to use Pillow for drawing if available. + bg (str): The background color of the canvas. + """ + if bg is None: + try: + bg = master.cget("background") + except tk.TclError: + bg = "#f0f0f0" # Fallback color + super().__init__(master, width=width, height=height, bg=bg, highlightthickness=0) + + self.width = width + self.height = height + self.animation_type = animation_type + self.color = color + self.highlight_color = highlight_color + self.use_pillow = use_pillow and PIL_AVAILABLE + self.running = False + self.angle = 0 + self.pulse_animation = False + + self.color_rgb = _hex_to_rgb(self.color) + self.highlight_color_rgb = _hex_to_rgb(self.highlight_color) + + if self.use_pillow: + self.image = Image.new( + "RGBA", (width * 4, height * 4), (0, 0, 0, 0)) + self.draw = ImageDraw.Draw(self.image) + self.photo_image = None + + def _draw_frame(self) -> None: + """Draws a single frame of the animation.""" + if self.use_pillow: + self._draw_pillow_frame() + else: + self._draw_canvas_frame() + + def _draw_canvas_frame(self) -> None: + """Draws a frame using native Tkinter canvas methods.""" + self.delete("all") + if self.pulse_animation: + self._draw_canvas_pulse() + elif self.animation_type == "line": + self._draw_canvas_line() + elif self.animation_type == "double_arc": + self._draw_canvas_double_arc() + elif self.animation_type == "counter_arc": + self._draw_canvas_counter_arc() + elif self.animation_type == "blink": + self._draw_canvas_blink() + + def _draw_canvas_pulse(self) -> None: + """Draws the pulse animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + pulse_color = f"#{r:02x}{g:02x}{b:02x}" + + if self.animation_type == "line": + for i in range(8): + angle = i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + self.create_line(start_x, start_y, end_x, + end_y, fill=pulse_color, width=2) + elif self.animation_type == "double_arc": + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.create_arc(bbox, start=0, extent=359.9, + style=tk.ARC, outline=pulse_color, width=2) + elif self.animation_type == "counter_arc": + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.create_arc(bbox_outer, start=0, extent=359.9, + style=tk.ARC, outline=pulse_color, width=2) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.create_arc(bbox_inner, start=0, extent=359.9, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_line(self) -> None: + """Draws the line animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + for i in range(8): + angle = self.angle + i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + alpha = (cos(self.angle * 2 + i * (pi / 4)) + 1) / 2 + + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + color = f"#{r:02x}{g:02x}{b:02x}" + + self.create_line(start_x, start_y, end_x, + end_y, fill=color, width=2) + + def _draw_canvas_double_arc(self) -> None: + """Draws the double arc animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + + start_angle1 = -self.angle * 180 / pi + extent1 = 120 + 60 * sin(-self.angle) + self.create_arc(bbox, start=start_angle1, extent=extent1, + style=tk.ARC, outline=self.highlight_color, width=2) + + start_angle2 = (-self.angle + pi) * 180 / pi + extent2 = 120 + 60 * sin(-self.angle + pi / 2) + self.create_arc(bbox, start=start_angle2, extent=extent2, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_counter_arc(self) -> None: + """Draws the counter arc animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + start_angle1 = -self.angle * 180 / pi + self.create_arc(bbox_outer, start=start_angle1, extent=150, + style=tk.ARC, outline=self.highlight_color, width=2) + + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + start_angle2 = self.angle * 180 / pi + 60 + self.create_arc(bbox_inner, start=start_angle2, extent=150, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_blink(self) -> None: + """Draws the blink animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + blink_color = f"#{r:02x}{g:02x}{b:02x}" + self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y + + radius, start=0, extent=359.9, style=tk.ARC, outline=blink_color, width=4) + + def _draw_pillow_frame(self) -> None: + """Draws a frame using Pillow for anti-aliased graphics.""" + self.draw.rectangle( + [0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0)) + if self.pulse_animation: + self._draw_pillow_pulse() + elif self.animation_type == "line": + self._draw_pillow_line() + elif self.animation_type == "double_arc": + self._draw_pillow_double_arc() + elif self.animation_type == "counter_arc": + self._draw_pillow_counter_arc() + elif self.animation_type == "blink": + self._draw_pillow_blink() + + resized_image = self.image.resize( + (self.width, self.height), Image.Resampling.LANCZOS) + self.photo_image = ImageTk.PhotoImage(resized_image) + self.delete("all") + self.create_image(0, 0, anchor="nw", image=self.photo_image) + + def _draw_pillow_pulse(self) -> None: + """Draws the pulse animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + pulse_color = (r, g, b) + + if self.animation_type == "line": + for i in range(12): + angle = i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=pulse_color, width=6, joint="curve") + elif self.animation_type == "double_arc": + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.draw.arc(bbox, start=0, end=360, fill=pulse_color, width=5) + elif self.animation_type == "counter_arc": + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.draw.arc(bbox_outer, start=0, end=360, + fill=pulse_color, width=7) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.draw.arc(bbox_inner, start=0, end=360, + fill=self.color_rgb, width=7) + + def _draw_pillow_line(self) -> None: + """Draws the line animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + for i in range(12): + angle = self.angle + i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + alpha = (cos(self.angle * 2.5 + i * (pi / 6)) + 1) / 2 + + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + color = (r, g, b) + + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=color, width=6, joint="curve") + + def _draw_pillow_double_arc(self) -> None: + """Draws the double arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + + start_angle1 = self.angle * 180 / pi + extent1 = 120 + 60 * sin(self.angle) + self.draw.arc(bbox, start=start_angle1, end=start_angle1 + + extent1, fill=self.highlight_color_rgb, width=5) + + start_angle2 = (self.angle + pi) * 180 / pi + extent2 = 120 + 60 * sin(self.angle + pi / 2) + self.draw.arc(bbox, start=start_angle2, end=start_angle2 + + extent2, fill=self.color_rgb, width=5) + + def _draw_pillow_counter_arc(self) -> None: + """Draws the counter arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + start_angle1 = self.angle * 180 / pi + self.draw.arc(bbox_outer, start=start_angle1, end=start_angle1 + + 150, fill=self.highlight_color_rgb, width=7) + + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + start_angle2 = -self.angle * 180 / pi + 60 + self.draw.arc(bbox_inner, start=start_angle2, + end=start_angle2 + 150, fill=self.color_rgb, width=7) + + def _draw_pillow_blink(self) -> None: + """Draws the blink animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + blink_color = (r, g, b) + self.draw.arc((center_x - radius, center_y - radius, center_x + radius, + center_y + radius), start=0, end=360, fill=blink_color, width=10) + + def _draw_stopped_frame(self) -> None: + """Draws the icon in its stopped (static) state.""" + self.delete("all") + if self.use_pillow: + self._draw_pillow_stopped_frame() + else: + self._draw_canvas_stopped_frame() + + def _draw_canvas_stopped_frame(self) -> None: + """Draws the stopped state using canvas methods.""" + if self.animation_type == "line": + self._draw_canvas_line_stopped() + elif self.animation_type == "double_arc": + self._draw_canvas_double_arc_stopped() + elif self.animation_type == "counter_arc": + self._draw_canvas_counter_arc_stopped() + elif self.animation_type == "blink": + self._draw_canvas_blink_stopped() + + def _draw_canvas_line_stopped(self) -> None: + """Draws the stopped state for the line animation.""" + center_x, center_y = self.width / 2, self.height / 2 + for i in range(8): + angle = i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + self.create_line(start_x, start_y, end_x, end_y, + fill=self.highlight_color, width=2) + + def _draw_canvas_double_arc_stopped(self) -> None: + """Draws the stopped state for the double arc animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.create_arc(bbox, start=0, extent=359.9, style=tk.ARC, + outline=self.highlight_color, width=2) + + def _draw_canvas_counter_arc_stopped(self) -> None: + """Draws the stopped state for the counter arc animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.create_arc(bbox_outer, start=0, extent=359.9, + style=tk.ARC, outline=self.highlight_color, width=2) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.create_arc(bbox_inner, start=0, extent=359.9, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_blink_stopped(self) -> None: + """Draws the stopped state for the blink animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y + + radius, start=0, extent=359.9, style=tk.ARC, outline=self.highlight_color, width=4) + + def _draw_pillow_stopped_frame(self) -> None: + """Draws the stopped state using Pillow.""" + self.draw.rectangle( + [0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0)) + if self.animation_type == "line": + self._draw_pillow_line_stopped() + elif self.animation_type == "double_arc": + self._draw_pillow_double_arc_stopped() + elif self.animation_type == "counter_arc": + self._draw_pillow_counter_arc_stopped() + elif self.animation_type == "blink": + self._draw_pillow_blink_stopped() + + resized_image = self.image.resize( + (self.width, self.height), Image.Resampling.LANCZOS) + self.photo_image = ImageTk.PhotoImage(resized_image) + self.create_image(0, 0, anchor="nw", image=self.photo_image) + + def _draw_pillow_line_stopped(self) -> None: + """Draws the stopped state for the line animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + for i in range(12): + angle = i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=self.highlight_color_rgb, width=6, joint="curve") + + def _draw_pillow_double_arc_stopped(self) -> None: + """Draws the stopped state for the double arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.draw.arc(bbox, start=0, end=360, + fill=self.highlight_color_rgb, width=5) + + def _draw_pillow_counter_arc_stopped(self) -> None: + """Draws the stopped state for the counter arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.draw.arc(bbox_outer, start=0, end=360, + fill=self.highlight_color_rgb, width=7) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.draw.arc(bbox_inner, start=0, end=360, + fill=self.color_rgb, width=7) + + def _draw_pillow_blink_stopped(self) -> None: + """Draws the stopped state for the blink animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + self.draw.arc((center_x - radius, center_y - radius, center_x + radius, + center_y + radius), start=0, end=360, fill=self.highlight_color_rgb, width=10) + + def _animate(self) -> None: + """The main animation loop.""" + if self.running: + self.angle += 0.1 + if self.angle > 2 * pi: + self.angle -= 2 * pi + self._draw_frame() + self.after(30, self._animate) + + def start(self, pulse: bool = False) -> None: + """ + Starts the animation. + + Args: + pulse (bool): If True, plays a pulsing animation instead of the main one. + """ + if not self.running: + self.pulse_animation = pulse + self.running = True + self._animate() + + def stop(self) -> None: + """Stops the animation and shows the static 'stopped' frame.""" + self.running = False + self.pulse_animation = False + self._draw_stopped_frame() + + def hide(self) -> None: + """Stops the animation and clears the canvas.""" + self.running = False + self.pulse_animation = False + self.delete("all") + + def show_full_circle(self) -> None: + """Shows the static 'stopped' frame without starting the animation.""" + if not self.running: + self._draw_stopped_frame() + + +try: + from PIL import Image, ImageDraw, ImageTk + PIL_AVAILABLE = True +except ImportError: + PIL_AVAILABLE = False + + +def _hex_to_rgb(hex_color): + """Converts a hex color string to an RGB tuple.""" + hex_color = hex_color.lstrip('#') + return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) + + +class AnimatedIcon(tk.Canvas): + """A custom Tkinter Canvas widget for displaying animations.""" + + def __init__(self, master, width=20, height=20, animation_type="counter_arc", color="#2a6fde", highlight_color="#5195ff", use_pillow=False, bg=None): + """ + Initializes the AnimatedIcon widget. + + Args: + master: The parent widget. + width (int): The width of the icon. + height (int): The height of the icon. + animation_type (str): The type of animation to display. + Options: "counter_arc", "double_arc", "line", "blink". + color (str): The primary color of the icon. + highlight_color (str): The highlight color of the icon. + use_pillow (bool): Whether to use Pillow for drawing if available. + bg (str): The background color of the canvas. + """ + if bg is None: + try: + bg = master.cget("background") + except tk.TclError: + bg = "#f0f0f0" # Fallback color + super().__init__(master, width=width, height=height, bg=bg, highlightthickness=0) + + self.width = width + self.height = height + self.animation_type = animation_type + self.color = color + self.highlight_color = highlight_color + self.use_pillow = use_pillow and PIL_AVAILABLE + self.running = False + self.angle = 0 + self.pulse_animation = False + + self.color_rgb = _hex_to_rgb(self.color) + self.highlight_color_rgb = _hex_to_rgb(self.highlight_color) + + if self.use_pillow: + self.image = Image.new( + "RGBA", (width * 4, height * 4), (0, 0, 0, 0)) + self.draw = ImageDraw.Draw(self.image) + self.photo_image = None + + def _draw_frame(self): + """Draws a single frame of the animation.""" + if self.use_pillow: + self._draw_pillow_frame() + else: + self._draw_canvas_frame() + + def _draw_canvas_frame(self): + """Draws a frame using native Tkinter canvas methods.""" + self.delete("all") + if self.pulse_animation: + self._draw_canvas_pulse() + elif self.animation_type == "line": + self._draw_canvas_line() + elif self.animation_type == "double_arc": + self._draw_canvas_double_arc() + elif self.animation_type == "counter_arc": + self._draw_canvas_counter_arc() + elif self.animation_type == "blink": + self._draw_canvas_blink() + + def _draw_canvas_pulse(self): + """Draws the pulse animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + pulse_color = f"#{r:02x}{g:02x}{b:02x}" + + if self.animation_type == "line": + for i in range(8): + angle = i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + self.create_line(start_x, start_y, end_x, + end_y, fill=pulse_color, width=2) + elif self.animation_type == "double_arc": + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.create_arc(bbox, start=0, extent=359.9, + style=tk.ARC, outline=pulse_color, width=2) + elif self.animation_type == "counter_arc": + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.create_arc(bbox_outer, start=0, extent=359.9, + style=tk.ARC, outline=pulse_color, width=2) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.create_arc(bbox_inner, start=0, extent=359.9, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_line(self): + """Draws the line animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + for i in range(8): + angle = self.angle + i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + alpha = (cos(self.angle * 2 + i * (pi / 4)) + 1) / 2 + + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + color = f"#{r:02x}{g:02x}{b:02x}" + + self.create_line(start_x, start_y, end_x, + end_y, fill=color, width=2) + + def _draw_canvas_double_arc(self): + """Draws the double arc animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + + start_angle1 = -self.angle * 180 / pi + extent1 = 120 + 60 * sin(-self.angle) + self.create_arc(bbox, start=start_angle1, extent=extent1, + style=tk.ARC, outline=self.highlight_color, width=2) + + start_angle2 = (-self.angle + pi) * 180 / pi + extent2 = 120 + 60 * sin(-self.angle + pi / 2) + self.create_arc(bbox, start=start_angle2, extent=extent2, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_counter_arc(self): + """Draws the counter arc animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + start_angle1 = -self.angle * 180 / pi + self.create_arc(bbox_outer, start=start_angle1, extent=150, + style=tk.ARC, outline=self.highlight_color, width=2) + + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + start_angle2 = self.angle * 180 / pi + 60 + self.create_arc(bbox_inner, start=start_angle2, extent=150, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_blink(self): + """Draws the blink animation using canvas methods.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + blink_color = f"#{r:02x}{g:02x}{b:02x}" + self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y + + radius, start=0, extent=359.9, style=tk.ARC, outline=blink_color, width=4) + + def _draw_pillow_frame(self): + """Draws a frame using Pillow for anti-aliased graphics.""" + self.draw.rectangle( + [0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0)) + if self.pulse_animation: + self._draw_pillow_pulse() + elif self.animation_type == "line": + self._draw_pillow_line() + elif self.animation_type == "double_arc": + self._draw_pillow_double_arc() + elif self.animation_type == "counter_arc": + self._draw_pillow_counter_arc() + elif self.animation_type == "blink": + self._draw_pillow_blink() + + resized_image = self.image.resize( + (self.width, self.height), Image.Resampling.LANCZOS) + self.photo_image = ImageTk.PhotoImage(resized_image) + self.delete("all") + self.create_image(0, 0, anchor="nw", image=self.photo_image) + + def _draw_pillow_pulse(self): + """Draws the pulse animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + pulse_color = (r, g, b) + + if self.animation_type == "line": + for i in range(12): + angle = i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=pulse_color, width=6, joint="curve") + elif self.animation_type == "double_arc": + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.draw.arc(bbox, start=0, end=360, fill=pulse_color, width=5) + elif self.animation_type == "counter_arc": + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.draw.arc(bbox_outer, start=0, end=360, + fill=pulse_color, width=7) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.draw.arc(bbox_inner, start=0, end=360, + fill=self.color_rgb, width=7) + + def _draw_pillow_line(self): + """Draws the line animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + for i in range(12): + angle = self.angle + i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + alpha = (cos(self.angle * 2.5 + i * (pi / 6)) + 1) / 2 + + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + color = (r, g, b) + + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=color, width=6, joint="curve") + + def _draw_pillow_double_arc(self): + """Draws the double arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + + start_angle1 = self.angle * 180 / pi + extent1 = 120 + 60 * sin(self.angle) + self.draw.arc(bbox, start=start_angle1, end=start_angle1 + + extent1, fill=self.highlight_color_rgb, width=5) + + start_angle2 = (self.angle + pi) * 180 / pi + extent2 = 120 + 60 * sin(self.angle + pi / 2) + self.draw.arc(bbox, start=start_angle2, end=start_angle2 + + extent2, fill=self.color_rgb, width=5) + + def _draw_pillow_counter_arc(self): + """Draws the counter arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + start_angle1 = self.angle * 180 / pi + self.draw.arc(bbox_outer, start=start_angle1, end=start_angle1 + + 150, fill=self.highlight_color_rgb, width=7) + + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + start_angle2 = -self.angle * 180 / pi + 60 + self.draw.arc(bbox_inner, start=start_angle2, + end=start_angle2 + 150, fill=self.color_rgb, width=7) + + def _draw_pillow_blink(self): + """Draws the blink animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed + r = int( + alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0]) + g = int( + alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1]) + b = int( + alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2]) + blink_color = (r, g, b) + self.draw.arc((center_x - radius, center_y - radius, center_x + radius, + center_y + radius), start=0, end=360, fill=blink_color, width=10) + + def _draw_stopped_frame(self): + """Draws the icon in its stopped (static) state.""" + self.delete("all") + if self.use_pillow: + self._draw_pillow_stopped_frame() + else: + self._draw_canvas_stopped_frame() + + def _draw_canvas_stopped_frame(self): + """Draws the stopped state using canvas methods.""" + if self.animation_type == "line": + self._draw_canvas_line_stopped() + elif self.animation_type == "double_arc": + self._draw_canvas_double_arc_stopped() + elif self.animation_type == "counter_arc": + self._draw_canvas_counter_arc_stopped() + elif self.animation_type == "blink": + self._draw_canvas_blink_stopped() + + def _draw_canvas_line_stopped(self): + """Draws the stopped state for the line animation.""" + center_x, center_y = self.width / 2, self.height / 2 + for i in range(8): + angle = i * (pi / 4) + start_x = center_x + cos(angle) * (self.width * 0.2) + start_y = center_y + sin(angle) * (self.height * 0.2) + end_x = center_x + cos(angle) * (self.width * 0.4) + end_y = center_y + sin(angle) * (self.height * 0.4) + self.create_line(start_x, start_y, end_x, end_y, + fill=self.highlight_color, width=2) + + def _draw_canvas_double_arc_stopped(self): + """Draws the stopped state for the double arc animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.create_arc(bbox, start=0, extent=359.9, style=tk.ARC, + outline=self.highlight_color, width=2) + + def _draw_canvas_counter_arc_stopped(self): + """Draws the stopped state for the counter arc animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.create_arc(bbox_outer, start=0, extent=359.9, + style=tk.ARC, outline=self.highlight_color, width=2) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.create_arc(bbox_inner, start=0, extent=359.9, + style=tk.ARC, outline=self.color, width=2) + + def _draw_canvas_blink_stopped(self): + """Draws the stopped state for the blink animation.""" + center_x, center_y = self.width / 2, self.height / 2 + radius = min(center_x, center_y) * 0.8 + self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y + + radius, start=0, extent=359.9, style=tk.ARC, outline=self.highlight_color, width=4) + + def _draw_pillow_stopped_frame(self): + """Draws the stopped state using Pillow.""" + self.draw.rectangle( + [0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0)) + if self.animation_type == "line": + self._draw_pillow_line_stopped() + elif self.animation_type == "double_arc": + self._draw_pillow_double_arc_stopped() + elif self.animation_type == "counter_arc": + self._draw_pillow_counter_arc_stopped() + elif self.animation_type == "blink": + self._draw_pillow_blink_stopped() + + resized_image = self.image.resize( + (self.width, self.height), Image.Resampling.LANCZOS) + self.photo_image = ImageTk.PhotoImage(resized_image) + self.create_image(0, 0, anchor="nw", image=self.photo_image) + + def _draw_pillow_line_stopped(self): + """Draws the stopped state for the line animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + for i in range(12): + angle = i * (pi / 6) + start_x = center_x + cos(angle) * (self.width * 0.8) + start_y = center_y + sin(angle) * (self.height * 0.8) + end_x = center_x + cos(angle) * (self.width * 1.6) + end_y = center_y + sin(angle) * (self.height * 1.6) + self.draw.line([(start_x, start_y), (end_x, end_y)], + fill=self.highlight_color_rgb, width=6, joint="curve") + + def _draw_pillow_double_arc_stopped(self): + """Draws the stopped state for the double arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + bbox = (center_x - radius, center_y - radius, + center_x + radius, center_y + radius) + self.draw.arc(bbox, start=0, end=360, + fill=self.highlight_color_rgb, width=5) + + def _draw_pillow_counter_arc_stopped(self): + """Draws the stopped state for the counter arc animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius_outer = min(center_x, center_y) * 0.8 + bbox_outer = (center_x - radius_outer, center_y - radius_outer, + center_x + radius_outer, center_y + radius_outer) + self.draw.arc(bbox_outer, start=0, end=360, + fill=self.highlight_color_rgb, width=7) + radius_inner = min(center_x, center_y) * 0.6 + bbox_inner = (center_x - radius_inner, center_y - radius_inner, + center_x + radius_inner, center_y + radius_inner) + self.draw.arc(bbox_inner, start=0, end=360, + fill=self.color_rgb, width=7) + + def _draw_pillow_blink_stopped(self): + """Draws the stopped state for the blink animation using Pillow.""" + center_x, center_y = self.width * 2, self.height * 2 + radius = min(center_x, center_y) * 0.8 + self.draw.arc((center_x - radius, center_y - radius, center_x + radius, + center_y + radius), start=0, end=360, fill=self.highlight_color_rgb, width=10) + + def _animate(self): + """The main animation loop.""" + if self.running: + self.angle += 0.1 + if self.angle > 2 * pi: + self.angle -= 2 * pi + self._draw_frame() + self.after(30, self._animate) + + def start(self, pulse=False): + """ + Starts the animation. + + Args: + pulse (bool): If True, plays a pulsing animation instead of the main one. + """ + if not self.running: + self.pulse_animation = pulse + self.running = True + self._animate() + + def stop(self): + """Stops the animation and shows the static 'stopped' frame.""" + self.running = False + self.pulse_animation = False + self._draw_stopped_frame() + + def hide(self): + """Stops the animation and clears the canvas.""" + self.running = False + self.pulse_animation = False + self.delete("all") + + def show_full_circle(self): + """Shows the static 'stopped' frame without starting the animation.""" + if not self.running: + self._draw_stopped_frame() diff --git a/wirepy.py b/wirepy.py index 588bfb0..82bf222 100755 --- a/wirepy.py +++ b/wirepy.py @@ -37,6 +37,7 @@ class Wirepy(tk.Tk): self.y_height = AppConfig.UI_CONFIG["window_size"][1] # Set the window size + print(f"Setting window size to: {self.x_width}x{self.y_height}") self.geometry(f"{self.x_width}x{self.y_height}") self.resizable( AppConfig.UI_CONFIG["resizable_window"][0], diff --git a/wp_app_config.py b/wp_app_config.py index 6f0c4c6..2ae1f98 100755 --- a/wp_app_config.py +++ b/wp_app_config.py @@ -55,7 +55,7 @@ class AppConfig: UI_CONFIG: Dict[str, Any] = { "window_title": "WirePy", - "window_size": (590, 460), + "window_size": (590, 440), "font_family": "Ubuntu", "font_size": 11, "resizable_window": (True, True),