You've built a functional Python desktop application with Tkinter. It works. But every time you look at it, you know it could look and feel so much better. Your users know it too. The good news: you don't have to start over. This guide walks you through migrating an existing Tkinter application to Bravura, one component at a time, without rewriting your entire codebase.
Why Migrate?
Tkinter ships with Python. It's stable, well-documented, and gets the job done. So why consider migrating at all?
The honest answer: Tkinter was designed in the 1990s. While it still works, user expectations have changed dramatically. Your users interact with polished, animated, dark-mode-friendly interfaces every day. Tkinter's default widgets don't meet that bar, and the effort required to force them there is often greater than the effort of migrating.
Here's what a migration to Bravura gives you without any extra work:
- Professional theming — 10 built-in themes (Cyberpunk, Ocean, Forest, and more) that make your application look modern instantly
- GPU acceleration — Hardware-accelerated rendering for smooth 60 FPS animations on capable machines
- Thread-safe workers — Background task system that prevents the UI freezing problems Tkinter developers know all too well
- Progress indicators — Rich progress bars with ETA, animation, and cancel support out of the box
- Cross-platform polish — Consistent appearance on Windows, macOS, and Linux without per-platform workarounds
Key Principle
Migration doesn't have to be all-or-nothing. Bravura is built on top of Tkinter, so you can migrate incrementally — one widget, one window, or one feature at a time. Your existing code continues to work while you upgrade around it.
Before You Start: Assess Your Application
Not every Tkinter application needs the same migration path. Before writing any code, answer these questions:
- How many windows does your application have? A single-window app can be migrated in an afternoon. A complex multi-window app with dialogs, wizards, and preferences may take several sessions.
- Do you use ttk widgets or plain tk? If you're already using
ttk.Button,ttk.Entry, etc., migration is smoother because Bravura follows similar patterns. - Do you have threading? If you've built your own threading system, you can eventually replace it with Bravura's built-in workers, but there's no rush.
- How much custom drawing do you do? Canvas-heavy applications (charts, diagrams) require more careful migration than form-based apps.
Step 1: Install Bravura
Bravura installs like any Python package. Assuming you have a valid license key:
pip install bravura
After installation, verify it's working:
import bravura
print(bravura.__version__)
# Expected: 2.x.x
If the import succeeds, you're ready to start migrating.
Step 2: Replace the Root Window
The simplest and highest-impact first step is replacing your Tkinter root window with a Bravura themed window. This single change affects every widget in your application.
import tkinter as tk
root = tk.Tk()
root.title("My Application")
root.geometry("800x600")
# ... your widgets here ...
root.mainloop()
from bravura import BravuraApp
app = BravuraApp(
title="My Application",
size=(800, 600),
theme="ocean" # Or: cyberpunk, forest, midnight, etc.
)
# ... your widgets here (they still work!) ...
app.run()
That's it for step one. Your existing Tkinter widgets will inherit the Bravura theme's color scheme automatically. They won't have full Bravura styling yet, but they'll look noticeably better than they did with Tkinter's defaults.
Common Pitfall
If you've hardcoded colors in your widgets (e.g., bg="#ffffff"), those hardcoded values will override Bravura's theme. Remove explicit color arguments to let the theme take over. You can always customize later.
Step 3: Migrate Widgets Incrementally
Now comes the real work, but it's straightforward. Replace Tkinter widgets with Bravura equivalents one at a time. The API is intentionally similar, so most replacements are near-mechanical.
| Tkinter Widget | Bravura Widget | Key Improvement |
|---|---|---|
tk.Button |
bravura.Button |
Hover animations, rounded corners, gradient support |
tk.Entry |
bravura.Entry |
Placeholder text, focus glow, validation states |
tk.Label |
bravura.Label |
Theme-aware colors, weight variants |
tk.Frame |
bravura.Card |
Rounded corners, shadow, border effects |
tk.Listbox |
bravura.List |
Smooth scrolling, selection animations |
tk.Progressbar |
bravura.ProgressBar |
60 FPS animation, ETA, gradient fills |
tk.Scrollbar |
bravura.ScrollView |
Auto-hiding, smooth momentum scrolling |
Example: Migrating a Login Form
username_label = tk.Label(frame, text="Username:")
username_label.pack()
username_entry = tk.Entry(frame, width=30)
username_entry.pack()
password_label = tk.Label(frame, text="Password:")
password_label.pack()
password_entry = tk.Entry(frame, show="*", width=30)
password_entry.pack()
login_btn = tk.Button(frame, text="Log In", command=login)
login_btn.pack(pady=10)
card = bravura.Card(frame, padding=24)
card.pack(padx=20, pady=20)
username_entry = bravura.Entry(
card, placeholder="Username", width=300
)
username_entry.pack(pady=(0, 12))
password_entry = bravura.Entry(
card, placeholder="Password", password=True, width=300
)
password_entry.pack(pady=(0, 16))
login_btn = bravura.Button(
card, text="Log In", command=login, style="primary"
)
login_btn.pack()
Notice the improvements: labels are replaced by placeholders (cleaner layout), the frame becomes a Card (visual elevation), and the button gets a predefined "primary" style. The total line count is similar, but the result looks dramatically different.
Step 4: Replace Threading with Bravura Workers
If your application does background work (file processing, API calls, database queries), you've probably written custom threading code with threading.Thread and queue.Queue. This works, but it's error-prone and repetitive.
Bravura's worker system handles the boilerplate:
import threading
from queue import Queue
def process_files(files):
q = Queue()
def worker():
for i, f in enumerate(files):
process(f)
q.put(("progress", (i+1)/len(files)*100))
q.put(("done", None))
t = threading.Thread(target=worker, daemon=True)
t.start()
def check_queue():
while not q.empty():
msg_type, value = q.get()
if msg_type == "progress":
progress_bar["value"] = value
elif msg_type == "done":
status_label.config(text="Complete!")
return
root.after(100, check_queue)
check_queue()
from bravura import BackgroundWorker
worker = BackgroundWorker(
task=process,
items=files,
on_progress=lambda p: progress_bar.set(p),
on_complete=lambda: status_label.set("Complete!"),
on_error=lambda e: show_error(e)
)
worker.start()
Bravura's workers handle thread lifecycle, progress callbacks, error handling, and cancellation. The result is less code, fewer bugs, and a better user experience.
Step 5: Add Themes and Polish
Once your widgets are migrated, spend time on the details that separate professional software from hobby projects:
- Theme switching — Let users choose between light and dark modes (Bravura supports this natively)
- Loading states — Add skeleton screens or spinners while data loads
- Transitions — Use Bravura's animation system for smooth page transitions
- Keyboard shortcuts — Map common actions to keyboard shortcuts for power users
- Error dialogs — Replace generic
messagebox.showerror()with Bravura's styled dialogs
# Let users switch themes at runtime
theme_selector = bravura.ThemeSelector(
app,
themes=["ocean", "cyberpunk", "forest", "midnight"],
on_change=lambda theme: app.set_theme(theme)
)
theme_selector.pack()
Migration Checklist
Use this checklist to track your progress. A typical application can be fully migrated in 1–3 days:
- Replace root window —
tk.Tk()toBravuraApp()(30 minutes) - Remove hardcoded colors — Delete explicit
bg=,fg=arguments (1 hour) - Migrate buttons and entries — Highest visual impact (1–2 hours)
- Migrate frames to cards — Add visual elevation (30 minutes)
- Replace progress bars — Add animation and ETA (30 minutes)
- Migrate threading — Replace manual threads with workers (1–2 hours)
- Add theme support — Enable dark/light mode switching (30 minutes)
- Polish and test — Cross-platform testing, edge cases (2–4 hours)
Total estimated time: 6–12 hours for a typical single-window application.
The Incremental Advantage
You don't have to do this all at once. Because Bravura is built on Tkinter, your old widgets and new widgets coexist peacefully. Migrate your most visible screens first, ship the update, and continue migrating at your own pace. Your users see improvements immediately while you maintain stability.
Common Questions
Will my existing Tkinter code break?
No. Bravura extends Tkinter rather than replacing it. Your existing tk and ttk widgets continue to work inside a Bravura application. You can mix and match freely during migration.
What about third-party Tkinter libraries?
Libraries like tkinterdnd2, tkcalendar, and tksheet all work with Bravura since they're built on Tkinter too. They'll inherit basic theme colors from Bravura, though they won't get Bravura-specific styling.
How does licensing work?
Bravura uses a one-time purchase model. Standard ($49.99/mo or $1,999.99 one-time) covers individual developers. Professional and Enterprise tiers are available for teams. No recurring royalties on applications you build.
What if I need help?
Every Bravura license includes access to documentation, code examples, and email support. Professional and Enterprise tiers include priority support with faster response times.
The Bottom Line
Migrating from Tkinter to Bravura isn't a rewrite. It's an upgrade. Your application logic stays the same. Your architecture stays the same. You're replacing the visual layer with something modern, while gaining professional infrastructure (threading, progress, GPU acceleration) that would take hundreds of hours to build yourself.
Start with the root window. Replace a few widgets. See the difference. Then decide how far you want to take it.
Ready to Upgrade Your Application?
See what Bravura can do for your Python desktop app. Browse themes, explore components, and start building something your users will love.
Explore Bravura SDK