In this tutorial, we’ll create a Unit Converter desktop application using Python and Tkinter. The app can convert between common length and weight units, includes a conversion history, and supports light/dark themes.
We'll walk through the complete source code and explain each part so you can build your own version.
Features
Convert Length (meters, kilometers, miles, etc.)
Convert Weight (kilograms, grams, pounds, etc.)
Maintains last 10 conversions
Dark Mode toggle for comfortable usage
Modern UI using ttk and sv_ttk themes
Prerequisites
Make sure you have Python 3.x installed. We'll also use sv_ttk for a nicer look and feel.
Install sv_ttk:
pip install sv_ttk
Full Source Code
import sys
import os
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import sv_ttk
# Helper to get resource path (for PyInstaller)
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)
# =========================
# App Setup
# =========================
root = tk.Tk()
root.title("Unit Converter")
root.geometry("1100x800")
# root.iconbitmap(resource_path("logo.ico"))
sv_ttk.set_theme("light")
# =========================
# Globals
# =========================
dark_mode_var = tk.BooleanVar(value=False)
history_data = [] # Stores last 10 conversions
# =========================
# Status Bar
# =========================
status_var = tk.StringVar(value="Ready")
ttk.Label(root, textvariable=status_var, anchor="w", font=("Segoe UI", 10)).pack(side=tk.BOTTOM, fill="x")
# =========================
# Theme Toggle Function
# =========================
style = ttk.Style()
style.theme_use("clam")
def set_status(msg):
status_var.set(msg)
root.update_idletasks()
def toggle_theme():
if dark_mode_var.get():
root.configure(bg="#2E2E2E")
style.configure("TLabel", background="#2E2E2E", foreground="white")
style.configure("TFrame", background="#2E2E2E")
style.configure("TNotebook.Tab", background="#444444", foreground="white")
style.map("TNotebook.Tab", background=[("selected", "#90caf9")], foreground=[("selected", "black")])
else:
root.configure(bg="#FFFFFF")
style.configure("TLabel", background="#FFFFFF", foreground="black")
style.configure("TFrame", background="#FFFFFF")
style.configure("TNotebook.Tab", background="#e0e0e0", foreground="black")
style.map("TNotebook.Tab", background=[("selected", "#90caf9")], foreground=[("selected", "black")])
set_status(f"Theme switched to {'Dark' if dark_mode_var.get() else 'Light'} mode")
# =========================
# Notebook Tabs
# =========================
tabs = ttk.Notebook(root)
tabs.pack(expand=True, fill="both", padx=20, pady=20)
# Dashboard Tab
dash_tab = ttk.Frame(tabs, padding=20)
tabs.add(dash_tab, text="🏠 Dashboard")
ttk.Label(dash_tab, text="Unit Converter", font=("Segoe UI", 20, "bold")).pack(anchor="w")
ttk.Label(dash_tab, text="Convert between Length and Weight units quickly and accurately.", font=("Segoe UI", 11)).pack(anchor="w", pady=(5,10))
# Converter Tab
tool_tab = ttk.Frame(tabs, padding=20)
tabs.add(tool_tab, text="🔧 Converter")
# Conversion Factors
length_units = {
"Meters": 1.0, "Kilometers": 1000.0, "Centimeters": 0.01, "Millimeters": 0.001,
"Miles": 1609.34, "Yards": 0.9144, "Feet": 0.3048, "Inches": 0.0254
}
weight_units = {
"Kilograms": 1.0, "Grams": 0.001, "Milligrams": 0.000001,
"Pounds": 0.453592, "Ounces": 0.0283495
}
# UI Elements
ttk.Label(tool_tab, text="Value to Convert:", font=("Segoe UI", 12)).pack(anchor="w", pady=5)
value_entry = ttk.Entry(tool_tab, font=("Segoe UI", 14))
value_entry.pack(fill="x", pady=5)
ttk.Label(tool_tab, text="Select Category:", font=("Segoe UI", 12)).pack(anchor="w", pady=5)
category_var = tk.StringVar(value="Length")
category_combo = ttk.Combobox(tool_tab, textvariable=category_var, state="readonly",
values=["Length", "Weight"], font=("Segoe UI", 12))
category_combo.pack(fill="x", pady=5)
ttk.Label(tool_tab, text="From Unit:", font=("Segoe UI", 12)).pack(anchor="w", pady=5)
from_unit_var = tk.StringVar(value="Meters")
from_combo = ttk.Combobox(tool_tab, textvariable=from_unit_var, state="readonly", font=("Segoe UI", 12))
from_combo.pack(fill="x", pady=5)
ttk.Label(tool_tab, text="To Unit:", font=("Segoe UI", 12)).pack(anchor="w", pady=5)
to_unit_var = tk.StringVar(value="Kilometers")
to_combo = ttk.Combobox(tool_tab, textvariable=to_unit_var, state="readonly", font=("Segoe UI", 12))
to_combo.pack(fill="x", pady=5)
result_var = tk.StringVar(value="")
ttk.Label(tool_tab, text="Result:", font=("Segoe UI", 12)).pack(anchor="w", pady=5)
ttk.Label(tool_tab, textvariable=result_var, font=("Segoe UI", 14, "bold")).pack(anchor="w", pady=5)
# History Frame
history_frame = ttk.LabelFrame(tool_tab, text="Conversion History (Last 10)", padding=10)
history_frame.pack(fill="both", expand=True, pady=10)
history_text = scrolledtext.ScrolledText(history_frame, height=6, font=("Consolas", 11), state="disabled")
history_text.pack(fill="both", expand=True)
# =========================
# Functions
# =========================
def update_units(event=None):
units = list(length_units.keys()) if category_var.get() == "Length" else list(weight_units.keys())
from_combo["values"] = units
to_combo["values"] = units
from_combo.set(units[0])
to_combo.set(units[1])
def convert_unit():
try:
value = float(value_entry.get())
factor_from = length_units[from_unit_var.get()] if category_var.get() == "Length" else weight_units[from_unit_var.get()]
factor_to = length_units[to_unit_var.get()] if category_var.get() == "Length" else weight_units[to_unit_var.get()]
result = value * factor_from / factor_to
result_var.set(f"{result:.6g}")
set_status(f"Converted {value} {from_unit_var.get()} to {result:.6g} {to_unit_var.get()}")
add_to_history(value, from_unit_var.get(), result, to_unit_var.get())
except ValueError:
messagebox.showerror("Error", "Please enter a valid number")
result_var.set("")
set_status("Error: Invalid input")
def add_to_history(value, from_unit, result, to_unit):
global history_data
entry = f"{value} {from_unit} → {result:.6g} {to_unit}"
history_data.insert(0, entry)
history_data = history_data[:10]
history_text.config(state="normal")
history_text.delete("1.0", tk.END)
history_text.insert(tk.END, "\n".join(history_data))
history_text.config(state="disabled")
# Bind events
category_combo.bind("<<ComboboxSelected>>", update_units)
# Buttons
ttk.Button(tool_tab, text="Convert", command=convert_unit, style="Action.TButton").pack(pady=10)
ttk.Checkbutton(root, text="Dark Mode", variable=dark_mode_var, command=toggle_theme).pack(side="bottom", pady=5)
# Initialize
update_units()
root.mainloop()
How It Works
UI Setup: We use ttk.Notebook for tabs (Dashboard & Converter).
Unit Conversion: Conversion factors are stored in dictionaries (length_units and weight_units).
History: Last 10 conversions are displayed in a ScrolledText widget.
Dark Mode: Toggle modifies the ttk.Style and background colors dynamically.
Error Handling: Invalid inputs are caught with a messagebox.
Screenshots
Conclusion
This project demonstrates how Tkinter can be used to create modern, functional desktop apps in Python with nice UI and features like history and dark mode.

Top comments (0)