Loading ...

Building a Modern CRUD Menu Application with Python and ttkbootstrap


Title: Building a Modern CRUD Menu Application with Python and ttkbootstrap

Introduction

Welcome to my latest tech adventure! In this blog post, I鈥檒l walk you through the development of a Modern CRUD Menu Application built with Python, leveraging the power of ttkbootstrap鈥攁 Bootstrap-inspired extension for Tkinter. This project combines data management functionality with a sleek, user-friendly interface, making it a perfect example of blending backend logic with frontend design. Whether you’re a developer looking to enhance your skills or just curious about Python GUI applications, this post is for you!

What is a CRUD Application?

CRUD stands for Create, Read, Update, and Delete鈥攖he four basic operations for managing data in any application. My CRUD Menu Application allows users to manage a menu database, complete with item details, prices, categories, and images. It鈥檚 a practical tool that could be used in a restaurant or caf茅 setting, but it鈥檚 also a great learning project for mastering GUI development.

Project Overview

Here鈥檚 what makes this application stand out:

  • CRUD Operations: Add new menu items, update existing ones, delete items, and refresh the list with intuitive buttons.
  • Image Preview: Double-click an item to view its associated image, supporting both local files and web URLs.
  • Data Persistence: Data is stored in a JSON file, ensuring it persists across sessions.
  • Modern UI: Powered by ttkbootstrap, the app features a professional, responsive design with Bootstrap-inspired themes like ‘flatly’.

The application is built using:

  • Python: The core programming language.
  • Tkinter: The standard Python GUI library.
  • ttkbootstrap: Adds modern styling and themes to Tkinter widgets.
  • PIL (Pillow): For image processing and display.

Development Journey

Setting Up the Environment

To get started, I installed ttkbootstrap via pip:

pip install ttkbootstrap

This library extends Tkinter with Bootstrap themes, providing a polished look without the need for external CSS frameworks.

Building the Interface

I structured the app with a top bar for CRUD buttons and a main area for a treeview to display the menu data. The ttk.Treeview widget, styled with ttkbootstrap, lists items with columns for ID, Item Name, Image URL, Price, and Category. Here鈥檚 a snippet of the treeview setup:

self.tree = ttk.Treeview(self.tree_frame, columns=columns, show="headings", height=25, style="info.Treeview")
for col in columns:
    self.tree.heading(col, text=col)
    self.tree.column(col, anchor=CENTER, width=220)
Adding Functionality
  • Create: A form dialog (built with tk.Toplevel) allows users to input item details.
  • Read: The treeview displays all items from the JSON file.
  • Update: Selecting an item opens the form pre-filled with existing data for editing.
  • Delete: A confirmation prompt ensures safe deletion.
Image Handling

