In this tutorial, we’ll build a desktop GUI app using Python + Tkinter that:
Generates text patterns (pyramid, triangle, inverted pyramid)
Stores every generated pattern in a history list
Lets users preview previous patterns
Exports pattern history to a .txt file
Supports light/dark mode
This guide is written for beginners, so we’ll build the app piece by piece.
🛠 Prerequisites
Before we start, make sure you have:
Python 3.9+
Basic Python knowledge (functions, loops)
Tkinter (comes with Python)
sv_ttk for modern themes
Install sv_ttk if needed:
pip install sv-ttk
1️⃣ Import Required Modules
We start by importing all the libraries we’ll need.
import sys
import os
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import sv_ttk
What these do:
tkinter → GUI framework
ttk → modern widgets
messagebox → pop-up alerts
filedialog → file save dialog
sv_ttk → clean light/dark themes
2️⃣ Helper Functions
🔹 Resource Path Helper (for packaging support)
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)
This makes your app PyInstaller-friendly if you later convert it into an .exe.
🔹 Status Bar Helper
def set_status(msg):
status_var.set(msg)
root.update_idletasks()
This updates the status bar text instantly without freezing the UI.
3️⃣ Create the Main App Window
root = tk.Tk()
root.title("Pattern History Printer")
root.geometry("600x700")
root.minsize(600, 700)
sv_ttk.set_theme("light")
We define:
App title
Default size
Minimum size
Light theme using sv_ttk
4️⃣ Global Variables & State
dark_mode_var = tk.BooleanVar(value=False)
rows_var = tk.StringVar(value="5")
pattern_type_var = tk.StringVar(value="Pyramid")
pattern_history = []
Why use StringVar / BooleanVar?
They automatically sync UI widgets with Python variables.
5️⃣ Dark Mode Toggle Logic
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)
rows_entry.configure(background=bg, foreground=fg)
This function dynamically:
Switches background colors
Updates text color
Keeps widgets readable in dark mode
6️⃣ Pattern Generation Logic
def generate_pattern():
try:
rows = int(rows_var.get())
if rows < 1:
raise ValueError
except ValueError:
messagebox.showerror("Invalid Input", "Please enter a valid positive integer.")
return
Input validation:
Ensures rows is a positive integer
Shows an error dialog if invalid
🔹 Generate Different Patterns
p_type = pattern_type_var.get()
pattern_lines = []
if p_type == "Pyramid":
for i in range(1, rows + 1):
pattern_lines.append(" " * (rows - i) + "*" * (2 * i - 1))
elif p_type == "Right Triangle":
for i in range(1, rows + 1):
pattern_lines.append("*" * i)
elif p_type == "Inverted Pyramid":
for i in range(rows, 0, -1):
pattern_lines.append(" " * (rows - i) + "*" * (2 * i - 1))
Each pattern is built line-by-line, then joined into a single string.
🔹 Save to History
pattern_str = "\n".join(pattern_lines)
add_to_history(p_type, rows, pattern_str)
set_status("Pattern generated! Click a history entry to view details.")
7️⃣ Pattern History Management
🔹 Add to History List
def add_to_history(p_type, rows, pattern_str):
pattern_history.append((p_type, rows, pattern_str))
preview = f"{p_type} | Rows: {rows}"
history_list.insert(tk.END, preview)
We store:
Pattern type
Number of rows
Full pattern text
8️⃣ Export History to a Text File
def export_history_txt():
if not pattern_history:
messagebox.showinfo("Empty History", "No patterns to export.")
return
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Text Files", "*.txt")],
title="Export Pattern History"
)
with open(file_path, "w", encoding="utf-8") as f:
f.write("Pattern History\n")
f.write("=" * 40 + "\n\n")
for i, (ptype, rows, pstr) in enumerate(pattern_history, 1):
f.write(f"{i}. {ptype} | Rows: {rows}\n")
f.write(pstr + "\n\n")
This creates a clean, readable export file.
9️⃣ View Pattern Details in a New Window
def view_selected_history(event=None):
selection = history_list.curselection()
if not selection:
messagebox.showinfo("No Selection", "Please select a pattern.")
return
history_window = tk.Toplevel(root)
history_window.title(f"{p_type} | Rows: {rows}")
history_window.geometry("500x400")
text_widget = tk.Text(frame, wrap="none", font=("Consolas", 12))
text_widget.insert(tk.END, pattern_str)
text_widget.configure(state="disabled")
This opens a read-only preview window for each saved pattern.
🔟 Styling & Status Bar
style = ttk.Style()
style.theme_use("clam")
style.configure("Action.TButton", font=("Segoe UI", 11, "bold"), padding=8)
status_var = tk.StringVar(value="Ready")
ttk.Label(root, textvariable=status_var, anchor="w").pack(side=tk.BOTTOM, fill="x")
1️⃣1️⃣ UI Layout (Inputs, Buttons & History)
We organize the layout using ttk.Frame, grid(), and pack() for a responsive design.
Key widgets:
Entry for rows
ComboBox for pattern type
Buttons for generate/export
Listbox for history
Dark mode toggle
1️⃣2️⃣ Run the App
root.mainloop()
This starts the Tkinter event loop and launches the app 🚀
🎉 Final Result
You now have a fully functional desktop app that:
Generates patterns
Tracks history
Supports dark mode
Exports data
Uses clean UI themes

Top comments (0)