Python is often praised for its data science ecosystem, but you don’t always need Pandas or NumPy to build something useful.
In this post, we’ll build StatMate — a clean, modern desktop statistics calculator using:
🪟 Tkinter (Python’s standard GUI library)
🎨 sv-ttk for modern theming
📊 Python’s built-in statistics module
By the end, you’ll have a polished GUI app that calculates:
Mean
Median
Mode
Variance
Standard Deviation
Min / Max
with real-time input validation and configurable decimal precision.
✨ Features
Modern light theme using sv-ttk
Real-time input validation
Placeholder text UX
Adjustable decimal precision
Clean status bar feedback
Graceful error handling
📦 Requirements
Install the only external dependency:
pip install sv-ttk
Everything else comes from Python’s standard library.
🧠 Design Overview
The app is structured into logical sections:
Helpers
Formatting
Status updates
Validation
Regex-based numeric input filtering
Statistics Logic
Uses Python’s statistics module
UI Layout
Cards, labels, buttons, and result panel
UX Enhancements
Placeholder text
Status bar
Precision control
🧮 How the Calculation Works
The user enters numbers as a comma-separated list:
1, 2, 3, 4, 5
We:
Parse and sanitize the input
Convert values to floats
Compute statistics safely
Format output based on selected precision
Edge cases (like no unique mode or a single value) are handled gracefully.
🧑💻 Full Source Code
import tkinter as tk
from tkinter import ttk, messagebox
import sv_ttk
import statistics
import re
# =========================
# Helpers
# =========================
def set_status(msg):
status_var.set(msg)
root.update_idletasks()
def fmt(value):
try:
p = precision_var.get()
return f"{value:.{p}f}"
except Exception:
return str(value)
def calculate_stats():
raw_input = numbers_entry.get()
if raw_input.strip() == 'e.g., 1, 2, 3, 4':
messagebox.showerror("Invalid Input", "Please enter a valid list of numbers.")
return
try:
numbers = [float(x.strip()) for x in raw_input.split(",") if x.strip() != ""]
if not numbers:
raise ValueError
mean_val = statistics.mean(numbers)
median_val = statistics.median(numbers)
try:
mode_val = statistics.mode(numbers)
except statistics.StatisticsError:
mode_val = "No unique mode"
variance_val = statistics.variance(numbers) if len(numbers) > 1 else 0
stdev_val = statistics.stdev(numbers) if len(numbers) > 1 else 0
result_var.set(
f"📊 Mean: {fmt(mean_val)}\n"
f"📌 Median: {fmt(median_val)}\n"
f"🎯 Mode: {mode_val}\n"
f"📈 Variance: {fmt(variance_val)}\n"
f"📉 Std. Dev.: {fmt(stdev_val)}\n"
f"🔽 Min: {fmt(min(numbers))}\n"
f"🔼 Max: {fmt(max(numbers))}"
)
set_status("✅ Statistics calculated successfully")
except ValueError:
messagebox.showerror("Invalid Input", "Please enter a valid list of numbers.")
def validate_input(new_value):
if new_value == '' or new_value == 'e.g., 1, 2, 3, 4':
return True
return re.match(r'^[0-9.,\s-]*$', new_value) is not None
# =========================
# App Setup
# =========================
root = tk.Tk()
root.title("StatMate - Full Statistics Tool")
root.geometry("950x650")
root.minsize(950, 650)
sv_ttk.set_theme("light")
precision_var = tk.IntVar(value=4)
status_var = tk.StringVar(value="Ready")
ttk.Label(root, textvariable=status_var, anchor="w",
font=("Segoe UI", 10), padding=(10, 5)
).pack(side="bottom", fill="x")
main_frame = ttk.Frame(root, padding=20)
main_frame.pack(expand=True, fill="both")
ttk.Label(main_frame, text="StatMate",
font=("Segoe UI", 28, "bold")
).pack(anchor="w")
ttk.Label(main_frame, text="Full Statistics Calculator",
font=("Segoe UI", 14), foreground="#555"
).pack(anchor="w", pady=(0, 20))
input_card = ttk.LabelFrame(main_frame, text="Input Numbers & Precision", padding=20)
input_card.pack(fill="x", pady=(0, 20))
input_row = ttk.Frame(input_card)
input_row.pack(fill="x")
def on_entry_click(event):
if numbers_entry.get() == 'e.g., 1, 2, 3, 4':
numbers_entry.delete(0, "end")
numbers_entry.config(fg='black')
def on_focusout(event):
if numbers_entry.get() == '':
numbers_entry.insert(0, 'e.g., 1, 2, 3, 4')
numbers_entry.config(fg='grey')
ttk.Label(input_row, text="Numbers:").grid(row=0, column=0)
numbers_entry = tk.Entry(input_row, font=("Segoe UI", 14), width=40, fg='grey')
numbers_entry.insert(0, 'e.g., 1, 2, 3, 4')
numbers_entry.bind('<FocusIn>', on_entry_click)
numbers_entry.bind('<FocusOut>', on_focusout)
numbers_entry.grid(row=0, column=1, padx=(5, 20))
vcmd = (root.register(validate_input), '%P')
numbers_entry.config(validate='key', validatecommand=vcmd)
ttk.Label(input_row, text="Decimal Precision:").grid(row=0, column=2)
ttk.Spinbox(input_row, from_=0, to=10,
textvariable=precision_var, width=5
).grid(row=0, column=3, padx=(5, 0))
ttk.Button(main_frame, text="📊 Calculate Statistics",
command=calculate_stats,
style="Accent.TButton"
).pack(pady=20, ipadx=10, ipady=10)
result_card = ttk.LabelFrame(main_frame, text="Results", padding=20)
result_card.pack(fill="both", expand=True)
result_var = tk.StringVar(value="—")
tk.Label(result_card, textvariable=result_var,
font=("Segoe UI", 16, "bold"),
justify="left", anchor="nw",
bd=2, relief="groove", padx=10, pady=10
).pack(fill="both", expand=True)
root.mainloop()
🚀 Possible Improvements
Export results to CSV
Add histogram visualization
Dark mode toggle
Keyboard shortcuts
Save/load datasets
🎯 Final Thoughts
Tkinter doesn’t have to look outdated. With a bit of structure, validation, and theming, you can build clean, professional desktop apps using only Python.
If you enjoyed this project, consider extending it—or turning it into a reusable statistics toolkit.
Happy building! 🧠📊

Top comments (0)