In this tutorial, we’ll walk through building a desktop image converter in Python using Tkinter, Pillow, and ttkbootstrap. The full script is available here: GitHub link
.
We’ll cover:
Project setup
Database logging
Image conversion logic
GUI creation with drag-and-drop support
License verification system
1️⃣ Project Setup and Dependencies
First, install the required packages:
pip install pillow ttkbootstrap tkinterdnd2
Then, import the required modules:
import os, sys, sqlite3, csv, time
from threading import Thread
from PIL import Image, ImageTk
Optional for better resizing quality
try:
from PIL import ImageResampling
RESAMPLE = ImageResampling.LANCZOS
except:
RESAMPLE = Image.LANCZOS
import ttkbootstrap as tb
from ttkbootstrap.constants import *
from tkinter import filedialog, messagebox, Listbox, Canvas, Scrollbar, simpledialog
from tkinterdnd2 import TkinterDnD, DND_FILES
Explanation:
Pillow handles image processing.
ttkbootstrap provides modern Tkinter themes.
tkinterdnd2 adds drag-and-drop file support.
We try to use ImageResampling.LANCZOS for better resizing but fallback if unavailable.
2️⃣ App Info and Paths
Define your app metadata and file paths:
APP_NAME = "Image Convert BUSINESS PRO"
APP_VERSION = "2.0"
BASE_DIR = os.path.dirname(sys.argv[0])
DB_NAME = os.path.join(BASE_DIR, "snapconvert.db")
OUTPUT_DIR = os.path.join(BASE_DIR, "converted")
LOG_FILE = os.path.join(BASE_DIR, "conversion.log")
LICENSE_FILE = os.path.join(BASE_DIR, "license.key")
Explanation:
This section sets up paths for your database, output folder, log file, and license file.
3️⃣ Database for Conversion History
We will log converted images into a SQLite database:
def init_db():
conn = sqlite3.connect(DB_NAME)
c = conn.cursor()
c.execute("""CREATE TABLE IF NOT EXISTS history(
id INTEGER PRIMARY KEY,
name TEXT,
original TEXT,
converted TEXT
)""")
conn.commit()
conn.close()
To insert and fetch history:
def insert_db(name, orig, conv):
conn = sqlite3.connect(DB_NAME)
c = conn.cursor()
c.execute("INSERT INTO history(name, original, converted) VALUES(?,?,?)", (name, orig, conv))
conn.commit()
conn.close()
def fetch_db():
conn = sqlite3.connect(DB_NAME)
c = conn.cursor()
c.execute("SELECT name, original, converted FROM history ORDER BY id DESC")
rows = c.fetchall()
conn.close()
return rows
Explanation:
Every converted image is logged in the database with its original and converted paths.
History is fetched in descending order to show the latest conversions first.
4️⃣ Logging and Exporting History
Keep a log file of events:
def log_event(msg):
with open(LOG_FILE, "a") as f:
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
Export history as a CSV:
def export_history_csv():
rows = fetch_db()
if not rows:
messagebox.showinfo("Export History","No history to export")
return
path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV file","*.csv")])
if path:
with open(path,"w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Name","Original Path","Converted Path"])
writer.writerows(rows)
messagebox.showinfo("Export History", f"Exported {len(rows)} records")
Explanation:
Every event is logged in a conversion.log file.
Users can export their history to a CSV for reference.
5️⃣ License System
You can secure your app using a simple license check:
VALID_LICENSES = ["PRO-2026-XYZ123", "PRO-2026-ABC789"]
def check_license():
if os.path.exists(LICENSE_FILE):
with open(LICENSE_FILE,"r") as f:
key = f.read().strip()
if key in VALID_LICENSES:
return True
key = simpledialog.askstring("License Required","Enter your license key:")
if key and key in VALID_LICENSES:
with open(LICENSE_FILE,"w") as f:
f.write(key)
messagebox.showinfo("License Activated","License activated successfully!")
return True
else:
messagebox.showerror("Invalid License","The license key is invalid. App will exit.")
return False
Explanation:
Checks for an existing license file.
Prompts the user if no valid license is found.
6️⃣ Image Conversion Worker
Core function to resize, convert, and save images:
def worker(images, fmt, out, quality, resize, keep, progress, finish):
os.makedirs(out, exist_ok=True)
total = len(images)
count = 0
for i, path in enumerate(images):
try:
with Image.open(path) as img:
if resize>0:
img = img.resize((resize,resize), RESAMPLE)
if fmt=="JPEG" and img.mode in ("RGBA","P"):
img = img.convert("RGB")
name = os.path.splitext(os.path.basename(path))[0]
if not keep:
name += f"_{i+1}"
out_path = os.path.join(out, f"{name}.{fmt.lower()}")
img.save(out_path, fmt, quality=quality if fmt=="JPEG" else None)
insert_db(name, path, out_path)
count += 1
except Exception as e:
log_event(f"Error converting {path}: {e}")
progress(int((i+1)/total*100))
finish(count)
Explanation:
Supports resizing and format conversion.
Handles JPEG transparency by converting to RGB.
Updates a progress bar and logs every conversion.
7️⃣ GUI Setup with Tkinter and ttkbootstrap
We’ll build a drag-and-drop GUI with a left file list, center gallery, and right settings panel. Example of adding files:
def add_files(self):
self.add_images(filedialog.askopenfilenames())
def add_folder(self):
folder = filedialog.askdirectory()
imgs = []
for r, _, f in os.walk(folder):
for x in f:
if x.lower().endswith(("png","jpg","jpeg","bmp","gif")):
imgs.append(os.path.join(r, x))
self.add_images(imgs)
Explanation:
Users can add single files or entire folders.
Only valid image extensions are accepted.
8️⃣ Run the App
Initialize the database and start the GUI:
if __name__=="__main__":
init_db()
App().run()
Explanation:
init_db() ensures the database exists.
App().run() launches the GUI.
✅ Full Script Reference
You can access the complete script here:
Image Convert BUSINESS PRO on GitHub

Top comments (0)