Add Pizza Timer

make sure to `pip3 install Pillow pystray`
This commit is contained in:
iAmInAction 2025-06-04 13:15:49 +02:00 committed by GitHub
parent eb760daa89
commit cce63a4169
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 210 additions and 0 deletions

BIN
PizzaTimer/DING.WAV Normal file

Binary file not shown.

210
PizzaTimer/main.py Normal file
View File

@ -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()