diff --git a/PizzaTimer/DING.WAV b/PizzaTimer/DING.WAV new file mode 100644 index 0000000..5741f23 Binary files /dev/null and b/PizzaTimer/DING.WAV differ diff --git a/PizzaTimer/main.py b/PizzaTimer/main.py new file mode 100644 index 0000000..13f8fd0 --- /dev/null +++ b/PizzaTimer/main.py @@ -0,0 +1,210 @@ +import tkinter as tk +from tkinter import messagebox +from threading import Thread +import time +import subprocess +import sys +import os +import pystray +from PIL import Image, ImageDraw, ImageFont + + +class TimerApp: + def __init__(self, root): + self.root = root + self.root.title("Pizza Timer") + self.root.geometry("240x350") + self.root.configure(bg="#2e2e2e") # Dark gray background + self.running = False + self.remaining_seconds = 0 + self.original_minutes = 0 + + # Timer display label + self.timer_label = tk.Label(root, text="00:00", font=("Helvetica", 32), bg="#2e2e2e", fg="#ff00ff") + self.timer_label.pack(pady=2) + + # Separator + tk.Frame(root, bg="#cccccc", height=2).pack(fill=tk.X, pady=5) + + # Input for minutes + self.minutes_var = tk.IntVar(value=1) + tk.Label(root, text="Minutes:", bg="#2e2e2e", fg="#ffffff").pack() + self.minutes_entry = tk.Entry(root, textvariable=self.minutes_var, bg="#3a3a3a", fg="#ffffff", insertbackground="white") + self.minutes_entry.pack() + + # Alert message + tk.Label(root, text="Message:", bg="#2e2e2e", fg="#ffffff").pack() + self.message_text = tk.Text(root, height=5, width=25, bg="#3a3a3a", fg="#ffffff", insertbackground="white") + self.message_text.pack(pady=5) + + # Notification method + self.method_var = tk.StringVar(value="beep") + tk.Label(root, text="Notification backend:", bg="#2e2e2e", fg="#ffffff").pack() + self.method_var.set("beep") # Set default + + OPTIONS = ["beep", "aplay", "mplayer"] + self.method_dropdown = tk.OptionMenu(root, self.method_var, *OPTIONS) + self.method_dropdown.config(bg="#2e2e2e", fg="#acacac", activebackground="#444444", activeforeground="#ffffff", highlightthickness=0) + self.method_dropdown["menu"].config(bg="#2e2e2e", fg="#acacac", activebackground="#444444", activeforeground="#ffffff") + self.method_dropdown.pack() + + # Buttons + button_frame = tk.Frame(root, bg="#2e2e2e") + button_frame.pack(pady=10) + start_button = tk.Button( + button_frame, + text="Start", + command=self.start_timer, + bg="#444444", + fg="#ffffff", + activebackground="#666666", + activeforeground="#ffffff", + relief=tk.FLAT + ) + start_button.pack(side=tk.LEFT, padx=5) + + reset_button = tk.Button( + button_frame, + text="Reset", + command=self.reset_timer, + bg="#444444", + fg="#ffffff", + activebackground="#666666", + activeforeground="#ffffff", + relief=tk.FLAT + ) + reset_button.pack(side=tk.LEFT, padx=5) + + # Hide on close + self.root.protocol("WM_DELETE_WINDOW", self.hide_window) + + # Tray icon setup + self.icon = self.create_tray_icon() + + def create_tray_icon(self): + icon_image = self.create_icon_image() + return pystray.Icon("timer", icon_image, "Pizza Timer", menu=pystray.Menu( + pystray.MenuItem("Show", self.show_window), + pystray.MenuItem("Quit", self.quit_app) + )) + + def create_icon_image(self): + # Draw a simple clock face + image = Image.new('RGB', (64, 64), "#2e2e2e") + draw = ImageDraw.Draw(image) + draw.ellipse((8, 8, 56, 56), outline="white", width=4) + + # Coordinates for triangle (pizza slice) + center = (32, 32) + point1 = (48, 0) # Top center + point2 = (64, 16) # Right center + + # Draw filled triangle + draw.polygon([center, point1, point2], fill="orange", outline="yellow") + draw.ellipse((48, 15, 52, 22), fill="red") # mini pepperoni + return image + + def update_tray_icon_image(self, minutes, seconds): + img = Image.new("RGB", (64, 64), "#2e2e2e") + draw = ImageDraw.Draw(img) + draw.line((0, 32, 64, 32), fill="green", width=4) # Separator + + time_m = f"{minutes:02}" + time_s = f"{seconds:02}" + + try: + font = ImageFont.truetype("DejaVuSansMono.ttf", 28) + except: + font = ImageFont.load_default() + + # Center each number in its 64x32 half + def draw_centered_text(text, top_offset): + bbox = font.getbbox(text) + w = bbox[2] - bbox[0] + h = bbox[3] - bbox[1] + x = (64 - w) // 2 + y = top_offset - 6 + (32 - h) // 2 + draw.text((x, y), text, fill="white", font=font) + + draw_centered_text(time_m, 0) # Top half for MM + draw_centered_text(time_s, 32) # Bottom half for SS + + if self.icon.visible: + self.icon.icon = img + + def update_timer_display(self): + mins, secs = divmod(self.remaining_seconds, 60) + time_str = f"{mins:02}:{secs:02}" + self.timer_label.config(text=time_str) + if self.icon.visible: + self.icon.title = f"Pizza Timer - {time_str}" + self.update_tray_icon_image(mins, secs) + + def hide_window(self): + self.root.withdraw() + Thread(target=self.icon.run, daemon=True).start() + + def show_window(self, icon=None, item=None): + self.icon.stop() + self.root.after(0, self.root.deiconify) + + def quit_app(self, icon=None, item=None): + self.icon.stop() + self.root.quit() + + def start_timer(self): + if self.running: + return + + try: + minutes = int(self.minutes_var.get()) + except ValueError: + messagebox.showerror("Error", "Invalid number of minutes.") + return + + if minutes <= 0: + messagebox.showerror("Error", "Minutes must be greater than 0.") + return + + self.original_minutes = minutes + self.remaining_seconds = minutes * 60 + self.running = True + Thread(target=self.countdown, daemon=True).start() + + def reset_timer(self): + self.running = False + self.remaining_seconds = self.original_minutes * 60 + self.update_timer_display() + + def countdown(self): + while self.remaining_seconds > 0 and self.running: + self.root.after(0, self.update_timer_display) + time.sleep(1) + self.remaining_seconds -= 1 + + if self.running and self.remaining_seconds <= 0: + self.running = False + self.root.after(0, self.update_timer_display) + self.root.after(0, self.trigger_alarm) + + def trigger_alarm(self): + message = self.message_text.get("1.0", tk.END).strip() + messagebox.showinfo("TIMER ALERT", message or "Go grab your Pizza!") + + method = self.method_var.get() + if method == "beep": + subprocess.Popen(["beep", "-f", "2000", "-l", "2000"]) + elif method == "aplay": + subprocess.Popen(["aplay", "DING.WAV"]) + elif method == "mplayer": + subprocess.Popen(["mplayer", "DING.WAV"]) + + +def main(): + root = tk.Tk() + app = TimerApp(root) + root.mainloop() + + +if __name__ == "__main__": + main()