DEV Community

Mate Technologies
Mate Technologies

Posted on

Build a Professional Screenshot Tool in Python (Tkinter + ttkbootstrap)

Sometimes you just need a simple, fast screenshot tool without installing large software.

In this tutorial, we’ll build a desktop screen capture application using Python.

Features of our tool:

β€’ πŸ“Έ Full screen capture
β€’ πŸ–Ό Region capture (drag to select area)
β€’ ⏱ Delayed screenshots
β€’ πŸ“‚ Custom output folder
β€’ πŸ“Š Progress indicator
β€’ 🧾 Processing log

The final tool looks and behaves like a mini professional desktop application.

You can also find the full project here:

GitHub repository

https://github.com/rogers-cyber/python-tiny-tools/tree/main/76-Screenshot%20tool

Step 1 β€” Install Required Libraries

Before we start, install the required Python packages.

pip install pillow ttkbootstrap
Enter fullscreen mode Exit fullscreen mode

Libraries used in this project:

Library Purpose
tkinter GUI framework
ttkbootstrap modern UI theme
pillow screen capture
threading background tasks
Step 2 β€” Import Required Modules

Now let’s import the modules we’ll use.

import os
import sys
import threading
import time
import traceback
from queue import Queue, Empty

import tkinter as tk
from tkinter import filedialog, messagebox

import ttkbootstrap as tb
from ttkbootstrap.constants import *

from PIL import ImageGrab
Enter fullscreen mode Exit fullscreen mode

What these modules do

β€’ threading β†’ prevents the UI from freezing
β€’ queue β†’ safely communicate between threads
β€’ ImageGrab β†’ capture screenshots
β€’ ttkbootstrap β†’ modern dark UI theme

Step 3 β€” Application Configuration

Let’s define the application name and version.

APP_NAME = "Screenshot PRO"
APP_VERSION = "1.0.0"
Enter fullscreen mode Exit fullscreen mode

These values will be used in the window title and About dialog.

Step 4 β€” Create the Main Window

Next we initialize the Tkinter application window.

app = tk.Tk()
app.title(f"{APP_NAME} {APP_VERSION}")
app.geometry("1050x620")

tb.Style("darkly")
Enter fullscreen mode Exit fullscreen mode

What happens here

β€’ Creates the main GUI window
β€’ Sets window size
β€’ Applies the Darkly theme from ttkbootstrap

This makes the interface look much more professional than default Tkinter.

Step 5 β€” Utility Functions

Let’s add some helper functions.

def resource_path(file_name):
    base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, file_name)
Enter fullscreen mode Exit fullscreen mode

This function helps when packaging the app with PyInstaller.

Error Logger

def log_error():
    with open("error.log", "a", encoding="utf-8") as f:
        f.write(traceback.format_exc() + "\n")
Enter fullscreen mode Exit fullscreen mode

If something crashes, it writes the error to error.log.

About Dialog

def show_about():
    messagebox.showinfo(
        f"About {APP_NAME}",
        f"{APP_NAME} v{APP_VERSION}\n\n"
        "Professional Screen Capture Tool\n\n"
        "Features:\n"
        "β€’ Full screen capture\n"
        "β€’ Region capture\n"
        "β€’ Delayed screenshots\n"
        "β€’ Custom output folder\n"
        "β€’ Progress tracking\n"
        "β€’ Processing log\n"
    )
Enter fullscreen mode Exit fullscreen mode

This displays a simple About window.

Step 6 β€” Create the Application Menu

Next we add a top menu.

menubar = tb.Menu(app)

help_menu = tb.Menu(menubar, tearoff=0)
help_menu.add_command(label="About", command=show_about)

menubar.add_cascade(label="Help", menu=help_menu)

app.config(menu=menubar)
Enter fullscreen mode Exit fullscreen mode

Now the app has a Help β†’ About menu.

Step 7 β€” Application State Variables

We define variables used across the application.

stop_flag = False
pause_flag = False

ui_queue = Queue()

output_path = tb.StringVar(value=os.getcwd())
delay_seconds = tb.IntVar(value=0)
Enter fullscreen mode Exit fullscreen mode

Why use StringVar and IntVar?

They automatically sync values with the GUI inputs.

Step 8 β€” Title Header

Now let’s create the title section.

tb.Label(
    app,
    text=APP_NAME,
    font=("Segoe UI", 24, "bold")
).pack(pady=(10, 2))

tb.Label(
    app,
    text="Professional Screen Capture Tool",
    font=("Segoe UI", 10, "italic"),
    foreground="#9ca3af"
).pack(pady=(0, 10))
Enter fullscreen mode Exit fullscreen mode

This creates the main application header.

Step 9 β€” Capture Controls Panel

We create a labeled frame for screenshot settings.

frame1 = tb.Labelframe(app, text="Capture Controls", padding=10)
frame1.pack(fill="x", padx=10, pady=6)
Delay Setting
tb.Label(frame1, text="Delay (seconds):").pack(side="left")

delay_entry = tb.Entry(frame1, textvariable=delay_seconds, width=6)
delay_entry.pack(side="left", padx=5)
Enter fullscreen mode Exit fullscreen mode

This allows users to delay screenshots.

Example:

Delay = 5 seconds

Useful for capturing menus or popups.

Output Folder Selection

tb.Label(frame1, text="Output Folder:", width=13).pack(side="left", padx=(20, 0))