One challenge was loading images from web URLs (e.g., https://rest.diciu.org/menus/). I implemented threaded image loading with urllib.request and added a User-Agent header to mimic browser requests. However, some URLs failed due to server restrictions (e.g., HTTP 403 errors). The solution? Enhanced error handling to display specific messages like “Failed to load image: HTTP Error 403: Forbidden.”

Challenges and Solutions

  • Button Visibility: Initial layouts hid the CRUD buttons. I resolved this by using a clear frame hierarchy with pack and grid.
  • Image Loading: Inaccessible URLs were a hurdle. I added fallback support for local files and plan to update the dataset with public URLs (e.g., from https://picsum.photos/).

Screenshots

[Insert Screenshot Here]
Caption: The CRUD Menu Application showing the treeview with sample data.

Future Improvements

  • Integrate a file picker for local image uploads.
  • Add validation for price inputs beyond numeric checks.
  • Explore database integration (e.g., SQLite) instead of JSON.

Conclusion

This project was a fantastic learning experience, blending Python鈥檚 versatility with a modern UI. The ttkbootstrap library made styling effortless, and tackling real-world issues like image loading deepened my problem-solving skills. I鈥檝e shared the code on [GitHub Link if available], and I鈥檇 love your feedback or collaboration ideas!

Have you worked with ttkbootstrap or built a CRUD app? Share your thoughts in the comments below!

Tags

Python #Tkinter #ttkbootstrap #CRUD #Programming #SoftwareDevelopment #UIUX #TechProjects #LearnToCode


Instructions for WordPress:

  1. Add to Editor:
  • Copy the text into the WordPress editor. Use the “Text” tab to preserve code blocks and formatting, or switch to “Visual” and manually format headings, lists, and code snippets.
  • Add the screenshot you shared earlier by uploading it to the media library and inserting it where indicated.
  1. Categories and Tags:
  • Assign categories like “Programming,” “Tutorials,” or “Projects.”
  • Use the suggested tags for better discoverability.
  1. Featured Image:
  • Set the screenshot as the featured image for the post.
  1. Call to Action:
  • Encourage reader engagement with the question at the end. You can also add a “Subscribe” or “Contact Me” link if desired.

import json
import tkinter as tk
from tkinter import messagebox
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from PIL import Image, ImageTk
import os
import urllib.request
import urllib.error
import threading

DATA_FILE = ‘data.json’

class ItemForm(tk.Toplevel):
def init(self, parent, title, item=None):
super().init(parent)
self.parent = parent
self.title(title)
self.geometry(“400×500”)
self.transient(parent)
self.grab_set()
self.item = item
self.result = None
form_frame = ttk.Frame(self, padding=20)
form_frame.pack(fill=BOTH, expand=YES)

self.fields = {}
labels = [“Item Name”, “Price”, “Category”, “Image Path/URL”]
defaults = [item.get(field.lower().replace(” “, “”), “”) if item else “”
for field in labels]

for i, (label, default) in enumerate(zip(labels, defaults)):
ttk.Label(form_frame, text=label + “:”, style=”info.TLabel”).grid(row=i, column=0, pady=10, padx=10, sticky=E)
entry = ttk.Entry(form_frame, width=30)
entry.insert(0, default)
entry.grid(row=i, column=1, pady=10, padx=10)
self.fields[label.lower().replace(” “, “”)] = entry

# Button frame
btn_frame = ttk.Frame(form_frame)
btn_frame.grid(row=len(labels), column=0, columnspan=2, pady=20)

ttk.Button(btn_frame, text=”Submit”, command=self.submit, style=”success.TButton”).pack(side=LEFT, padx=5)
ttk.Button(btn_frame, text=”Cancel”, command=self.destroy, style=”danger.TButton”).pack(side=LEFT, padx=5)

def submit(self):
self.result = {
“itemname”: self.fields[“itemname”].get(),
“price”: self.fields[“price”].get(),
“category”: self.fields[“category”].get(),
“image”: self.fields[“imagepath/url”].get()
}
self.destroy()

class ModernCRUDApp:
def init(self, root):
self.root = root
self.root.title(“Modern CRUD Menu”)
self.root.geometry(“1200×700”)

    # Use a Bootstrap theme
    style = ttk.Style()
    style.theme_use('flatly')  # Options: 'flatly', 'cosmo', 'minty', etc.

    # Button frame at the top
    self.button_frame = ttk.Frame(self.root, padding=10)
    self.button_frame.pack(fill=X, pady=(20, 10))

    # Main frame for treeview below buttons
    self.main_frame = ttk.Frame(self.root)
    self.main_frame.pack(fill=BOTH, expand=YES, pady=(0, 20))

    # Treeview frame inside main frame
    self.tree_frame = ttk.Frame(self.main_frame, padding=10)
    self.tree_frame.pack(fill=BOTH, expand=YES)

    self.setup_treeview()
    self.setup_buttons()
    self.refresh_data()

def setup_treeview(self):
    columns = ("ID", "Item Name", "Image", "Price", "Category")
    self.tree = ttk.Treeview(self.tree_frame, columns=columns, show="headings", height=25, style="info.Treeview")

    style = ttk.Style()
    style.configure("info.Treeview", rowheight=30, font=('Helvetica', 10))
    style.configure("info.Treeview.Heading", font=('Helvetica', 11, 'bold'))

    for col in columns:
        self.tree.heading(col, text=col)
        self.tree.column(col, anchor=CENTER, width=220)

    scrollbar = ttk.Scrollbar(self.tree_frame, orient=VERTICAL, command=self.tree.yview)
    self.tree.configure(yscrollcommand=scrollbar.set)

    self.tree.pack(side=LEFT, fill=BOTH, expand=YES, padx=(0, 10))
    scrollbar.pack(side=RIGHT, fill=Y)

    self.tree.bind("<Double-1>", self.show_image_preview)

def setup_buttons(self):
    buttons = [
        ("Add Item", self.add_item, "success"),
        ("Update Item", self.update_item, "info"),
        ("Delete Item", self.delete_item, "danger"),
        ("Refresh", self.refresh_data, "secondary")
    ]

    for text, command, style_type in buttons:
        btn = ttk.Button(self.button_frame, text=text, command=command, style=f"{style_type}.TButton", width=15)
        btn.pack(side=LEFT, padx=5, pady=5)

def load_data(self):
    try:
        with open(DATA_FILE, 'r') as file:
            return json.load(file)
    except (FileNotFoundError, json.JSONDecodeError):
        return []

def save_data(self, data):
    with open(DATA_FILE, 'w') as file:
        json.dump(data, file, indent=4)

def refresh_data(self):
    data = self.load_data()
    self.tree.delete(*self.tree.get_children())
    for item in data:
        self.tree.insert('', END, values=(
            item["id"],
            item["itemName"],
            item["itemImage"],
            f"${item['price']}",
            item["category"]
        ))

def add_item(self):
    form = ItemForm(self.root, "Add New Item")
    self.root.wait_window(form)

    if not form.result:
        return

    result = form.result
    if not all(result.values()):
        messagebox.showwarning("Warning", "All fields are required.")
        return

    try:
        float(result["price"])
    except ValueError:
        messagebox.showerror("Error", "Price must be a number.")
        return

    data = self.load_data()
    new_id = max((item["id"] for item in data), default=0) + 1

    new_item = {
        "id": new_id,
        "itemName": result["itemname"],
        "itemImage": result["image"],
        "price": result["price"],
        "category": result["category"],
        "ingredients": []
    }

    data.append(new_item)
    self.save_data(data)
    self.refresh_data()

def delete_item(self):
    selected = self.tree.focus()
    if not selected:
        messagebox.showwarning("Warning", "Please select an item to delete.")
        return

    if messagebox.askyesno("Confirm", "Are you sure you want to delete this item?"):
        item_id = int(self.tree.item(selected)["values"][0])
        data = self.load_data()
        data = [item for item in data if item["id"] != item_id]
        self.save_data(data)
        self.refresh_data()

def update_item(self):
    selected = self.tree.focus()
    if not selected:
        messagebox.showwarning("Warning", "Please select an item to update.")
        return

    values = self.tree.item(selected)["values"]
    item_id = int(values[0])
    data = self.load_data()
    item = next((x for x in data if x["id"] == item_id), None)

    if not item:
        return

    form = ItemForm(self.root, "Update Item", item)
    self.root.wait_window(form)

    if not form.result:
        return

    result = form.result
    if not all(result.values()):
        messagebox.showwarning("Warning", "All fields are required.")
        return

    try:
        float(result["price"])
    except ValueError:
        messagebox.showerror("Error", "Price must be a number.")
        return

    item.update({
        "itemName": result["itemname"],
        "itemImage": result["image"],
        "price": result["price"],
        "category": result["category"]
    })

    self.save_data(data)
    self.refresh_data()

def show_image_preview(self, event):
    selected = self.tree.focus()
    if not selected:
        return

    values = self.tree.item(selected)["values"]
    image_path = values[2]

    preview_window = tk.Toplevel(self.root)
    preview_window.title("Image Preview")
    preview_window.geometry("450x450")
    preview_window.transient(self.root)
    preview_window.grab_set()

    def load_image():
        try:
            if image_path.startswith(('http://', 'https://')):
                temp_file = "temp_img.jpg"
                req = urllib.request.Request(
                    image_path,
                    headers={'User-Agent': 'Mozilla/5.0'}
                )
                with urllib.request.urlopen(req, timeout=10) as response:
                    with open(temp_file, 'wb') as out_file:
                        out_file.write(response.read())
                img = Image.open(temp_file)
            else:
                img = Image.open(image_path)

            img = img.resize((400, 400), Image.Resampling.LANCZOS)
            photo = ImageTk.PhotoImage(img)

            label = ttk.Label(preview_window, image=photo, text="")
            label.image = photo
            label.pack(pady=20)
        except (urllib.error.URLError, urllib.error.HTTPError, ValueError, FileNotFoundError, OSError) as e:
            error_label = ttk.Label(preview_window, text=f"Failed to load image: {str(e)}", 
                                  style="danger.TLabel")
            error_label.pack(pady=20)
            print(f"Image loading error: {e}")
        finally:
            if os.path.exists("temp_img.jpg"):
                os.remove("temp_img.jpg")

    threading.Thread(target=load_image, daemon=True).start()

def main():
root = tk.Tk()
app = ModernCRUDApp(root)
root.mainloop()

if name == “main“:
main()

Related Posts

馃悕 How to Use Tkinter Listbox to Add and Print Selected Items in Python

馃悕 How to Use Tkinter Listbox to Add and Print Selected Items in Python If you’re learning Tkinter, Python’s standard GUI library, and want to build a fun and interactive…

Read more

Building Car Racing Elite: A High-Graphics Pygame without any images

Want to create a thrilling car racing game with stunning visuals using Python? In this step-by-step Pygame tutorial, I鈥檒l guide you through building “Car Racing Elite”鈥攁 modern racing game with…

Read more

Building a Modern Menu Viewer with Python and Tkinter: A Step-by-Step Guide

Morden Menu Viewer with Python Looking to build a modern desktop application with Python? In this detailed guide, I鈥檒l walk you through creating a sleek menu viewer using Python鈥檚 Tkinter…

Read more

馃幆 Title: Build a Modern Calculator Using Python Tkinter

Here鈥檚 a complete blog post explaining how to build a modern calculator using Python Tkinter from scratch. This will cover everything from setting up Tkinter to customizing the UI for…

Read more

Jumping Jack Game with Python and Pygame

Let’s build a simple Jumping Jack game using Pygame, a popular library for game development in Python. This game will feature basic mechanics like jumping, gravity, and obstacle dodging, with…

Read more

Flappy Bird clone using Pygame

Let’s build a simple Flappy Bird clone using Pygame, a popular library for game development in Python. Here’s a basic structure for the game: Step 1: Install Pygame Make sure…

Read more

Leave a Reply

Your email address will not be published. Required fields are marked *