In this tutorial, weβll build a desktop Email Validation Tool using Python + Tkinter.
By the end, your app will:
Validate email format using regex
Check if the domain has MX records (can receive email)
Keep a history of validated emails
Export results to a .txt file
Support light/dark mode
Stay responsive using threading
No advanced GUI knowledge required β weβll go step by step.
π¦ Step 1: Import Required Libraries
Letβs start by importing everything we need.
import sys
import os
import re
import threading
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import dns.resolver
import sv_ttk
What these are for:
tkinter / ttk β GUI components
re β email format validation
threading β prevent UI freezing
dns.resolver β MX record lookup
sv_ttk β modern Tkinter theme
π Install missing packages:
pip install dnspython sv-ttk
π Step 2: Helper Functions
Resolve file paths (useful for packaging later)
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)
Status bar updates
def set_status(msg):
status_var.set(msg)
root.update_idletasks()
This keeps users informed while background tasks run.
π₯ Step 3: Create the Main Application Window
root = tk.Tk()
root.title("Email Validation Tool")
root.geometry("720x680")
sv_ttk.set_theme("light")
Creates the main window
Sets size and title
Applies a modern light theme
π Step 4: Global State Variables
dark_mode_var = tk.BooleanVar(value=False)
email_var = tk.StringVar()
validation_result_var = tk.StringVar(value="Result: β")
email_history = []
These variables help us:
Track dark mode
Read email input
Display validation results
Store history
π Step 5: Dark Mode Toggle
def toggle_theme():
bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
fg = "white" if dark_mode_var.get() else "black"
root.configure(bg=bg)
for w in ["TFrame", "TLabel", "TLabelframe",
"TLabelframe.Label", "TCheckbutton"]:
style.configure(w, background=bg, foreground=fg)
email_entry.configure(background=bg, foreground=fg)
This manually updates widget colors when dark mode is toggled.
π§ Step 6: Email Validation Logic
6.1 Check Email Format (Regex)
def is_valid_email_format(email):
regex = r"^[\w\.-]+@[\w\.-]+\.\w+$"
return re.match(regex, email) is not None
This ensures the email looks valid.
6.2 Check Domain MX Records
def has_mx_record(domain):
try:
records = dns.resolver.resolve(domain, 'MX')
return bool(records)
except Exception:
return False
MX records indicate whether a domain can receive email.
6.3 Start Validation (UI Thread)
def validate_email():
email = email_var.get().strip()
if not email:
messagebox.showwarning("Error", "Please enter an email address.")
return
set_status("Validating email...")
threading.Thread(
target=_validate_email_thread,
args=(email,),
daemon=True
).start()
We run validation in a background thread so the UI doesnβt freeze.
6.4 Validation Worker Thread
def _validate_email_thread(email):
if not is_valid_email_format(email):
validation_result_var.set("Result: β Invalid format")
set_status("Validation complete")
return
domain = email.split("@")[1]
if has_mx_record(domain):
validation_result_var.set("Result: β
Valid email")
else:
validation_result_var.set("Result: β Domain may not receive emails")
add_to_history(email)
set_status("Validation complete")
π Step 7: Email History Management
Add to history list
def add_to_history(email):
email_history.append(email)
history_list.insert(tk.END, f"{len(email_history)} β’ {email}")
Export History to .txt
def export_history_txt():
if not email_history:
messagebox.showinfo("Empty History", "No emails to export.")
return
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Text Files", "*.txt")],
title="Export Email History"
)
if not file_path:
return
try:
with open(file_path, "w", encoding="utf-8") as f:
f.write("Email Validation History\n")
f.write("=" * 30 + "\n\n")
for i, email in enumerate(email_history, 1):
f.write(f"{i}. {email}\n")
set_status("Email history exported")
messagebox.showinfo("Export Successful", "Email history saved successfully.")
except Exception as e:
messagebox.showerror("Export Failed", str(e))
π¨ Step 8: Styling
style = ttk.Style()
style.theme_use("clam")
style.configure("Action.TButton",
font=("Segoe UI", 11, "bold"),
padding=8)
Creates bold action buttons.
π Step 9: Status Bar
status_var = tk.StringVar(value="Ready")
ttk.Label(
root,
textvariable=status_var,
anchor="w"
).pack(side=tk.BOTTOM, fill="x")
π§± Step 10: Build the UI Layout
Main Container
main = ttk.Frame(root, padding=20)
main.pack(expand=True, fill="both")
Title & Input
ttk.Label(
main,
text="Email Validation Tool",
font=("Segoe UI", 22, "bold")
).pack()
email_entry = ttk.Entry(
main,
textvariable=email_var,
font=("Segoe UI", 14),
justify="center"
)
email_entry.pack(fill="x", pady=8)
Result Display
ttk.Label(
main,
textvariable=validation_result_var,
font=("Segoe UI", 12, "bold")
).pack(pady=4)
π Step 11: Controls
controls = ttk.Frame(main)
controls.pack(pady=8)
ttk.Button(
controls,
text="β
Validate",
command=validate_email,
style="Action.TButton"
).pack(side="left", padx=4)
ttk.Button(
controls,
text="π€ Export History",
command=export_history_txt,
style="Action.TButton"
).pack(side="left", padx=4)
π Step 12: Email History Vault
vault = ttk.LabelFrame(
main,
text="Email History Vault",
padding=10
)
vault.pack(fill="both", expand=True, pady=10)
history_list = tk.Listbox(
vault,
font=("Segoe UI", 10),
height=10
)
history_list.pack(fill="both", expand=True)
β Step 13: App Options
ttk.Checkbutton(
main,
text="Dark Mode",
variable=dark_mode_var,
command=toggle_theme
).pack(pady=6)
βΆ Step 14: Run the App
root.mainloop()
This starts the Tkinter event loop and launches your application.
π Final Thoughts
You now have a fully functional email validation desktop app with:
Regex validation
MX record checking
Multithreading
History tracking
Export functionality
Dark mode

Top comments (0)