DEV Community

Mate Technologies
Mate Technologies

Posted on

🎨 Building a Professional AI Art Generator with Python and Tkinter

In this tutorial, we’ll build ArtForge, a class-based, production-ready AI art generator GUI that uses OpenAI’s Images API. We’ll cover:

Creating the app structure

Building a modern GUI with Tkinter + ttkbootstrap

Connecting to OpenAI’s Images API

Thread-safe batch generation and logging

1️⃣ Project Setup

Before starting, make sure you have the following installed:

pip install openai pillow ttkbootstrap

Tip: ttkbootstrap is a modern styling toolkit for Tkinter. It makes GUIs look sleek without extra CSS.

Create a new Python file:

artforge.py

2️⃣ Import Required Libraries

We’ll import libraries for GUI, threading, file handling, images, and OpenAI API access.

import os
import sys
import threading
import platform
from datetime import datetime
import tkinter as tk
from tkinter import filedialog

import ttkbootstrap as tb
from ttkbootstrap.constants import *
from PIL import Image
import base64
import openai
Enter fullscreen mode Exit fullscreen mode

Explanation:

tkinter → GUI elements

ttkbootstrap → modern styling

threading → run generation without freezing the GUI

PIL → image handling

openai → connect to OpenAI Images API

3️⃣ Set Up Your OpenAI API Key

Make sure your environment variable is set:

# Linux / macOS
export OPENAI_API_KEY="your_api_key_here"

# Windows
setx OPENAI_API_KEY "your_api_key_here"
Enter fullscreen mode Exit fullscreen mode

Then, in your code:

openai.api_key = os.getenv("OPENAI_API_KEY")

Tip: Never hardcode your API key. Use environment variables for security.

4️⃣ Define the App Class

We’ll use a class-based structure for better organization:

class ArtForgeApp:
    APP_NAME = "ArtForge"
    APP_VERSION = "1.1.0"

    def __init__(self):
        # Root window
        self.root = tk.Tk()
        self.style = tb.Style(theme="darkly")
        self.root.title(f"{self.APP_NAME} v{self.APP_VERSION}")
        self.root.geometry("1100x720")
Enter fullscreen mode Exit fullscreen mode

Explanation:

self.root → main window

tb.Style(theme="darkly") → applies a dark theme

geometry → sets window size

5️⃣ App Directories & State Variables

We’ll create a folder for saving images and initialize GUI state variables:

# App directories
self.home_dir = os.path.expanduser("~")
self.app_dir = (
    os.path.join(self.home_dir, "AppData", "Local", self.APP_NAME)
    if platform.system() == "Windows"
    else os.path.join(self.home_dir, f".{self.APP_NAME.lower()}")
)
os.makedirs(self.app_dir, exist_ok=True)

# State variables
self.stop_event = threading.Event()
self.progress_var = tk.IntVar(value=0)
self.output_dir = tk.StringVar(value=self.app_dir)
Enter fullscreen mode Exit fullscreen mode

Tip: Using stop_event allows us to stop image generation safely without freezing the GUI.

6️⃣ Build the User Interface (UI)

We’ll create sections: Prompt, Settings, Log, and Controls.

Prompt Input:

prompt_frame = tb.Labelframe(self.root, text="Prompt", padding=10)
prompt_frame.pack(fill="x", padx=12, pady=6)

self.prompt_box = tk.Text(prompt_frame, height=4)
self.prompt_box.pack(fill="x", expand=True)
Enter fullscreen mode Exit fullscreen mode

Settings & Output Folder:

settings = tb.Labelframe(self.root, text="Generation Settings", padding=10)
settings.pack(fill="x", padx=12, pady=6)

self.images_var = tk.IntVar(value=1)
tb.Label(settings, text="Images").pack(side="left")
tb.Spinbox(settings, from_=1, to=10, textvariable=self.images_var, width=6).pack(side="left", padx=6)

