mirror of
https://github.com/iAmInActions/random-scripts.git
synced 2025-08-24 14:40:06 +00:00
Add Pizza Timer
make sure to `pip3 install Pillow pystray`
This commit is contained in:
parent
eb760daa89
commit
cce63a4169
BIN
PizzaTimer/DING.WAV
Normal file
BIN
PizzaTimer/DING.WAV
Normal file
Binary file not shown.
210
PizzaTimer/main.py
Normal file
210
PizzaTimer/main.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user