홈으로 돌아가기

Tkinter ToDo: 편집, 색상 및 작업 정렬

이 기사는 Tkinter ToDo 애플리케이션의 확장을 설명합니다. 새로운 기능: 모달 창을 통한 작업 편집, 우선순위 색상 표시, 미완료 작업 정렬, 생성 날짜 저장. JSON 데이터의 하위 호환성 및 Listbox 스타일링 기법을 포함한 전체 코드 제공.

Tkinter ToDo: 색상, 정렬 및 편집 모달 창
Advertisement 728x90

Tkinter 할 일 앱 확장하기: 편집, 색상, 정렬 및 날짜 기능 추가

기능을 확장하려면 작업 구조를 업데이트해야 합니다. 각 작업에는 이제 id, text, completed, created_at(ISO 문자열), priority(high/medium/low) 필드가 포함됩니다. 이전 버전과의 호환성을 위해 기본값을 사용한 기존 데이터 마이그레이션이 구현되었습니다.

로딩 및 저장 기능은 원활한 통합을 보장합니다:

import json
import os
from datetime import datetime

TASKS_FILE = "tasks.json"

def load_tasks():
    """이전 버전과 호환되도록 JSON 파일에서 작업을 로드합니다"""
    if not os.path.exists(TASKS_FILE):
        return []
    with open(TASKS_FILE, "r", encoding="utf-8") as f:
        try:
            tasks = json.load(f)
            # 이전 작업 마이그레이션 (created_at 및 priority가 없는 경우 추가)
            for task in tasks:
                if "created_at" not in task:
                    task["created_at"] = datetime.now().isoformat()
                if "priority" not in task:
                    task["priority"] = "medium"
            return tasks
        except json.JSONDecodeError:
            return []

