Tkinter ToDo: implementace filtrů, vyhledávání a exportu dat
Integrace výběru priority přímo do vstupního panelu urychluje vytváření úkolů. Nahradíme modální okno pomocí ttk.Combobox s možnostmi high, medium, low. Aktivace přidání klávesou Enter zjednodušuje workflow.
V create_widgets přidáme rámec s Entry, Combobox a tlačítkem:
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="Priorita:").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="➕ Přidat", command=self.add_task, bg="light blue")
self.add_button.pack(side=tk.RIGHT)
Metoda add_task se zjednodušuje: bere data z self.entry a self.priority_var.get(), uloží úkol, vyčistí pole a aktualizuje seznam.
Filtrace podle stavu úkolů
Radioknočky přepínají zobrazení: všechny úkoly, pouze aktivní nebo dokončené. Logika filtrace se integruje do get_sorted_tasks.
V __init__ přidáme self.filter_mode = "all". V create_widgets — panel s Radiobutton:
filter_frame = tk.Frame(self.root)
filter_frame.pack(pady=5, padx=10, fill=tk.X)
tk.Label(filter_frame, text="Filtr:").pack(side=tk.LEFT, padx=(0, 10))
self.filter_var = tk.StringVar(value="all")
tk.Radiobutton(filter_frame, text="Vše", variable=self.filter_var,
value="all", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)
tk.Radiobutton(filter_frame, text="Aktivní", variable=self.filter_var,
value="active", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)
tk.Radiobutton(filter_frame, text="Dokončené", variable=self.filter_var,
value="completed", command=self.refresh_task_list).pack(side=tk.LEFT, padx=5)
V refresh_task_list: self.filter_mode = self.filter_var.get().
get_sorted_tasks nejprve filtruje podle stavu, poté řadí aktivní podle ID nahoře, dokončené dole:
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
Dynamické vyhledávání podle textu úkolů
Pole pro vyhledávání s bind na <KeyRelease> zajišťuje real-time filtraci nad aktuálním filtrem stavu. Tlačítko pro vymazání resetuje dotaz.
V create_widgets přidáme search_frame:
search_frame = tk.Frame(self.root)
search_frame.pack(pady=(0, 5), padx=10, fill=tk.X)
tk.Label(search_frame, text="Hledat:").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))
Metoda clear_search vyčistí self.search_entry a zavolá refresh_task_list.
get_sorted_tasks se rozšíří o druhý filtr podle search_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()]
Vyhledávání je case-insensitive, aplikuje se po filtru stavu.
- Výhody real-time vyhledávání: Minimální zpoždění díky list comprehension.
- Omezení: Lineární vyhledávání O(n), vhodné pro <1000 úkolů.
- Optimalizace: Pro větší seznamy zvažte bisect nebo externí index.
Export filtrovaných dat do CSV
Export zachycuje aktuální pohled na seznam (po všech filtrech/vyhledávání). Použijeme csv.writer s UTF-8 BOM pro kompatibilitu s Excel. Oddělovač ';', záhlaví: ID, Text, Status, Priorita, Datum vytvoření.
Tlačítko v bottom_frame:
self.export_button = tk.Button(bottom_frame, text="📎 Export do 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("Informace", "Žádné úkoly k exportu")
return
filename = filedialog.asksaveasfilename(
defaultextension=".csv",
filetypes=[("CSV soubory", "*.csv"), ("Všechny soubory", "*.*")],
title="Uložit jako 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', 'Text', 'Status', 'Priorita', 'Datum vytvoření'])
for task in tasks_to_export:
status = "Dokončeno" if task["completed"] else "Aktivní"
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": "Vysoká", "medium": "Střední", "low": "Nízká"}
priority_display = priority_map.get(task["priority"], task["priority"])
writer.writerow([task["id"], task["text"], status, priority_display, date_str])
messagebox.showinfo("Úspěch", f"Úkoly byly exportovány do souboru:\n{filename}")
except Exception as e:
messagebox.showerror("Chyba", f"Nelze uložit soubor: {e}")
Co je důležité
- Integrace Combobox pro priority zjednodušuje UX bez modálních oken.
- Kombinovaná filtrace (stav + vyhledávání) využívá řetězec list comprehension pro výkon.
- CSV export s UTF-8-sig a ';' zajišťuje kompatibilitu s Excel na Windows.
- Real-time aktualizace přes bind na <KeyRelease> a command v Radiobutton.
- Řazení vždy umisťuje aktivní úkoly nahoru pro zaměření na priority.
— Editorial Team
Zatím žádné komentáře.