from app import BreakReminderApp from settings import AppSettings if __name__ == "__main__": # Create an instance of the AppSettings class to hold configuration settings = AppSettings() # Initialize the main application with the settings app = BreakReminderApp(settings) # Start the application app.start()
#setting.py
from threading import Event class AppSettings: def __init__(self): # Initialize work and rest durations in seconds self.work_duration = 20 * 60 # 20 minutes by default self.rest_duration = 20 # 20 seconds by default # Event to signal when settings are updated self.settings_updated = Event() self.rest_period_over = Event() # Add this line def update_settings(self, work_minutes, rest_seconds): """ Update the work and rest durations based on user input. Args: work_minutes (int): The duration of work time in minutes. rest_seconds (int): The duration of rest time in seconds. """ # Update the work and rest durations self.work_duration = work_minutes * 60 # Convert minutes to seconds self.rest_duration = rest_seconds # Signal that the settings have been updated self.settings_updated.set() # Demonstration of using the AppSettings class if __name__ == "__main__": # Create an instance of the AppSettings class settings = AppSettings() # Print the default settings print(f"Default work duration: {settings.work_duration // 60} minutes") print(f"Default rest duration: {settings.rest_duration} seconds") # Update the settings settings.update_settings(25, 30) # Update to 25 minutes of work and 30 seconds of rest # Check if the settings were updated if settings.settings_updated.is_set(): print("Settings have been updated.") print(f"New work duration: {settings.work_duration // 60} minutes") print(f"New rest duration: {settings.rest_duration} seconds")
#ui_components.py
import tkinter as tk from PIL import Image, ImageTk def show_break_window(root, rest_duration, rest_period_over_event): window = tk.Toplevel(root) window.attributes('-fullscreen', True) window.configure(background='black') label = tk.Label(window, text="", font=("Helvetica", 48), fg="white", bg="black") label.pack(expand=True) def countdown(time_left): if time_left > 0: label.config(text=f"Take a break! Time remaining: {time_left} seconds") window.after(1000, countdown, time_left - 1) else: window.destroy() # This will end the rest period and allow the break_cycle to continue rest_period_over_event.set() countdown(rest_duration) def show_settings_window(root, settings, on_close_callback): settings_window = tk.Toplevel(root) settings_window.title("Settings") settings_window.geometry('200x100+250+250') # Adjust position to avoid overlap # Setup the rest of your settings window widgets here... def save_settings(): # Retrieve values from the entry widgets and update the settings work_time = int(work_time_entry.get()) rest_time = int(rest_time_entry.get()) settings.update_settings(work_time, rest_time) settings_window.destroy() # Close the settings window # Create and pack the label and entry for work time tk.Label(settings_window, text="Work time (minutes):").pack() work_time_entry = tk.Entry(settings_window) work_time_entry.pack() work_time_entry.insert(0, str(settings.work_duration // 60)) # Set default value # Create and pack the label and entry for rest time tk.Label(settings_window, text="Rest time (seconds):").pack() rest_time_entry = tk.Entry(settings_window) rest_time_entry.pack() rest_time_entry.insert(0, str(settings.rest_duration)) # Set default value # Create and pack the save button tk.Button(settings_window, text="Save", command=save_settings).pack() # Handle the window close event to call the on_close_callback settings_window.protocol("WM_DELETE_WINDOW", lambda: on_close_close(settings_window, on_close_callback)) return settings_window def on_close_close(window, callback): window.destroy() # Destroy the window callback() # Call the provided callback
#app.py
import tkinter as tk from threading import Thread import time import threading from settings import AppSettings from ui_components import show_break_window, show_settings_window # Import winsound on Windows try: import winsound except ImportError: # For Unix/Linux, we'll use a simple fallback def beep(): print("\a") # This will attempt to make a beep sound in the terminal else: def beep(): winsound.Beep(1000, 300) # 1000 Hz frequency, 300 ms duration class BreakReminderApp: def __init__(self, settings): self.settings = settings self.root = tk.Tk() self.root.withdraw() # Hide the main window self.current_banner_thread = None self.current_banner_window = None self.settings_window_open = False # Flag to track if the settings window is open self.rest_mode = False self.seconds_until_break = self.settings.work_duration def start(self): Thread(target=self.break_cycle, daemon=True).start() self.root.mainloop() def close_current_banner(self): if self.current_banner_window is not None: self.current_banner_window.destroy() self.current_banner_window = None def show_banner(self): self.close_current_banner() # Ensure only one banner window is open at a time banner_window = tk.Toplevel(self.root) self.current_banner_window = banner_window banner_window.geometry('300x100+100+100') banner_window.attributes('-topmost', True) banner_window.overrideredirect(True) banner_label = tk.Label(banner_window, text="", font=("Helvetica", 12)) banner_label.pack(side=tk.TOP, expand=True, fill='both') settings_button = tk.Button(banner_window, text="Settings", command=lambda: self.open_settings_window(banner_window)) settings_button.pack(side=tk.BOTTOM, fill='x') def update_time(): if not self.rest_mode and self.seconds_until_break > 0: minutes, seconds = divmod(self.seconds_until_break, 60) banner_label.config(text=f"{minutes:02d}:{seconds:02d} until break") self.seconds_until_break -= 1 # Blink the banner window and make a beep sound during the last 10 seconds if self.seconds_until_break <= 10: banner_window.attributes('-alpha', 0 if banner_window.attributes('-alpha') == 1 else 1) # Toggle visibility beep() # Make a beep sound with each blink banner_window.after(1000, update_time) else: show_break_window(self.root, self.settings.rest_duration, self.settings.rest_period_over) # Show full-screen break window banner_window.destroy() # Destroy the banner window after countdown update_time() # Start the countdown def open_settings_window(self, parent_window): if self.settings_window_open: try: self.settings_window.lift() except AttributeError: pass # Log this situation if needed return # Callback to reset the flag and clear the window reference when the settings window is closed def on_settings_window_close(): self.settings_window_open = False self.settings_window = None # Proceed to open settings window and set flag self.settings_window_open = True # Create the settings window and assign it to self.settings_window self.settings_window = show_settings_window(self.root, self.settings, on_settings_window_close) def update_and_restart(): self.settings.settings_updated.wait() self.settings.settings_updated.clear() self.root.after(0, self.restart_break_cycle) # Start a new thread to wait for settings update without freezing the GUI settings_update_thread = threading.Thread(target=update_and_restart, daemon=True) settings_update_thread.start() def restart_break_cycle(self): # Ensure this method runs on the main thread if called from another thread if threading.current_thread() is not threading.main_thread(): self.root.after(0, self.restart_break_cycle) return # Reset the seconds_until_break counter with the new work duration self.seconds_until_break = self.settings.work_duration # Close any open banner windows self.close_current_banner() # If there is an ongoing break cycle thread, wait for it to finish if self.current_banner_thread is not None and self.current_banner_thread.is_alive(): self.current_banner_thread.join() # Start a new break cycle thread self.current_banner_thread = threading.Thread(target=self.break_cycle, daemon=True) self.current_banner_thread.start() self.settings_window_open = False # Reset the flag when settings window is closed def close_current_banner(self): if self.current_banner_window is not None: self.current_banner_window.destroy() self.current_banner_window = None def break_cycle(self): while True: self.seconds_until_break = self.settings.work_duration self.show_banner() while self.seconds_until_break > 0 and not self.settings.settings_updated.is_set(): time.sleep(1) self.close_current_banner() if self.settings.settings_updated.is_set(): continue self.rest_mode = True # Reset the event before showing the break window self.settings.rest_period_over.clear() # Pass the rest_period_over event to the show_break_window function show_break_window(self.root, self.settings.rest_duration, self.settings.rest_period_over) # Wait here until the rest period is over self.settings.rest_period_over.wait() self.rest_mode = False