Powrót do strony głównej

CRUD w Pythonie: tworzymy menedżer zadań z Tkinter i JSON

Praktyczne przewodnictwo po implementacji operacji CRUD w Pythonie poprzez stworzenie desktopowego menedżera zadań. Artykuł obejmuje pracę z Tkinter do budowania graficznego interfejsu, użycie JSON do przechowywania danych i podstawy programowania obiektowego.

Tworzymy aplikację CRUD w Pythonie: menedżer zadań od zera
Advertisement 728x90

Praktyczny przewodnik CRUD: Tworzymy menedżer zadań w Pythonie z Tkinter

CRUD (Create, Read, Update, Delete) to fundamentalny wzorzec do pracy z danymi. W tym przewodniku zaimplementujemy go w praktyce, tworząc aplikację desktopową do zarządzania zadaniami za pomocą Pythona, Tkinter i JSON do przechowywania danych. Projekt jest odpowiedni dla programistów, którzy chcą utrwalić podstawy OOP, pracę z plikami i budowanie interfejsów graficznych.

Architektura aplikacji i przygotowanie środowiska

Nasza aplikacja będzie składać się z jednego pliku app.py. Do przechowywania danych wybrano format JSON, jako najprostszy i najbardziej przejrzysty dla takich zadań. Każde zadanie jest reprezentowane jako słownik z polami: id (unikalny identyfikator), text (opis) i completed (status wykonania). Wszystkie zadania są przechowywane na liście, która jest serializowana do pliku tasks.json.

Przed rozpoczęciem upewnij się, że masz zainstalowany Python 3.6 lub nowszy. Tkinter jest częścią standardowej biblioteki, więc dodatkowa instalacja nie jest wymagana. Utwórz nowy katalog dla projektu i otwórz go w swoim IDE.

Google AdInline article slot

Implementacja modelu danych i pracy z JSON

Najpierw zaimplementujemy funkcje do ładowania i zapisywania danych. To podstawa operacji CRUD Read i Update.

import json
import os

TASKS_FILE = "tasks.json"

def load_tasks():
    """Ładuje listę zadań z pliku JSON."""
    if not os.path.exists(TASKS_FILE):
        return []
    with open(TASKS_FILE, "r", encoding="utf-8") as f:
        try:
            return json.load(f)
        except json.JSONDecodeError:
            return []

