홈으로 돌아가기

Tkinter ToDo: 검색 필터 CSV 내보내기

이 기사는 Tkinter ToDo 확장을 설명합니다: 우선순위용 Combobox 추가, 라디오 버튼 필터링, 실시간 검색 및 CSV 내보내기. UTF-8 처리와 정렬을 포함한 메서드의 전체 코드 포함.

Tkinter ToDo 애플리케이션의 필터 및 데이터 내보내기
Advertisement 728x90

Tkinter ToDo 앱: 필터링, 검색 및 CSV 내보내기

입력 패널에 우선순위 선택을 직접 통합하면 작업 생성이 훨씬 빨라집니다. 모달 대화상자를 ttk.Combobox로 대체해 높음, 중간, 낮음 옵션을 제공합니다. Enter 키로 추가 기능을 활성화해 워크플로를 간소화합니다.

create_widgets에서 Entry, Combobox, 버튼이 포함된 프레임을 추가합니다:

from tkinter import ttk

top_frame = tk.Frame(self.root)
top_frame.pack(pady=10, padx=10, fill=tk.X)

self.entry = tk.Entry(top_frame, font=("Arial", 12))
self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
self.entry.bind("<Return>", lambda event: self.add_task())

tk.Label(top_frame, text="우선순위:").pack(side=tk.LEFT, padx=(5, 2))
self.priority_var = tk.StringVar(value="medium")
priority_combo = ttk.Combobox(
    top_frame, 
    textvariable=self.priority_var, 
    values=["high", "medium", "low"],
    state="readonly",
    width=8
)
priority_combo.pack(side=tk.LEFT, padx=(0, 5))
priority_combo.set("medium")

self.add_button = tk.Button(top_frame, text="➕ 추가", command=self.add_task, bg="light blue")
self.add_button.pack(side=tk.RIGHT)

add_task 메서드는 self.entryself.priority_var.get()에서 데이터를 가져와 작업을 저장하고, 필드를 지우며 목록을 새로고침합니다.

Google AdInline article slot

상태별 작업 필터링

라디오 버튼으로 전체 작업, 진행 중 작업, 완료된 작업만 보기 전환. 필터 로직은 get_sorted_tasks에 통합됩니다.

__init__에서 self.filter_mode = "all" 추가. create_widgets에서 라디오 버튼이 포함된 필터 패널 추가:

filter_frame = tk.Frame(self.root)
filter_frame.pack(pady=5, padx=10, fill=tk.X)

tk.Label(filter_frame, text="필터:").pack(side=tk.LEFT, padx=(0, 10))
self.filter_var = tk.StringVar(value="all")

tk.Radiobutton(filter_frame, text="전체", variable=self.filter_var, 
               value="all", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)
tk.Radiobutton(filter_frame, text="진행 중", variable=self.filter_var, 
               value="active", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)
tk.Radiobutton(filter_frame, text="완료", variable=self.filter_var, 
               value="completed", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)

refresh_task_list에서: self.filter_mode = self.filter_var.get().

Google AdInline article slot

get_sorted_tasks는 먼저 상태로 필터링한 후, 진행 중 작업을 위로, 완료 작업을 아래로 ID 기준 정렬:

def get_sorted_tasks(self):
    if self.filter_mode == "active":
        filtered_tasks = [task for task in self.tasks if not task["completed"]]
    elif self.filter_mode == "completed":
        filtered_tasks = [task for task in self.tasks if task["completed"]]
    else:
        filtered_tasks = self.tasks.copy()
    
    active_tasks = [task for task in filtered_tasks if not task["completed"]]
    completed_tasks = [task for task in filtered_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

실시간 작업 텍스트 검색

<KeyRelease> 바인딩으로 검색 필드가 상태 필터 위에 즉시 필터링. 지우기 버튼으로 쿼리 초기화.

create_widgets에서 search_frame 추가:

Google AdInline article slot
search_frame = tk.Frame(self.root)
search_frame.pack(pady=(0, 5), padx=10, fill=tk.X)

tk.Label(search_frame, text="검색:").pack(side=tk.LEFT, padx=(0, 5))
self.search_entry = tk.Entry(search_frame, font=("Arial", 10))
self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
self.search_entry.bind("<KeyRelease>", lambda event: self.refresh_task_list())

clear_btn = tk.Button(search_frame, text="✖", command=self.clear_search, width=3)
clear_btn.pack(side=tk.RIGHT, padx=(5, 0))

clear_searchself.search_entry를 지우고 refresh_task_list 호출.

get_sorted_taskssearch_text = self.search_entry.get().strip().lower()로 두 번째 필터 추가:

search_text = self.search_entry.get().strip().lower()
if search_text:
    filtered_tasks = [task for task in filtered_tasks if search_text in task["text"].lower()]

검색은 대소문자 구분 없이 상태 필터링 후 적용.

  • 실시간 검색 장점: 리스트 컴프리헨션으로 지연 최소화.
  • 제한사항: 선형 O(n) 검색, 1000개 미만 작업에 적합.
  • 최적화: 대용량 목록에는 bisect나 외부 인덱싱 고려.

필터링된 데이터 CSV 내보내기

내보내기는 현재 목록 뷰(모든 필터/검색 적용 후)를 캡처. 엑셀 호환을 위해 csv.writer에 UTF-8 BOM 사용. 세미콜론 구분자, 헤더: ID, 텍스트, 상태, 우선순위, 생성일.

bottom_frame에 버튼:

self.export_button = tk.Button(bottom_frame, text="📎 CSV 내보내기", command=self.export_to_csv, bg="light cyan")
self.export_button.pack(side=tk.LEFT, padx=5)

export_to_csv:

import csv
from tkinter import filedialog

def export_to_csv(self):
    tasks_to_export = self.get_sorted_tasks()
    if not tasks_to_export:
        messagebox.showinfo("알림", "내보낼 작업이 없습니다")
        return
    
    filename = filedialog.asksaveasfilename(
        defaultextension=".csv",
        filetypes=[("CSV 파일", "*.csv"), ("모든 파일", "*.*")],
        title="CSV로 저장"
    )
    if not filename:
        return
    
    try:
        with open(filename, 'w', encoding='utf-8-sig', newline='') as f:
            writer = csv.writer(f, delimiter=';')
            writer.writerow(['ID', '텍스트', '상태', '우선순위', '생성일'])
            
            for task in tasks_to_export:
                status = "완료" if task["completed"] else "진행 중"
                try:
                    created = datetime.fromisoformat(task["created_at"])
                    date_str = created.strftime("%d.%m.%Y %H:%M:%S")
                except:
                    date_str = task["created_at"]
                priority_map = {"high": "높음", "medium": "중간", "low": "낮음"}
                priority_display = priority_map.get(task["priority"], task["priority"])
                
                writer.writerow([task["id"], task["text"], status, priority_display, date_str])
        
        messagebox.showinfo("성공", f"작업이 내보내졌습니다:\n{filename}")
    except Exception as e:
        messagebox.showerror("오류", f"파일 저장 실패: {e}")

주요 요약

  • Combobox 우선순위 통합으로 팝업 없이 UX 간소화.
  • 결합 필터링(상태 + 검색)은 체인 리스트 컴프리헨션으로 빠름.
  • CSV 내보내기는 UTF-8-sig와 ';'로 윈도우 엑셀과 완벽 호환.
  • 실시간 업데이트<KeyRelease> 바인딩과 라디오 버튼 명령으로.
  • 정렬은 항상 진행 중 작업을 최상단에.

— Editorial Team

Advertisement 728x90

다음 읽기