Back to Home

CRUD in Python: creating a task manager with Tkinter and JSON

Practical guide to implementing CRUD operations in Python by creating a desktop task manager. The article covers working with Tkinter to build a graphical interface, using JSON for data storage, and the basics of object-oriented programming.

Creating a CRUD application in Python: task manager from scratch
Advertisement 728x90

Hands-On CRUD Guide: Build a Task Manager with Python and Tkinter

CRUD (Create, Read, Update, Delete) is a core pattern for data operations. In this guide, we'll put it into practice by building a desktop task manager using Python, Tkinter, and JSON for data storage. Perfect for developers looking to solidify OOP basics, file handling, and GUI development.

App Architecture and Setup

Our app will live in a single app.py file. We'll use JSON for data storage—simple and straightforward for this kind of project. Each task is a dictionary with id (unique identifier), text (description), and completed (status). All tasks are stored in a list, serialized to tasks.json.

Make sure you have Python 3.6 or later installed. Tkinter comes built-in, so no extra installs needed. Create a new project folder and open it in your IDE.

Google AdInline article slot

Data Model and JSON Handling

First, we'll implement functions for loading and saving data—the foundation for Read and Update CRUD operations.

import json
import os

TASKS_FILE = "tasks.json"

def load_tasks():
    """Loads tasks from JSON file."""
    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):
    """Saves tasks list to JSON file."""
    with open(TASKS_FILE, "w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)

def get_next_id(tasks):
    """Generates next unique ID for a new task."""
    if not tasks:
        return 1
    return max(task["id"] for task in tasks) + 1

Key features:

  • load_tasks handles missing or corrupted files gracefully.
  • ensure_ascii=False in json.dump ensures proper Unicode support.
  • indent=2 keeps JSON readable.

Building the GUI with Tkinter

We'll create a TodoApp class to manage the main window and widgets. The interface includes:

Google AdInline article slot
  • Input field for new tasks.
  • Listbox to display all tasks.
  • Control buttons: add, mark done, delete.
import tkinter as tk
from tkinter import messagebox

class TodoApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Task Manager")
        self.root.geometry("500x400")
        self.root.resizable(False, False)
        self.tasks = load_tasks()
        self.create_widgets()
        self.refresh_task_list()

    def create_widgets(self):
        """Sets up all UI elements."""
        # Top panel: input and add button
        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="➕ Add", command=self.add_task)
        self.add_button.pack(side=tk.RIGHT)
        # Task list with scrollbar
        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)
        # Bottom panel: action buttons
        bottom_frame = tk.Frame(self.root)
        bottom_frame.pack(pady=10, padx=10, fill=tk.X)
        self.done_button = tk.Button(bottom_frame, text="✅ Done", command=self.mark_done)
        self.done_button.pack(side=tk.LEFT, padx=5)
        self.delete_button = tk.Button(bottom_frame, text="🗑 Delete", command=self.delete_task)
        self.delete_button.pack(side=tk.LEFT, padx=5)
        self.exit_button = tk.Button(bottom_frame, text="Exit", command=self.root.quit)
        self.exit_button.pack(side=tk.RIGHT, padx=5)

    def refresh_task_list(self):
        """Refreshes the task list display."""
        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)

refresh_task_list handles the Read operation, showing the current data state. Checkmarks (✓/○) visualize completion status.

Implementing CRUD Operations

Next, add methods to the class for user actions, each tied to a CRUD operation.

Create: Adding a New Task

    def add_task(self):
        """Adds a new task (Create)."""
        text = self.entry.get().strip()
        if not text:
            messagebox.showwarning("Warning", "Enter task text")
            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("Success", f"Task added (ID: {next_id})")

Update: Marking a Task Done

    def mark_done(self):
        """Marks selected task as done (Update)."""
        selection = self.task_listbox.curselection()
        if not selection:
            messagebox.showwarning("Warning", "Select a task")
            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("Info", "Task already done")
                else:
                    task["completed"] = True
                    save_tasks(self.tasks)
                    self.refresh_task_list()
                    messagebox.showinfo("Success", f"Task #{task_id} marked done")
                return

Delete: Removing a Task

    def delete_task(self):
        """Deletes selected task (Delete)."""
        selection = self.task_listbox.curselection()
        if not selection:
            messagebox.showwarning("Warning", "Select a task")
            return
        index = selection[0]
        task_id = self.tasks[index]["id"]
        task_text = self.tasks[index]["text"]
        if messagebox.askyesno("Confirm", f"Delete task #{task_id} \"{task_text}\"?"):
            del self.tasks[index]
            save_tasks(self.tasks)
            self.refresh_task_list()
            messagebox.showinfo("Success", f"Task #{task_id} deleted")

Each method validates input, updates in-memory data, saves to file, and refreshes the UI. Messageboxes provide clear user feedback.

Google AdInline article slot

Assembling and Running the App

Tie it all together in one file with a main entry point.

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

Run with python app.py. Test all CRUD ops—the app saves data to tasks.json between sessions.

Key Takeaways

  • CRUD Fundamentals: Create, Read, Update, Delete powers most data-driven apps.
  • JSON Storage: Ideal for small projects—no database needed.
  • Tkinter GUI: Quick, cross-platform desktop UIs with zero dependencies.
  • Clean Code Structure: Separates data model (JSON), view (Tkinter), and logic (TodoApp) for easy maintenance.
  • Error Handling: Validates inputs and catches issues like bad JSON.

Next Steps

Expand your task manager:

  • Task Categories with filtering.
  • Deadlines using datetime and color-coding.
  • Export to CSV or Excel.
  • Database Integration (SQLite, PostgreSQL).
  • Web Version with Flask or Django.

This project shows how core programming concepts apply to real-world apps, building a strong foundation for advanced topics.

— Editorial Team

Advertisement 728x90

Read Next