def save_tasks(tasks):
    """Zapisuje listę zadań do pliku 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):
    """Generuje następny unikalny ID dla nowego zadania."""
    if not tasks:
        return 1
    return max(task["id"] for task in tasks) + 1

Kluczowe aspekty tej implementacji:

  • Funkcja load_tasks obsługuje przypadki braku lub uszkodzenia pliku.
  • Parametr ensure_ascii=False w json.dump zapewnia poprawne zapisywanie znaków diakrytycznych.
  • indent=2 formatuje JSON dla czytelności.

Budowanie interfejsu graficznego z Tkinter

Stworzymy klasę TodoApp, która będzie zarządzać głównym oknem i widżetami. Użyjemy Tkinter do zbudowania interfejsu, składającego się z:

Google AdInline article slot
  • Pola wprowadzania dla nowego zadania.
  • Listy (Listbox) do wyświetlania wszystkich zadań.
  • Przycisków sterowania: dodawanie, oznaczanie jako wykonane, usuwanie.
import tkinter as tk
from tkinter import messagebox

class TodoApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Menedżer zadań")
        self.root.geometry("500x400")
        self.root.resizable(False, False)
        self.tasks = load_tasks()
        self.create_widgets()
        self.refresh_task_list()

    def create_widgets(self):
        """Inicjalizuje wszystkie elementy interfejsu."""
        # Górny panel: pole wprowadzania i przycisk dodawania
        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())
        self.add_button = tk.Button(top_frame, text="➕ Dodaj", command=self.add_task)
        self.add_button.pack(side=tk.RIGHT)
        # Lista zadań z przewijaniem
        list_frame = tk.Frame(self.root)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        scrollbar = tk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.task_listbox = tk.Listbox(
            list_frame,
            font=("Arial", 11),
            yscrollcommand=scrollbar.set,
            selectmode=tk.SINGLE,
            height=15
        )
        self.task_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.task_listbox.yview)
        # Dolny panel: przyciski akcji
        bottom_frame = tk.Frame(self.root)
        bottom_frame.pack(pady=10, padx=10, fill=tk.X)
        self.done_button = tk.Button(bottom_frame, text="✅ Wykonano", command=self.mark_done)
        self.done_button.pack(side=tk.LEFT, padx=5)
        self.delete_button = tk.Button(bottom_frame, text="🗑 Usuń", command=self.delete_task)
        self.delete_button.pack(side=tk.LEFT, padx=5)
        self.exit_button = tk.Button(bottom_frame, text="Wyjście", command=self.root.quit)
        self.exit_button.pack(side=tk.RIGHT, padx=5)

    def refresh_task_list(self):
        """Aktualizuje wyświetlanie listy zadań w interfejsie."""
        self.task_listbox.delete(0, tk.END)
        for task in self.tasks:
            status = "✓" if task["completed"] else "○"
            display_text = f"{status} {task['id']}. {task['text']}"
            self.task_listbox.insert(tk.END, display_text)

Metoda refresh_task_list implementuje operację Read, wyświetlając aktualny stan danych. Symbole ✓ i ○ wizualizują status wykonania.

Implementacja operacji CRUD w interfejsie

Dodajmy do klasy metody, które obsługują działania użytkownika, wykonując odpowiednie operacje CRUD.

Create: dodawanie nowego zadania

    def add_task(self):
        """Dodaje nowe zadanie (Create)."""
        text = self.entry.get().strip()
        if not text:
            messagebox.showwarning("Ostrzeżenie", "Wprowadź tekst zadania")
            return
        next_id = get_next_id(self.tasks)
        new_task = {
            "id": next_id,
            "text": text,
            "completed": False
        }
        self.tasks.append(new_task)
        save_tasks(self.tasks)
        self.entry.delete(0, tk.END)
        self.refresh_task_list()
        messagebox.showinfo("Sukces", f"Zadanie dodane (ID: {next_id})")

Update: oznaczanie zadania jako wykonanego

    def mark_done(self):
        """Oznacza wybrane zadanie jako wykonane (Update)."""
        selection = self.task_listbox.curselection()
        if not selection:
            messagebox.showwarning("Ostrzeżenie", "Wybierz zadanie")
            return
        index = selection[0]
        task_id = self.tasks[index]["id"]
        for task in self.tasks:
            if task["id"] == task_id:
                if task["completed"]:
                    messagebox.showinfo("Informacja", "Zadanie już wykonane")
                else:
                    task["completed"] = True
                    save_tasks(self.tasks)
                    self.refresh_task_list()
                    messagebox.showinfo("Sukces", f"Zadanie #{task_id} oznaczone jako wykonane")
                return

Delete: usuwanie zadania

    def delete_task(self):
        """Usuwa wybrane zadanie (Delete)."""
        selection = self.task_listbox.curselection()
        if not selection:
            messagebox.showwarning("Ostrzeżenie", "Wybierz zadanie")
            return
        index = selection[0]
        task_id = self.tasks[index]["id"]
        task_text = self.tasks[index]["text"]
        if messagebox.askyesno("Potwierdzenie", f"Usunąć zadanie #{task_id} \"{task_text}\"?"):
            del self.tasks[index]
            save_tasks(self.tasks)
            self.refresh_task_list()
            messagebox.showinfo("Sukces", f"Zadanie #{task_id} usunięte")

Każda metoda zawiera walidację wprowadzania, zmianę danych w pamięci, zapis do pliku i aktualizację interfejsu. Użycie messagebox poprawia UX, zapewniając informację zwrotną.

Google AdInline article slot

Kompilacja i uruchomienie aplikacji

Aby zakończyć projekt, połączymy wszystkie komponenty w jednym pliku i dodamy punkt wejścia.

if __name__ == "__main__":
    root = tk.Tk()
    app = TodoApp(root)
    root.mainloop()

Uruchom skrypt poleceniem python app.py. Aplikacja otworzy okno, gdzie będziesz mógł przetestować wszystkie operacje CRUD. Dane będą zapisywane między sesjami w pliku tasks.json.

Co jest ważne

  • CRUD to podstawa: Wzorzec Create, Read, Update, Delete jest fundamentalny dla większości aplikacji pracujących z danymi.
  • JSON do przechowywania: Użycie plików JSON to prosty i efektywny sposób przechowywania danych dla małych projektów, które nie wymagają pełnej bazy danych.
  • Tkinter dla GUI: Biblioteka Tkinter pozwala szybko tworzyć wieloplatformowe interfejsy desktopowe bez zewnętrznych zależności.
  • Struktura kodu: Jasne podzielenie na model danych (praca z JSON), widok (Tkinter) i logikę aplikacji (klasa TodoApp) poprawia utrzymywalność kodu.
  • Obsługa błędów: Implementacja zawiera sprawdzanie poprawności wprowadzania i obsługę wyjątków (np. uszkodzony plik JSON).

Dalszy rozwój projektu

Tego menedżera zadań można rozszerzyć w następujący sposób:

  • Dodanie kategorii zadań z możliwością filtrowania.
  • Implementacja terminów z użyciem modułu datetime i kolorową sygnalizacją.
  • Eksport danych do formatów CSV lub Excel do raportowania.
  • Integracja z bazami danych (SQLite, PostgreSQL) dla bardziej niezawodnego przechowywania.
  • Stworzenie wersji webowej z użyciem Flask lub Django, co pozwoli przećwiczyć te same operacje CRUD w innym kontekście.

Takie podejście pokazuje, jak podstawowe koncepcje programowania są stosowane w rzeczywistych projektach, zapewniając solidną podstawę do nauki bardziej złożonych tematów.

— Editorial Team

Advertisement 728x90

Czytaj dalej