DEV Community

Mate Technologies
Mate Technologies

Posted on

Pro Image Cropper Plus: Step-by-Step Python GUI Tutorial

Learn how to build Pro Image Cropper Plus, a lightweight Python image cropping app using Tkinter, Pillow, and ttkbootstrap. This tutorial is beginner-friendly, with each section broken into small steps and explained clearly.

GitHub repo: ProImageCropperPlus

Step 1: Setup Your Environment

Make sure you have Python installed (preferably 3.8+). You’ll also need the following packages:

pip install pillow ttkbootstrap
Enter fullscreen mode Exit fullscreen mode

Pillow – For image handling.

ttkbootstrap – For modern themed Tkinter widgets.

Step 2: Import Required Modules

At the top of your Python file, import all required modules:

import sys
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import ttkbootstrap as tb
Enter fullscreen mode Exit fullscreen mode

Explanation:

sys and Path handle file paths and packaging.

tkinter is for GUI elements.

Pillow is for image opening, cropping, and displaying.

ttkbootstrap gives modern-looking widgets.

Step 3: App Configuration

Define some basic app information:

APP_NAME = "Pro Image Cropper Plus"
APP_VERSION = "2.1.0"
APP_AUTHOR = "Mate Technologies"
APP_WEBSITE = "https://matetools.gumroad.com"
Enter fullscreen mode Exit fullscreen mode

Create a helper function for resource paths:

def resource_path(name):
    base = getattr(sys, "_MEIPASS", Path(__file__).parent)
    return Path(base) / name
Enter fullscreen mode Exit fullscreen mode

This ensures your app works even when bundled with PyInstaller.

Step 4: Create the Main Window

app = tk.Tk()
style_obj = tb.Style(theme="superhero")  # Using ttkbootstrap theme

app.title(f"{APP_NAME} {APP_VERSION}")
app.geometry("1200x680")

try:
    app.iconbitmap(str(resource_path("logo.ico")))
except Exception as e:
    print("Icon error:", e)
Enter fullscreen mode Exit fullscreen mode

Explanation:

tk.Tk() initializes the main window.

ttkbootstrap.Style applies a modern theme.

iconbitmap sets the app icon (optional).

Step 5: Define Variables

image_paths = []        # Stores all selected images
current_index = 0       # Tracks current image
img = None              # PIL Image object
tk_img = None           # Tkinter-compatible image
rect_id = None          # Crop rectangle ID on canvas

# Crop properties
crop_x = tk.IntVar(master=app, value=0)
crop_y = tk.IntVar(master=app, value=0)
crop_width = tk.IntVar(master=app, value=100)
crop_height = tk.IntVar(master=app, value=100)
constrain_aspect = tk.BooleanVar(master=app, value=False)
aspect_ratio_value = tk.DoubleVar(master=app, value=1.0)
crop_status = tk.StringVar(master=app, value="Idle")
crop_output_path = tk.StringVar(master=app, value="")
Enter fullscreen mode Exit fullscreen mode

Explanation:
These variables will control:

The current image being edited

Crop rectangle position and size

Crop constraints (aspect ratio, status, output path)

Step 6: Load and Display Images

def load_image(path):
    global img, tk_img
    img = Image.open(path)
    update_canvas_image()
    reset_crop()

def update_canvas_image():
    global tk_img
    if img is None: return

    scale = zoom_scale.get()
    w, h = int(img.width*scale), int(img.height*scale)
    tk_img = ImageTk.PhotoImage(img.resize((w,h), Image.Resampling.LANCZOS), master=app)

    canvas.config(scrollregion=(0,0,w,h))
    canvas.delete("all")
    canvas.create_image(0,0,anchor="nw",image=tk_img)
Enter fullscreen mode Exit fullscreen mode

Explanation:

load_image opens the selected image.

update_canvas_image applies zoom and updates the Tkinter canvas.

Image.Resampling.LANCZOS ensures smooth scaling.

Step 7: Crop Rectangle and Handles