tb.Button(
    settings, text="📁 Output Folder",
    bootstyle="secondary-outline",
    command=self.choose_output
).pack(side="right")
Enter fullscreen mode Exit fullscreen mode

Explanation:

Text → multi-line input for prompts

Spinbox → number of images to generate

Button → choose output folder

7️⃣ Logging & Progress

preview = tb.Labelframe(self.root, text="Generation Log", padding=10)
preview.pack(fill="both", expand=True, padx=12, pady=6)

self.log_view = tk.Text(preview, height=10)
self.log_view.pack(fill="both", expand=True)

self.progress = tb.Progressbar(
    self.root,
    variable=self.progress_var,
    maximum=100,
    bootstyle="success-striped"
)
self.progress.pack(fill="x", padx=12, pady=6)
Enter fullscreen mode Exit fullscreen mode

Tip: Logging helps track generation progress and errors.

8️⃣ File Dialog for Output

def choose_output(self):
    path = filedialog.askdirectory()
    if path:
        self.output_dir.set(path)
Enter fullscreen mode Exit fullscreen mode

This allows the user to select where generated images are saved.

9️⃣ Generating Images via OpenAI

def generate_image_api(self, prompt):
    response = openai.images.generate(
        model="gpt-image-1",
        prompt=prompt,
        size="512x512"
    )
    image_base64 = response.data[0].b64_json
    image_bytes = base64.b64decode(image_base64)
    return image_bytes
Enter fullscreen mode Exit fullscreen mode

Explanation:

Calls the OpenAI Images API

Returns PNG image bytes from base64

🔟 Run Batch Generation

def run_generation(self):
    prompt = self.prompt_box.get("1.0", tk.END).strip()
    if not prompt or not openai.api_key:
        self.log_view.insert(tk.END, "⚠️ Prompt or API key missing!\n")
        return

    self.stop_event.clear()
    self.progress_var.set(0)

    for i in range(self.images_var.get()):
        if self.stop_event.is_set():
            break
        try:
            img_bytes = self.generate_image_api(prompt)
        except Exception as e:
            self.log_view.insert(tk.END, f"❌ API error: {e}\n")
            break

        filename = f"art_{datetime.now().strftime('%H%M%S')}_{i+1}.png"
        filepath = os.path.join(self.output_dir.get(), filename)
        with open(filepath, "wb") as f:
            f.write(img_bytes)

        self.log_view.insert(tk.END, f"✔ Generated {filename}\n")
        self.progress_var.set(int(((i + 1) / self.images_var.get()) * 100))

    self.log_view.insert(tk.END, "✅ Generation complete\n")
Enter fullscreen mode Exit fullscreen mode

Thread-safe: Run run_generation in a separate thread to keep UI responsive.

1️⃣1️⃣ Stop Generation Button

def stop_generation(self):
    self.stop_event.set()
    self.log_view.insert(tk.END, "⛔ Generation stopped by user\n")
Enter fullscreen mode Exit fullscreen mode

This allows safely stopping long-running image batches.

1️⃣2️⃣ About Window

def show_about(self):
    win = tb.Toplevel(self.root)
    win.title(f"{self.APP_NAME} v{self.APP_VERSION} – About")
    win.geometry("500x380")
    tb.Label(win, text=f"{self.APP_NAME} v{self.APP_VERSION}", font=("Segoe UI", 14, "bold")).pack()
    tb.Label(win, text="AI art generator powered by OpenAI.", wraplength=460).pack()
Enter fullscreen mode Exit fullscreen mode

Extra UI polish: users can view app info.

1️⃣3️⃣ Run the App

if __name__ == "__main__":
    app = ArtForgeApp()
    app.run()
Enter fullscreen mode Exit fullscreen mode

✅ Congrats! You now have a modern AI art generator GUI with:

Multi-image batch generation

Thread-safe UI

Progress bar and logging

Custom output folder

Darkly themed Tkinter interface

Top comments (0)