카테고리 없음

todo

1231. 2025. 4. 25. 08:48

import tkinter as tk
from tkinter import messagebox, ttk
import json
import os
from datetime import datetime

# 바탕화면 경로에 tasks.json 저장
def get_desktop_path():
    try:
        return os.path.join(os.environ['USERPROFILE'], 'Desktop')
    except Exception:
        return os.path.join(os.path.expanduser("~"), 'Desktop')

TASKS_FILE = os.path.join(get_desktop_path(), "tasks.json")
ENGLISH_WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

class ToDoApp:
    def __init__(self, root):
        self.root = root
        self.root.title("To-Do List")
        self.root.geometry("460x240")
        self.root.resizable(False, False)
        self.root.configure(bg="#f7f7f7")

        self.tasks = []

        self.setup_ui()
        self.root.after(300, lambda: self.task_entry.focus_force())
        self.load_tasks()
        self.root.after(3000, self.schedule_reminder)

    def setup_ui(self):
        entry_frame = tk.Frame(self.root, bg="#f7f7f7")
        entry_frame.pack(pady=10)

        self.task_entry = tk.Entry(entry_frame, font=("Helvetica", 12), width=30)
        self.task_entry.pack(side=tk.LEFT, padx=(0, 8))
        self.task_entry.bind('<Return>', lambda event: self.add_task())
        self.task_entry.bind('<Button-1>', lambda e: self.task_entry.focus_force())

        style = ttk.Style()
        style.configure("Rounded.TButton", font=("Helvetica", 10, "bold"), padding=4,
                        foreground="black", background="#4CAF50")
        style.map("Rounded.TButton",
                  background=[('!active', '#4CAF50'), ('active', '#45A049')],
                  foreground=[('!active', 'black'), ('active', 'black')])

        add_button = ttk.Button(entry_frame, text="Add", style="Rounded.TButton", command=self.add_task)
        add_button.pack(side=tk.LEFT, padx=4)

        tree_frame = tk.Frame(self.root, highlightbackground="black", highlightthickness=1, bd=0)
        tree_frame.pack(fill='both', expand=True, padx=10, pady=(0, 10))

        self.tree = ttk.Treeview(tree_frame, columns=('status', 'task', 'created'), show='headings', height=10)
        self.tree.heading('status', text='✓')
        self.tree.heading('task', text='Task')
        self.tree.heading('created', text='Added')
        self.tree.column('status', width=30, anchor='center')
        self.tree.column('task', width=260, anchor='w')
        self.tree.column('created', width=150, anchor='center', stretch=True)
        self.tree.pack(fill='both', expand=True)

        style.configure("Treeview", font=("Helvetica", 10), rowheight=20)
        style.configure("Treeview.Heading", font=("Helvetica", 10, "bold"))
        self.tree.tag_configure('done', foreground='gray')

        self.tree.bind('<Key>', self.handle_keypress)

    def get_english_time(self):
        now = datetime.now()
        weekday = ENGLISH_WEEKDAYS[now.weekday()]
        return now.strftime(f"%m/%d ({weekday}) %H:%M")

    def add_task(self):
        task_text = self.task_entry.get().strip()
        if not task_text:
            messagebox.showwarning("입력 오류", "할 일을 입력하세요.")
            return

        created_time = self.get_english_time()
        self.tasks.append({
            "task": task_text,
            "done": False,
            "created": created_time
        })
        self.task_entry.delete(0, tk.END)
        self.update_task_list()
        self.save_tasks()
        self.task_entry.focus_force()

    def delete_selected_task(self):
        selected = self.tree.selection()
        if not selected:
            return

        index = self.tree.index(selected[0])
        if 0 <= index < len(self.tasks):
            del self.tasks[index]
            self.update_task_list()
            self.save_tasks()

            new_count = len(self.tasks)
            if new_count == 0:
                return
            new_index = min(index, new_count - 1)
            self.tree.selection_set(self.tree.get_children()[new_index])
            self.tree.focus(self.tree.get_children()[new_index])

    def toggle_selected_task(self):
        selected = self.tree.selection()
        if not selected:
            return
        index = self.tree.index(selected[0])
        if 0 <= index < len(self.tasks):
            self.tasks[index]["done"] = not self.tasks[index]["done"]
            self.update_task_list()
            self.save_tasks()
            self.tree.selection_set(self.tree.get_children()[index])
            self.tree.focus(self.tree.get_children()[index])

    def handle_keypress(self, event):
        if event.keysym == 'space':
            self.toggle_selected_task()
        elif event.keysym == 'Delete':
            self.delete_selected_task()

    def update_task_list(self):
        self.tree.delete(*self.tree.get_children())
        for i, task in enumerate(self.tasks):
            status = "✓" if task["done"] else ""
            tags = ['done'] if task["done"] else []
            self.tree.insert('', 'end', values=(status, task["task"], task["created"]), tags=tags)

    def load_tasks(self):
        if os.path.exists(TASKS_FILE):
            try:
                with open(TASKS_FILE, 'r', encoding='utf-8') as f:
                    self.tasks = json.load(f)
                for task in self.tasks:
                    if "created" not in task:
                        task["created"] = self.get_english_time()
                self.update_task_list()
            except Exception as e:
                messagebox.showerror("파일 읽기 오류", f"tasks.json 읽기 실패:\n\n{e}")

    def save_tasks(self):
        try:
            with open(TASKS_FILE, 'w', encoding='utf-8') as f:
                json.dump(self.tasks, f, indent=4, ensure_ascii=False)
        except Exception as e:
            messagebox.showerror("파일 저장 오류", f"tasks.json 저장 실패:\n\n{e}")

    def schedule_reminder(self):
        self.root.after(60 * 60 * 1000, self.schedule_reminder)
        self.show_reminder()

    def show_reminder(self):
        if not self.tasks:
            return
        pending = [t["task"] for t in self.tasks if not t["done"]]
        if not pending:
            return
        message = "Check your pending tasks:\n\n" + "\n".join(f"- {t}" for t in pending[:5])
        messagebox.showinfo("To-Do Reminder", message)

if __name__ == "__main__":
    root = tk.Tk()
    app = ToDoApp(root)
    root.mainloop()