DEV Community

Benito Mallamaci
Benito Mallamaci

Posted on

I Made Tkinter Look Like a Modern Glassmorphic App — Here's the Dark Magic I Used

If you've ever built a desktop app in Python, you've probably used Tkinter. And if you have, you probably think it's doomed to look like a clunky, grey Windows 95 application.

I thought so too.

But recently, I needed a lightweight, floating UI for a 100% local, offline voice assistant I was building. I absolutely refused to bundle an entire Chromium instance (Electron) just to render a small widget.

So I decided to push Tkinter to its absolute limits. The result is Writher: an open-source, privacy-first voice assistant and dictation tool powered by faster-whisper and Ollama.

In this post, I'll break down the tricks I used to make a legacy Python GUI look modern, and the architecture behind a fully local AI desktop app.


🎨 The UI Hack: Making Tkinter Beautiful

To get that modern, glassmorphic floating pill shape with glowing borders, I completely bypassed Tkinter's standard widgets.

Instead, I used PIL (Pillow) to render high-resolution graphics dynamically on a transparent Tkinter Canvas.

The Trick: Borderless & Transparent Windows

# Remove the window frame entirely
root.overrideredirect(True)

# Chromakey hack: make a specific color fully transparent
root.wm_attributes("-transparentcolor", "#000001")
Enter fullscreen mode Exit fullscreen mode

This gives you a frameless, floating window — the foundation for any modern-looking widget.

Dynamic Glow Rendering

Every frame of the animation (the bot's eyes changing expressions: listening, thinking, happy) is drawn on-the-fly using ImageDraw and ImageFilter.GaussianBlur to create a glowing effect that mimics SVG filters:

from PIL import Image, ImageDraw, ImageFilter

def draw_glow(size, color, blur_radius=10):
    img = Image.new("RGBA", size, (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
    draw.rounded_rectangle(
        [blur_radius, blur_radius,
         size[0] - blur_radius, size[1] - blur_radius],
        radius=20, fill=color
    )
    return img.filter(ImageFilter.GaussianBlur(blur_radius))
Enter fullscreen mode Exit fullscreen mode

The Ghost Window

Since Writher is a dictation tool, clicking it shouldn't steal focus from your active app (like VSCode or a text editor). I used Win32 ctypes to apply the WS_EX_NOACTIVATE style:

import ctypes

GWL_EXSTYLE = -20
WS_EX_NOACTIVATE = 0x08000000
WS_EX_TOOLWINDOW = 0x00000080

def make_ghost_window(hwnd):
    style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
    ctypes.windll.user32.SetWindowLongW(
        hwnd, GWL_EXSTYLE,
        style | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW
    )
Enter fullscreen mode Exit fullscreen mode

💡 Pro tip: WS_EX_TOOLWINDOW also hides the window from the Alt+Tab menu, making it behave like a true desktop widget.


🧠 The Brain: 100% Local AI

I'm tired of sending my voice and private notes to the cloud. Writher operates entirely on your local machine.

Speech-to-Text — I used faster-whisper (CTranslate2). It runs flawlessly on CPU or GPU and transcribes voice in near real-time.

The LLM — I hooked the app to Ollama. Press Ctrl+R, Writher listens, transcribes, and passes the text to a local model for processing.

Function Calling — This is where it gets interesting. Instead of just chatting, I configured the LLM with tool definitions. It converts your voice commands into structured function calls — saving notes, scheduling appointments, or setting reminders in a local SQLite database:

# Thread-safe SQLite with WAL mode
conn = sqlite3.connect("writher.db")
conn.execute("PRAGMA journal_mode=WAL")
Enter fullscreen mode Exit fullscreen mode

The LLM doesn't just understand you — it acts on what you say.


⚙️ The Muscle: Seamless OS Integration

Most dictation tools copy text to your clipboard and force you to paste manually. Writher acts like a phantom keyboard.

I initially used pyperclip, but it suffers from race conditions when other apps lock the clipboard. To make it bulletproof, I wrote a custom clipboard injector using the Win32 API (OpenClipboard, SetClipboardData).

The flow:

  1. Save your current clipboard contents
  2. Inject the transcribed text and simulate Ctrl+V via pynput
  3. Restore your original clipboard — all in milliseconds

And just in case Windows blocks the clipboard? I implemented a fail-safe that automatically appends your dictation to a recovery_notes.txt file. You never lose a single word.


🔑 What I Learned

Building Writher taught me a few things I wasn't expecting:

  • You don't need Electron for everything. A transparent Tkinter canvas + Pillow can produce surprisingly polished UIs. The binary is tiny compared to any web-based alternative.
  • Local AI is ready. With faster-whisper + Ollama, you can build genuinely useful AI tools that never phone home. Privacy doesn't have to mean sacrificing quality.
  • Win32 APIs are your secret weapon on Windows. Ghost windows, clipboard control, focus management — they unlock capabilities that pure Python can't reach alone.

🚀 Try It Yourself

The whole project is open-source. You can check out the UI implementation, the AI pipeline, and run it on your own machine:

👉 Writher on GitHub

I'd love to hear your thoughts. Have you ever pushed a legacy GUI framework beyond what it was meant to do? What local LLM setup are you running? Drop a comment — I read all of them.

And if you find the project useful, a ⭐ on the repo helps more than you'd think. 🙏

Top comments (0)