In this tutorial, we’ll build SpamSentinel, a fast email spam detector with a GUI using Python. This tool can scan .eml and .txt email files, calculate a spam score, and help you manage spam emails effectively.
We’ll cover:
Setting up the environment
Creating utility functions
Building the spam detection worker
Designing the GUI
Adding file selection and drag & drop
Connecting the worker to the GUI
Exporting results
Running the application
- Setup: Install required libraries
We’ll need:
tkinter (built-in)
ttkbootstrap (for modern UI)
tkinterdnd2 (optional, for drag & drop support)
Install the extras using pip:
pip install ttkbootstrap
pip install tkinterdnd2
- Utility function
We need a helper to get resource paths, especially for icons when packaged with PyInstaller.
import os
import sys
def resource_path(file_name):
"""
Get the path to a resource, works with PyInstaller.
"""
base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, file_name)
Explanation:
This function ensures your icon or other files can be found whether running as a script or a packaged executable.
- Spam detection worker
We’ll create a class to handle scanning emails and calculating a spam score.
import re
from collections import Counter, deque
class SpamWorker:
def __init__(self, files, min_confidence, include_words, exclude_words, regex_pattern, max_results, callbacks):
self.files = files
self.min_confidence = min_confidence
self.include_words = include_words
self.exclude_words = exclude_words
self.regex_pattern = re.compile(regex_pattern, re.IGNORECASE) if regex_pattern else None
self.max_results = max_results
self.callbacks = callbacks
self._running = True
self.spam_patterns = [
re.compile(r"(free money|win cash|click here|urgent|lottery)", re.I),
re.compile(r"(prize|offer|risk-free|credit card)", re.I),
]
def stop(self):
self._running = False
def spam_score(self, text):
score = 0
for pattern in self.spam_patterns:
if pattern.search(text):
score += 50
return min(score, 100)
Explanation:
spam_score calculates a fake spam score for demonstration.
We use regex patterns to detect typical spam keywords.
stop() allows the process to be canceled.
- Running the spam worker
We now scan files and calculate scores:
def run(self):
total_files = len(self.files)
counters = Counter()
results_buffer = deque(maxlen=self.max_results)
for i, path in enumerate(self.files):
if not self._running:
break
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
if self.include_words and not any(w in content.lower() for w in self.include_words):
continue
if self.exclude_words and any(w in content.lower() for w in self.exclude_words):
continue
if self.regex_pattern and not self.regex_pattern.search(content):
continue
score = self.spam_score(content)
if score < self.min_confidence:
continue
counters["SPAM"] += 1
counters["TOTAL"] += 1
results_buffer.append((path, score))
if "found" in self.callbacks:
self.callbacks["found"](path, score)
if counters["TOTAL"] >= self.max_results:
break
except Exception:
pass
if total_files > 0 and "progress" in self.callbacks:
self.callbacks["progress"](int((i + 1) / total_files * 100))
if "stats" in self.callbacks:
self.callbacks["stats"](dict(counters))
if "stats" in self.callbacks:
self.callbacks["stats"](dict(counters))
if "finished" in self.callbacks:
self.callbacks["finished"]()
Explanation:
Loops over files and calculates spam scores.
Supports include/exclude word filters and optional regex.
Reports progress and statistics via callbacks for the GUI.
- GUI: Setup with ttkbootstrap
We’ll use ttkbootstrap for a modern dark-themed GUI.
import tkinter as tk
import ttkbootstrap as tb
from ttkbootstrap.constants import *
class SpamSentinelApp:
APP_NAME = "SpamSentinel"
APP_VERSION = "1.0"
def __init__(self):
self.root = tb.Window(themename="darkly")
self.root.title(f"{self.APP_NAME} v{self.APP_VERSION}")
self.root.minsize(1200, 700)
self._build_ui()
def _build_ui(self):
main = tb.Frame(self.root, padding=10)
main.pack(fill=tk.BOTH, expand=True)
tb.Label(main, text=f"🧠 {self.APP_NAME} - Enterprise Spam Detector",
font=("Segoe UI", 22, "bold")).pack(pady=(0, 4))
Explanation:
tb.Window(themename="darkly") creates a dark-themed window.
Label displays the app name and description.
- File selection & drag & drop
We allow users to select folders or drag & drop files:
from tkinter import filedialog
try:
from tkinterdnd2 import TkinterDnD, DND_FILES
DND_ENABLED = True
except ImportError:
DND_ENABLED = False
def browse_files(self):
folder = filedialog.askdirectory(title="Select Email Folder")
if folder:
print("Folder selected:", folder)
Explanation:
TkinterDnD enables drag & drop.
filedialog.askdirectory allows folder selection.
- Connecting GUI & Worker
We connect buttons to start scanning and update the GUI with results:
import threading
def start_scan(self):
selected_files = ["emails/test1.eml", "emails/test2.txt"] # Example
min_conf = 50
self.worker_obj = SpamWorker(
selected_files,
min_conf,
include_words=[],
exclude_words=[],
regex_pattern="",
max_results=1000,
callbacks={
"found": lambda f,s: print(f"{f} -> {s}%"),
"progress": lambda p: print(f"Progress: {p}%"),
"stats": lambda stats: print(stats),
"finished": lambda: print("Scan finished")
}
)
threading.Thread(target=self.worker_obj.run, daemon=True).start()
Explanation:
Uses a separate thread to avoid freezing the GUI.
Updates are sent back to the GUI via callbacks.
- Exporting results
We allow users to save selected results:
def export_results(self, results):
path = filedialog.asksaveasfilename(defaultextension=".txt")
if path:
with open(path, "w", encoding="utf-8") as f:
for r in results:
f.write(f"{r[0]} | Spam Score: {r[1]}%\n")
print("Export completed!")
Explanation:
Saves selected files with their spam score to a text file.
- Run the App
Finally, run the application:
if __name__ == "__main__":
app = SpamSentinelApp()
app.root.mainloop()
Explanation:
Starts the GUI loop.
Users can now drag & drop emails, scan them, and export results.
GitHub Link
You can clone the full project here:
https://github.com/rogers-cyber/python-tiny-tools/tree/main/Spam-email-detector-GUI
This structure makes it beginner-friendly, because each part is explained and separated into small, digestible steps with code blocks.

Top comments (0)