If you’ve ever needed a quick way to roll dice for tabletop games, probability experiments, or just for fun, building your own dice roller is a great weekend project.
In this post, we’ll walk through DiceForge, a polished dice-rolling simulator built with Python, Tkinter, and ttk, featuring:
A clean, modern UI
Configurable dice counts and sides
Instant roll results and totals
Light & dark mode support
A tab-based layout using ttk.Notebook
Let’s break it down.
🧰 Tech Stack
Python 3 – Core logic
Tkinter – GUI framework (built into Python)
ttk / sv_ttk – Themed widgets & modern styling
random – Dice roll generation
No external APIs. No databases. Just clean, local Python.
🧠 App Overview
DiceForge is structured as a single-window desktop app with two main tabs:
Dashboard – Intro text and feature list
Dice Roller – Where the magic happens
The UI is responsive, readable, and designed to feel closer to a modern app than a default Tkinter window.
🚀 Application Setup
We start by configuring the main window and enabling themed widgets.
root = tk.Tk()
root.title("DiceForge - Dice Rolling Simulator")
root.geometry("900x620")
sv_ttk.set_theme("light")
The sv_ttk library gives us a cleaner base theme, which we later extend with custom styles.
🎛 Global State with Tkinter Variables
Tkinter’s Variable classes make state management simple:
dark_mode_var = tk.BooleanVar(value=False)
dice_count_var = tk.IntVar(value=1)
dice_sides_var = tk.IntVar(value=6)
These variables automatically stay in sync with widgets like spinboxes and checkbuttons.
🌗 Light & Dark Mode Toggle
Dark mode is handled by dynamically reconfiguring ttk styles and the root background:
def toggle_theme():
style.theme_use("clam")
if dark_mode_var.get():
root.configure(bg="#2E2E2E")
style.configure("TLabel", background="#2E2E2E", foreground="white")
else:
root.configure(bg="#FFFFFF")
style.configure("TLabel", background="#FFFFFF", foreground="black")
This approach keeps the UI consistent without rebuilding widgets.
📑 Tabbed Layout with Notebook
We use ttk.Notebook to separate concerns cleanly:
tabs = ttk.Notebook(root)
tabs.pack(expand=True, fill="both", padx=20, pady=20)
Each tab is its own Frame, making the layout modular and easy to extend later.
⚙️ Dice Configuration Controls
The dice settings use Spinbox widgets so users can’t input invalid types:
dice_spin = ttk.Spinbox(
settings_card,
from_=1,
to=100,
textvariable=dice_count_var
)
This keeps validation simple and UX friendly.
🎲 Rolling the Dice
Here’s the heart of the app:
def roll_dice():
count = dice_count_var.get()
sides = dice_sides_var.get()
rolls = [random.randint(1, sides) for _ in range(count)]
total = sum(rolls)
The results are displayed in a read-only Text widget, showing:
Dice notation (XdY)
Individual rolls
Final total
Perfect for quick checks or repeated testing.
🧾 Status Bar Feedback
A subtle but important UX touch: a status bar.
status_var = tk.StringVar(value="Ready")
This gives instant feedback for actions like rolling dice or switching themes.
✨ Polished Styling
Custom button styling helps important actions stand out:
style.configure(
"Action.TButton",
font=("Segoe UI", 11, "bold"),
background="#4CAF50"
)
Small details like this make a huge difference in perceived quality.
🏁 Final Thoughts
DiceForge is a great example of how far you can push Tkinter with thoughtful layout and styling.
It’s:
Beginner-friendly
Easily extendable (advantage/disadvantage rolls, presets, history)
A solid foundation for packaging with PyInstaller
If you’re learning Python GUI development, this project hits a sweet spot between approachable and polished.
Happy rolling 🎲

Top comments (0)