def draw_rectangle():
    global rect_id, handles
    if img is None: return

    x, y, w, hgt = crop_x.get(), crop_y.get(), crop_width.get(), crop_height.get()
    scale = zoom_scale.get()
    x1, y1, x2, y2 = x*scale, y*scale, (x+w)*scale, (y+hgt)*scale

    rect_id = canvas.create_rectangle(x1, y1, x2, y2, outline="red", width=2, tag="rect")
Enter fullscreen mode Exit fullscreen mode

Explanation:

This draws a red rectangle for cropping.

Later, you’ll add corner and edge handles for resizing.

Step 8: Cropping and Saving Images

def crop_image():
    if img is None or not crop_output_path.get():
        messagebox.showwarning("Save first", "Please choose save location.")
        return

    x, y, w, h = crop_x.get(), crop_y.get(), crop_width.get(), crop_height.get()
    cropped = img.crop((x, y, x+w, y+h))
    cropped.save(crop_output_path.get())
    messagebox.showinfo("Success", f"Cropped image saved:\n{crop_output_path.get()}")
Enter fullscreen mode Exit fullscreen mode

Explanation:

Uses Pillow’s crop() to select the rectangle.

Saves the cropped image to the chosen path.

Step 9: Zoom and Aspect Ratio Controls

zoom_scale = tk.DoubleVar(master=app, value=1.0)

def set_aspect(ratio):
    if img is None: return
    # Calculate crop size based on aspect ratio
    new_w = img.width
    new_h = int(new_w / ratio)
    if new_h > img.height:
        new_h = img.height
        new_w = int(new_h * ratio)

    crop_x.set((img.width-new_w)//2)
    crop_y.set((img.height-new_h)//2)
    crop_width.set(new_w)
    crop_height.set(new_h)
    aspect_ratio_value.set(ratio)
    draw_rectangle()
Enter fullscreen mode Exit fullscreen mode

Explanation:

Zooming changes how the image is displayed, not the actual image.

Aspect ratio buttons (1:1, 16:9, etc.) adjust crop rectangle proportionally.

Step 10: UI Layout

# Left Frame: Canvas
canvas_frame = tb.Frame(app)
canvas_frame.pack(side="left", fill="both", expand=True)

canvas = tk.Canvas(canvas_frame, width=600, height=600, bg="#222222")
canvas.pack(fill="both", expand=True)

# Right Frame: Controls
control_frame = tb.Frame(app)
control_frame.pack(side="right", fill="y", padx=5, pady=5)

tb.Label(control_frame, text="Zoom:").pack(anchor="w")
zoom_slider = tb.Scale(control_frame, from_=0.1, to=3.0, orient="horizontal", variable=zoom_scale, command=lambda v: update_canvas_image())
zoom_slider.pack(fill="x", pady=5)
Enter fullscreen mode Exit fullscreen mode

Explanation:

Left side contains the canvas for images.

Right side contains sliders, aspect buttons, preview, and save buttons.

Step 11: Event Handling (Drag & Resize)

canvas.bind("<ButtonPress-1>", start_drag)
canvas.bind("<B1-Motion>", drag_motion)
canvas.bind("<ButtonRelease-1>", end_drag)
canvas.tag_bind("handle","<ButtonPress-1>", start_drag_handle)
canvas.tag_bind("handle","<B1-Motion>", drag_handle)
canvas.tag_bind("handle","<ButtonRelease-1>", end_drag_handle)
Enter fullscreen mode Exit fullscreen mode

Explanation:

Allows dragging and resizing the crop rectangle.

Handles are interactive corner and edge points.

Step 12: Run the App

Finally, run the main loop:

app.mainloop()
Enter fullscreen mode Exit fullscreen mode

Explanation:

This keeps the GUI running until the user closes it.

Step 13: Try It Out

Open one or more images with Open Images.

Drag or resize the crop rectangle.

Adjust zoom and aspect ratio.

Click Save Cropped to export.

✅ This tutorial covers the core features of Pro Image Cropper Plus. For full code and extras like undo/redo, batch navigation, and live preview, check the GitHub repo:
ProImageCropperPlus

Top comments (0)