def save_tasks(tasks):
    """작업을 JSON 파일에 저장합니다"""
    with open(TASKS_FILE, "w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)

def get_next_id(tasks):
    """새 작업에 사용할 다음 사용 가능한 ID를 반환합니다"""
    if not tasks:
        return 1
    return max(task["id"] for task in tasks) + 1

datetime.now().isoformat()2025-03-22T15:30:45.123456 형식의 정렬 가능한 문자열을 생성합니다.

Google AdInline article slot

정렬 및 시각적 표시

정렬은 self.tasks의 원래 작업 순서를 변경하지 않고 구현됩니다. get_sorted_tasks 메서드는 복사본을 반환합니다:

  • 먼저, 미완료 작업(completed == False)
  • 그 다음, 완료된 작업
  • 그룹 내에서 — id 기준
class TodoApp:
    
    def get_sorted_tasks(self):
        """표시용으로 정렬된 작업을 반환합니다: 활성 작업 먼저, 그 다음 완료된 작업"""
        active_tasks = [task for task in self.tasks if not task["completed"]]
        completed_tasks = [task for task in self.tasks if task["completed"]]
        active_tasks.sort(key=lambda x: x["id"])
        completed_tasks.sort(key=lambda x: x["id"])
        return active_tasks + completed_tasks

    def refresh_task_list(self):
        """정렬 및 색상 표시를 사용하여 작업 목록 표시를 업데이트합니다"""
        self.task_listbox.delete(0, tk.END)
        sorted_tasks = self.get_sorted_tasks()

        for task in sorted_tasks:
            status = "✓" if task["completed"] else "○"
            try:
                created = datetime.fromisoformat(task["created_at"])
                date_str = created.strftime("%m/%d/%y %H:%M")
            except:
                date_str = "날짜 알 수 없음"

            display_text = f"{status} [{date_str}] {task['id']}. {task['text']}"

            self.task_listbox.insert(tk.END, display_text)

            bg_color = "white"
            fg_color = "black"

            if task["completed"]:
                fg_color = "green"
                bg_color = "light gray"
            else:
                if task["priority"] == "high":
                    fg_color = "red"
                    bg_color = "#ffe6e6"
                elif task["priority"] == "medium":
                    fg_color = "orange"
                    bg_color = "#fff0e6"

            index = tk.END
            self.task_listbox.itemconfig(tk.END, bg=bg_color, fg=fg_color)

주요 기술:

  • itemconfig(index, bg=..., fg=...)를 사용하여 Listbox 행 스타일링
  • strftime("%m/%d/%y %H:%M")를 사용하여 지역화된 날짜 형식
  • refresh_task_list 중에만 지연 정렬

모달 편집 창

편집은 모달성을 위해 grab_set()이 있는 Toplevel 창을 엽니다. 로직은 정렬된 목록에서 id로 작업을 찾습니다:

Google AdInline article slot
class TodoApp:
   def edit_task(self):
        selection = self.task_listbox.curselection()
        if not selection:
            messagebox.showwarning("경고", "편집할 작업을 선택하세요")
            return

        selected_index = selection[0]
        sorted_tasks = self.get_sorted_tasks()
        selected_task = sorted_tasks[selected_index]
        task_id = selected_task["id"]

        task_to_edit = None
        task_index = None
        for i, task in enumerate(self.tasks):
            if task["id"] == task_id:
                task_to_edit = task
                task_index = i
                break

        if not task_to_edit:
            return

        edit_window = tk.Toplevel(self.root)
        edit_window.title(f"작업 #{task_id} 편집")
        edit_window.geometry("450x250")
        edit_window.resizable(False, False)
        edit_window.grab_set()

        tk.Label(edit_window, text="작업 내용:").pack(pady=(10, 0), anchor=tk.W, padx=10)
        text_entry = tk.Text(edit_window, height=4, width=50, font=("Arial", 10))
        text_entry.pack(pady=5, padx=10, fill=tk.BOTH, expand=True)
        text_entry.insert("1.0", task_to_edit["text"])

        tk.Label(edit_window, text="우선순위:").pack(pady=(5, 0), anchor=tk.W, padx=10)
        priority_var = tk.StringVar(value=task_to_edit["priority"])
        priority_frame = tk.Frame(edit_window)
        priority_frame.pack(pady=5, padx=10, anchor=tk.W)

        tk.Radiobutton(priority_frame, text="높음", variable=priority_var, value="high", fg="red").pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(priority_frame, text="중간", variable=priority_var, value="medium", fg="orange").pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(priority_frame, text="낮음", variable=priority_var, value="low").pack(side=tk.LEFT, padx=5)

        button_frame = tk.Frame(edit_window)
        button_frame.pack(pady=15)

        def save_edit():
            new_text = text_entry.get("1.0", "end-1c").strip()
            if not new_text:
                messagebox.showwarning("경고", "작업 내용은 비워둘 수 없습니다", parent=edit_window)
                return

            task_to_edit["text"] = new_text
            task_to_edit["priority"] = priority_var.get()
            save_tasks(self.tasks)
            self.refresh_task_list()
            edit_window.destroy()
            messagebox.showinfo("성공", f"작업 #{task_id}이(가) 업데이트되었습니다")

        tk.Button(button_frame, text="저장", command=save_edit, bg="light green", width=10).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="취소", command=edit_window.destroy, bg="light gray", width=10).pack(side=tk.LEFT, padx=5)

통합: create_widgetsself.edit_button = tk.Button(bottom_frame, text="✏ 편집", command=self.edit_task)를 추가합니다.

우선순위가 있는 작업 추가 대화상자

add_task 메서드는 Entry 및 라디오 버튼에 초점을 맞춘 유사한 Toplevel 구조를 사용합니다:

  • UX를 위한 text_entry.focus()
  • strip() 및 빈 값 확인을 통한 유효성 검사
  • id, created_at, completed=False 자동 생성

전체 메서드 코드는 편집과의 일관성을 보장합니다.

Google AdInline article slot

주요 포인트

  • 이전 버전과의 호환성: 로드 시 created_atpriority 자동 마이그레이션
  • 성능: 목록의 복사본 정렬, 지속 데이터는 변경하지 않음
  • Listbox 스타일링: 동적 색상을 위한 itemconfig(tk.END, bg=..., fg=...)
  • 모달 창: Toplevel + grab_set() + resizable(False, False)
  • 날짜 형식: ISO 문자열의 가독성 있는 표시를 위한 strftime

— Editorial Team

Advertisement 728x90

다음 읽기