Skip to Content
Course content

387: Building a Password Manager GUI with tkinter

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When most people first dive into building a GUI for a tool like a password manager, they fall into a specific trap: they treat the GUI as the program itself. I've seen this countless times. A learner will write a 200-line function tied to a "Save Password" button that handles the input validation, encrypts the string, opens a file, writes the data, and then updates a label to say "Success!"

The "Put Everything in the Button" Trap

The problem with shoving your business logic—the actual "work" of managing passwords—inside a tkinter callback is that your code becomes brittle. If you decide later that you want to move from a local text file to an SQLite database, or if you want to add a command-line interface version of your tool, you can't. You'd have to rip the encryption and file logic out of the command=save_password function and rewrite it all.

# The Wrong Way: Logic trapped in the GUI
def save_password_button_clicked():
    site = site_entry.get()
    pwd = pwd_entry.get()
    # Logic is trapped here!
    with open("passwords.txt", "a") as f:
        f.write(f"{site}|{pwd}\n")
    status_label.config(text="Saved!")

If that file write fails because of a permissions error, your entire GUI freezes or crashes because the "view" is too busy trying to be the "database."

Decoupling Your Vault Logic from Your Windows

The professional approach is to separate the Logic (what the app does) from the Interface (how the user sees it). I like to think of the Logic as a "black box." The GUI should just send data into the box and wait for a result. It shouldn't care how the password is encrypted or where it's stored.

Let's look at how we structure this. First, we build a PasswordVault class that knows nothing about tkinter. Then, we build the PasswordApp class that handles the windows and buttons.

import tkinter as tk
from tkinter import messagebox

# THE LOGIC: This could be moved to a separate file entirely
class PasswordVault:
    def __init__(self, filename="vault.txt"):
        self.filename = filename

    def add_password(self, site, password):
        # In a real app, you'd encrypt 'password' here
        with open(self.filename, "a") as f:
            f.write(f"{site}:{password}\n")
        return True

    def get_all_passwords(self):
        try:
            with open(self.filename, "r") as f:
                return f.readlines()
        except FileNotFoundError:
            return []

# THE INTERFACE: This only handles widgets and user interaction
class PasswordApp:
    def __init__(self, root, vault):
        self.root = root
        self.vault = vault # We inject the logic dependency here
        
        self.root.title("SecureVault 1.0")
        
        # Entry fields
        tk.Label(root, text="Website").grid(row=0, column=0)
        self.site_entry = tk.Entry(root)
        self.site_entry.grid(row=0, column=1)
        
        tk.Label(root, text="Password").grid(row=1, column=0)
        self.pwd_entry = tk.Entry(root, show="*")
        self.pwd_entry.grid(row=1, column=1)
        
        # The button calls a handler, which calls the vault
        self.save_btn = tk.Button(root, text="Save", command=self.handle_save)
        self.save_btn.grid(row=2, column=0, columnspan=2)

    def handle_save(self):
        site = self.site_entry.get()
        pwd = self.pwd_entry.get()
        
        if not site or not pwd:
            messagebox.showwarning("Error", "Fields cannot be empty!")
            return

        # We delegate the hard work to the vault object
        if self.vault.add_password(site, pwd):
            messagebox.showinfo("Success", "Password stored securely!")
            self.site_entry.delete(0, tk.END)
            self.pwd_entry.delete(0, tk.END)

# Bootstrap the application
if __name__ == "__main__":
    root = tk.Tk()
    my_vault = PasswordVault() # Logic
    app = PasswordApp(root, my_vault) # Interface
    root.mainloop()

Wiring Up the tkinter Interface

Notice how PasswordApp doesn't actually know how to write to a file? It just calls self.vault.add_password(). This is called Dependency Injection. It's a fancy term, but it just means we're giving the GUI the tools it needs to do its job without making it responsible for creating those tools.

I've used grid() here instead of pack() because password managers usually look like forms, and grids are far superior for alignment. Also, notice the show="*" argument in the password entry—never forget that. There's nothing worse than a "security" tool that broadcasts passwords to everyone walking past your monitor.




📋 Practical Task

Implementing a "View All Passwords" Listbox

Your current password manager can save passwords, but it can't show them back to the user. Your task is to extend the PasswordApp class to include a way to view stored passwords.

Requirements:

  • Add a tk.Listbox widget to the PasswordApp GUI, placed below the save button.
  • Add a "Refresh List" button that, when clicked, calls self.vault.get_all_passwords().
  • The "Refresh List" handler must clear the current contents of the Listbox (using delete(0, tk.END)) and then populate it with the current list of saved sites and passwords from the vault.
  • Ensure the Listbox is scrollable or has a fixed height so it doesn't push the window off the screen.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.