Rozdział 16 · Tworzenie fajnych aplikacji z Tkinter

Mini-projekt: lista zadania

Proste lista linii i cała mała aplikacja z zapisywaniem między przejściami.

Lista zadań — integracja rozdziału 11 (listy), rozdziału 15 (JSON) i Tkinter (Listbox).

tasks : list[str]
ekspozycja
Listbox
callback
add_task() / remove_task()
wytrwałości
todo.json
Dane, mapowanie i trwałość to trzy różne obowiązki jednej małej aplikacji.
todo_logika.py
import json
from pathlib import Path

def load_tasks(path):
    if not path.exists():
        return []
    with path.open("r", encoding="utf-8") as f:
        return json.load(f)

def save_tasks(path, tasks):
    with path.open("w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)

def add_task(tasks, text):
    if not text.strip():
        return tasks
    return tasks + [text.strip()]

def remove_task(tasks, index):
    return tasks[:index] + tasks[index + 1:]
Lista zadań: trzy zadania, drugie jest podświetlone, pole wejściowe oraz przyciski + / −
Wynik wykonania kodu znajduje się poniżej. Przykład pojawienia się w środowisku kursu. Czcionki, rozmiary i motywy mogą się nieznacznie różnić w zależności od systemu operacyjnego.
Pojedyncze funkcje callback bez klasy są źródłem błędu
Callback trochę on_add(), zadeklarowany samodzielnie (nie w innej funkcji), nie można użyć nonlocal tasksnonlocal wymaga zmiennej z funkcji obejmującej, a nie z zakresu globalnego. Bardziej odpowiednią klasą zastosowań tutaj (Rozdział 14) jest self.tasks rozwiązuje ten problem naturalnie.
todo_gui.py
class TodoApp:
    def __init__(self, root, path):
        self.root = root
        self.path = path
        self.tasks = load_tasks(path)
        self.new_task_var = tk.StringVar()
        self.listbox = tk.Listbox(root, height=6)
        self.listbox.pack()
        entry = ttk.Entry(root, textvariable=self.new_task_var)
        entry.pack(side="left", fill="x", expand=True)
        ttk.Button(root, text="+", command=self.on_add).pack(side="left")
        ttk.Button(root, text="−", command=self.on_delete).pack(side="left")
        self.refresh_listbox()

    def refresh_listbox(self):
        self.listbox.delete(0, "end")
        for task in self.tasks:
            self.listbox.insert("end", task)

    def on_add(self):
        self.tasks = add_task(self.tasks, self.new_task_var.get())
        self.new_task_var.set("")
        self.refresh_listbox()
        save_tasks(self.path, self.tasks)

    def on_delete(self):
        selected = self.listbox.curselection()
        if not selected:
            return
        self.tasks = remove_task(self.tasks, selected[0])
        self.refresh_listbox()
        save_tasks(self.path, self.tasks)
Logika jest sprawdzalna bez żadnego widżetu
add_task, remove_task, load_tasks, save_tasks są zwykłe funkcje z listami i słownikami. To właśnie sprawdza praktyka tej sekcji — dokładnie ten podział omawiany w sekcji 16.24.
Praktyka: Logika listy zadań
Sprawdziłem bez tkinter – funkcje pracy z listą zadań
Otwórz praktykę →