In this tutorial, we will create a simple graphical application using Python’s tkinter
and Pillow
libraries. This application will allow users to load an image, draw a circle on it, and then crop the image to the circle. The application will also enable users to save the cropped image.
Prerequisites
Before you begin, ensure you have the necessary libraries installed. You can install them using pip if they’re not already installed:
pip install pillow
Overview
Our application will have the following features:
- Load an Image: Allows users to select an image file.
- Draw and Move Circle: Users can draw and move a circle on the image.
- Save Cropped Image: Saves the image cropped to the circle’s area.
Step-by-Step Guide
1. Import Required Libraries
We need tkinter
for the GUI, Pillow
(PIL) for image manipulation, and some additional libraries for encoding and decoding image data.
import tkinter as tk
from tkinter import filedialog, Canvas
from PIL import Image, ImageDraw, ImageTk
import math
import base64
from io import BytesIO
2. Define the Main Application Class
Create a class CircleCropApp
that encapsulates the functionality of the application.
class CircleCropApp:
def __init__(self, root):
self.root = root
self.root.title("Circle Crop Tool")
self.set_icon()
self.button_frame = tk.Frame(root)
self.button_frame.pack(side=tk.TOP, fill=tk.X)
self.load_button = tk.Button(self.button_frame, text="Load Image", command=self.load_image)
self.load_button.pack(side=tk.LEFT)
self.save_button = tk.Button(self.button_frame, text="Save Image", command=self.save_image)
self.save_button.pack(side=tk.LEFT)
self.canvas = Canvas(root, width=800, height=600)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.image = None
self.image_id = None
self.circle_id = None
self.circle_center = (0, 0)
self.circle_radius = 0
self.drawing = False
self.moving = False
self.canvas.bind("<Button-1>", self.on_click)
self.canvas.bind("<B1-Motion>", self.move_circle)
self.canvas.bind("<ButtonRelease-1>", self.finish_moving)
3. Set the Application Icon
You can set a custom icon for the window using a base64-encoded image string.
def set_icon(self):
base64_icon = """
AAABAAEAAAA==
"""
icon_data = base64.b64decode(base64_icon)
icon_image = Image.open(BytesIO(icon_data))
icon_image = icon_image.resize((16, 16), Image.LANCZOS)
self.icon_photo = ImageTk.PhotoImage(icon_image)
self.root.iconphoto(False, self.icon_photo)
4. Load and Display Image
Implement functionality to load and display an image on the canvas.
def load_image(self):
file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg;*.png")])
if file_path:
self.image = Image.open(file_path).convert("RGBA")
self.display_image()
def display_image(self):
self.tk_image = ImageTk.PhotoImage(self.image)
self.canvas.config(width=self.image.width, height=self.image.height)
if self.image_id:
self.canvas.delete(self.image_id)
self.image_id = self.canvas.create_image(self.image.width // 2, self.image.height // 2, anchor=tk.CENTER, image=self.tk_image)
5. Handle Circle Drawing and Moving
Handle mouse events to draw a circle and move it.
def on_click(self, event):
if self.is_within_circle(event.x, event.y):
self.moving = True
self.offset_x = event.x - self.circle_center[0]
self.offset_y = event.y - self.circle_center[1]
else:
self.drawing = True
self.circle_center = (event.x, event.y)
self.circle_radius = 0
self.draw_circle()
def move_circle(self, event):
if self.moving:
self.circle_center = (event.x - self.offset_x, event.y - self.offset_y)
self.draw_circle()
elif self.drawing:
self.circle_radius = int(math.sqrt((event.x - self.circle_center[0]) ** 2 + (event.y - self.circle_center[1]) ** 2))
self.draw_circle()
def finish_moving(self, event):
self.moving = False
self.drawing = False
6. Draw the Circle on the Canvas
Redraw the circle based on user actions.
def draw_circle(self):
if self.circle_id:
self.canvas.delete(self.circle_id)
if self.circle_radius > 0:
x, y = self.circle_center
self.circle_id = self.canvas.create_oval(
x - self.circle_radius, y - self.circle_radius,
x + self.circle_radius, y + self.circle_radius,
outline='red', width=2
)
def is_within_circle(self, x, y):
cx, cy = self.circle_center
return (x - cx) ** 2 + (y - cy) ** 2 <= self.circle_radius ** 2
7. Save the Cropped Image
Implement the functionality to save the cropped image.
def save_image(self):
if self.image:
result = Image.new("RGBA", self.image.size, (0, 0, 0, 0))
mask = Image.new("L", self.image.size, 0)
draw = ImageDraw.Draw(mask)
x, y = self.circle_center
draw.ellipse((x - self.circle_radius, y - self.circle_radius,
x + self.circle_radius, y + self.circle_radius), fill=255)
result.paste(self.image, (0, 0), mask)
left = x - self.circle_radius
upper = y - self.circle_radius
right = x + self.circle_radius
lower = y + self.circle_radius
result = result.crop((left, upper, right, lower))
save_path = filedialog.asksaveasfilename(defaultextension=".png",
filetypes=[("PNG files", "*.png")])
if save_path:
result.save(save_path)
8. Run the Application
Finally, set up the main function to run the application.
if __name__ == "__main__":
root = tk.Tk()
app = CircleCropApp(root)
root.mainloop()
Packaging Your Application
To distribute your application as a standalone executable, you can use PyInstaller. Here’s how you can package your script into a single executable file:
Install PyInstaller: If you haven’t already installed PyInstaller, you can do so with pip:
pip install pyinstaller
Create the Executable: Use the following command to package your Python script into a standalone executable. Replace icon.ico with the path to your icon file if you have one, and ImageCrop.py with the name of your Python script:
pyinstaller --onefile --icon=icon.ico ImageCrop.py --windowed
--onefile
bundles everything into a single executable.--icon=icon.ico
sets the icon for your application.--windowed
prevents a terminal window from opening alongside your GUI application on Windows.
After running the command, PyInstaller will generate the executable in the dist directory. You can now distribute this standalone executable to users who don’t need to have Python installed.
Conclusion
In this tutorial, we’ve built a simple circle cropping tool using Python’s tkinter
and Pillow
libraries. This tool allows users to load an image, draw a circle on it, and save the cropped result. You can further enhance this tool by adding features such as resizing the circle, or by improving the user interface. Happy coding!