tb.Entry(frame1, textvariable=output_path, width=40).pack(side="left", padx=6)
Browse Folder Button
def browse_output():
    folder = filedialog.askdirectory()
    if folder:
        output_path.set(folder)

tb.Button(frame1, text="Browse", command=browse_output).pack(side="left", padx=4)
Enter fullscreen mode Exit fullscreen mode

Users can select where screenshots are saved.

Step 10 β€” Full Screen Capture

Now we create the function that captures the entire screen.

def capture_fullscreen():

    try:

        if delay_seconds.get() > 0:
            ui_queue.put(("log", f"Waiting {delay_seconds.get()} seconds..."))
            time.sleep(delay_seconds.get())

        img = ImageGrab.grab()

        file_name = f"screenshot_{int(time.time())}.png"

        path = os.path.join(output_path.get(), file_name)

        img.save(path)

        ui_queue.put(("log", f"βœ” Saved: {file_name}"))

    except Exception:
        log_error()
        ui_queue.put(("log", "❌ Screenshot failed"))

    ui_queue.put(("progress", 100))
Enter fullscreen mode Exit fullscreen mode

What this function does

1️⃣ Waits if delay is set
2️⃣ Captures screen
3️⃣ Saves image
4️⃣ Updates UI log

Step 11 β€” Region Capture Tool

Now we create the drag-to-select screenshot feature.

def capture_region():

    selector = tk.Toplevel()
    selector.attributes("-fullscreen", True)
    selector.attributes("-alpha", 0.3)
    selector.configure(bg="black")

    canvas = tk.Canvas(selector, cursor="cross", bg="black")
    canvas.pack(fill="both", expand=True)
Enter fullscreen mode Exit fullscreen mode

This creates a transparent overlay window.

Mouse Events

start_x = start_y = 0
rect = None
Enter fullscreen mode Exit fullscreen mode

These store the coordinates of the selection.

Mouse Press

def on_press(event):
    nonlocal start_x, start_y, rect
    start_x = event.x
    start_y = event.y
    rect = canvas.create_rectangle(start_x, start_y, start_x, start_y, outline="red", width=2)
Enter fullscreen mode Exit fullscreen mode

Start drawing the selection box.

Mouse Drag

def on_drag(event):
    canvas.coords(rect, start_x, start_y, event.x, event.y)
Enter fullscreen mode Exit fullscreen mode

Updates the rectangle size.

Mouse Release

def on_release(event):

    x1 = min(start_x, event.x)
    y1 = min(start_y, event.y)
    x2 = max(start_x, event.x)
    y2 = max(start_y, event.y)

    selector.destroy()

    img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
Enter fullscreen mode Exit fullscreen mode

This captures the selected area.

Step 12 β€” Start Screenshot Threads

To prevent the UI from freezing we use threads.

def start_fullscreen():
    progress_var.set(0)
    threading.Thread(target=capture_fullscreen, daemon=True).start()
Enter fullscreen mode Exit fullscreen mode

Step 13 β€” Screenshot Buttons

tb.Button(
    frame1,
    text="πŸ“Έ Full Screen",
    bootstyle="success",
    command=start_fullscreen
).pack(side="left", padx=6)

tb.Button(
    frame1,
    text="πŸ–Ό Capture Region",
    bootstyle="info",
    command=start_region
).pack(side="left", padx=4)
Enter fullscreen mode Exit fullscreen mode

Now users can choose:

β€’ Full screen capture
β€’ Region capture

Step 14 β€” Progress Bar

frame2 = tb.Labelframe(app, text="Progress", padding=8)
frame2.pack(fill="x", padx=10)

progress_var = tb.IntVar()

tb.Progressbar(
    frame2,
    variable=progress_var,
    maximum=100,
    length=400
).pack(side="left", padx=10)
Enter fullscreen mode Exit fullscreen mode

This shows screenshot progress.

Step 15 β€” Processing Log

frame3 = tb.Labelframe(app, text="Processing Log", padding=8)
frame3.pack(fill="both", expand=True, padx=10, pady=6)

log_text = tk.Text(frame3, height=10)
log_text.pack(side="left", fill="both", expand=True)
Enter fullscreen mode Exit fullscreen mode

The log shows:

βœ” Screenshot saved
❌ Screenshot failed
Waiting 5 seconds...
Step 16 β€” UI Queue Processor

Background threads communicate with the UI using a queue.

def process_ui_queue():

    try:

        while True:

            cmd, data = ui_queue.get_nowait()

            if cmd == "progress":
                progress_var.set(data)

            elif cmd == "log":

                log_text.config(state="normal")
                log_text.insert("end", data + "\n")
                log_text.see("end")
                log_text.config(state="disabled")

    except Empty:
        pass

    app.after(100, process_ui_queue)
Enter fullscreen mode Exit fullscreen mode

This keeps the UI responsive and thread-safe.

Step 17 β€” Start the Application

Finally, we start the GUI.

app.after(100, process_ui_queue)

app.mainloop()
Enter fullscreen mode Exit fullscreen mode

Final Result

You now have a fully functional desktop screenshot tool built with Python.

Capabilities:

βœ” Full screen capture
βœ” Region capture
βœ” Delayed screenshots
βœ” Modern UI
βœ” Progress tracking
βœ” Log output

Source Code

Full project:

https://github.com/rogers-cyber/python-tiny-tools/tree/main/76-Screenshot%20tool

Top comments (0)