Code Snippets Repository

Home

eye protector

Wednesday, 7 February 2024 -

 #main.py

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