Task Zapper Python GUI

👤 Sharing: AI
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import datetime
import threading
import time
import os

class TaskZapperApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Task Zapper")
        self.root.geometry("800x600")
        self.tasks = []
        self.task_timers = {}

        self.style = ttk.Style()
        self.style.theme_use('clam')

        self.setup_ui()

    def setup_ui(self):
        # Task Input Frame
        input_frame = ttk.Frame(self.root, padding=10)
        input_frame.pack(fill=tk.X)

        ttk.Label(input_frame, text="Task Description:").grid(row=0, column=0, sticky=tk.W)
        self.task_entry = ttk.Entry(input_frame, width=50)
        self.task_entry.grid(row=0, column=1, sticky=tk.EW)

        ttk.Label(input_frame, text="Due Date (YYYY-MM-DD):").grid(row=1, column=0, sticky=tk.W)
        self.date_entry = ttk.Entry(input_frame, width=20)
        self.date_entry.grid(row=1, column=1, sticky=tk.W)

        ttk.Label(input_frame, text="Due Time (HH:MM):").grid(row=2, column=0, sticky=tk.W)
        self.time_entry = ttk.Entry(input_frame, width=20)
        self.time_entry.grid(row=2, column=1, sticky=tk.W)

        add_button = ttk.Button(input_frame, text="Add Task", command=self.add_task)
        add_button.grid(row=3, column=1, sticky=tk.E)

        # Task List Frame
        list_frame = ttk.Frame(self.root, padding=10)
        list_frame.pack(fill=tk.BOTH, expand=True)

        self.task_list = tk.Listbox(list_frame, height=15, width=70, bg="#f0f0f0", fg="#333333")
        self.task_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.task_list.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.task_list['yscrollcommand'] = scrollbar.set

        # Task Management Frame
        management_frame = ttk.Frame(self.root, padding=10)
        management_frame.pack(fill=tk.X)

        complete_button = ttk.Button(management_frame, text="Mark Complete", command=self.mark_complete)
        complete_button.pack(side=tk.LEFT, padx=5)

        delete_button = ttk.Button(management_frame, text="Delete Task", command=self.delete_task)
        delete_button.pack(side=tk.LEFT, padx=5)

        self.details_button = ttk.Button(management_frame, text="Details", command=self.show_details)
        self.details_button.pack(side=tk.LEFT, padx=5)

        # Status Bar
        self.status_label = ttk.Label(self.root, text="Ready", anchor=tk.W)
        self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=5)

    def add_task(self):
        description = self.task_entry.get()
        due_date_str = self.date_entry.get()
        due_time_str = self.time_entry.get()

        try:
            due_datetime = datetime.datetime.strptime(f'{due_date_str} {due_time_str}', '%Y-%m-%d %H:%M')
        except ValueError:
            messagebox.showerror("Error", "Invalid date or time format. Use YYYY-MM-DD and HH:MM.")
            return

        if not description:
            messagebox.showerror("Error", "Task description cannot be empty.")
            return

        task = {
            'description': description,
            'due_date': due_datetime,
            'completed': False
        }
        self.tasks.append(task)
        self.update_task_list()
        self.task_entry.delete(0, tk.END)
        self.date_entry.delete(0, tk.END)
        self.time_entry.delete(0, tk.END)
        self.start_timer_thread(task)

    def update_task_list(self):
        self.task_list.delete(0, tk.END)
        for i, task in enumerate(self.tasks):
            status = "[?]" if task['completed'] else "[ ]"
            due_str = task['due_date'].strftime('%Y-%m-%d %H:%M')
            self.task_list.insert(tk.END, f"{status} {task['description']} (Due: {due_str})")

    def mark_complete(self):
        selected_index = self.task_list.curselection()
        if selected_index:
            index = selected_index[0]
            self.tasks[index]['completed'] = True
            self.update_task_list()

    def delete_task(self):
        selected_index = self.task_list.curselection()
        if selected_index:
            index = selected_index[0]
            task = self.tasks.pop(index)
            self.update_task_list()
            # Stop the timer if it exists
            if task in self.task_timers:
                self.task_timers[task]['stop_flag'] = True  # Signal the thread to stop
                del self.task_timers[task]

    def start_timer_thread(self, task):
       # Initialize the stop flag
        self.task_timers[task] = {'stop_flag': False}
        timer_thread = threading.Thread(target=self.task_timer, args=(task,))
        timer_thread.daemon = True  # Allow the main program to exit even if the thread is running
        timer_thread.start()

    def task_timer(self, task):
        while True:
            if self.task_timers[task]['stop_flag']:
                break # Exit if stop flag is set

            now = datetime.datetime.now()
            time_left = task['due_date'] - now

            if time_left.total_seconds() <= 0 and not task['completed']:
                self.show_notification(task['description'])
                break  # Stop the timer after notification

            hours, remainder = divmod(time_left.total_seconds(), 3600)
            minutes, seconds = divmod(remainder, 60)
            time_str = f'{int(hours):02}:{int(minutes):02}:{int(seconds):02}'
            self.status_label.config(text=f"Time left for '{task['description']}': {time_str}")
            time.sleep(1)

    def show_notification(self, task_description):
        # Cross-platform notification
        if os.name == 'nt':  # Windows
            import win10toast
            toaster = win10toast.ToastNotifier()
            toaster.show_toast("Task Due!", f"{task_description} is due now!", duration=10)
        else:  # macOS, Linux (requires plyer)
            try:
                from plyer import notification
                notification.notify(
                    title="Task Due!",
                    message=f"{task_description} is due now!",
                    timeout=10
                )
            except ImportError:
                messagebox.showinfo("Notification", f"Task '{task_description}' is due!")

    def show_details(self):
        detail_window = tk.Toplevel(self.root)
        detail_window.title("Details")
        text_area = scrolledtext.ScrolledText(detail_window, wrap=tk.WORD, width=80, height=20)
        text_area.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

        details_text = """
        Task Zapper: Your Versatile Task Management Solution

        Features:
        - Add tasks with descriptions, due dates, and due times.
        - View a list of all tasks with their status (complete/incomplete).
        - Mark tasks as complete.
        - Delete tasks.
        - Real-time countdown timer for each task displayed in the status bar.
        - Cross-platform notifications when a task is due (Windows, macOS, Linux).

        How to Use:
        1. Enter the task description.
        2. Enter the due date in YYYY-MM-DD format.
        3. Enter the due time in HH:MM format.
        4. Click 'Add Task'.
        5. Select a task from the list to mark it complete or delete it.

        Requirements:
        - Python 3.x
        - tkinter (usually included with Python)
        - plyer (for macOS/Linux notifications): pip install plyer
        - win10toast (for Windows notifications): pip install win10toast

        Potential Improvements:
        - Implement task prioritization.
        - Add recurring task functionality.
        - Integrate with cloud services for task synchronization.
        - Allow custom notification sounds.
        - Implement task categories/tags.
        """
        text_area.insert(tk.INSERT, details_text)
        text_area.config(state=tk.DISABLED)  # Make it read-only

if __name__ == "__main__":
    root = tk.Tk()
    app = TaskZapperApp(root)
    root.mainloop()
👁️ Viewed: 5

Comments