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()