Motivation
I used to have a single password for everything until I learnt things the hard way. So I started creating more complicated passwords and my caveman instinct taught me to save them somewhere. Before you opine whether storing non-encrypted passwords locally is a heinous crime, please know that I’m still going to proceed with this post.
Starting with the end in mind
Building the UI
This is the easy part.
root = Tk()
root.title(“Password Manager”)
root.config(padx=50, pady=50, bg=NAVY)# insert widgets hereroot.mainloop()
Canvas widget
# ROW 0
canvas = Canvas(height=200, width=200, bg=NAVY, highlightthickness=0)
img = PhotoImage(file='logo.png')
canvas.create_image(100, 100, image=img)
canvas.grid(row=0,column=1)
Other widgets
What is worth noting in the below code is that it’s not such a great idea to hardcode the widget width because there’s too much trial and error. A better way would be to use the sticky
parameter.
Notice that there are 3 button widgets linked to search_website, random_password and saved_entries using thecommand
param which we will build in the later section.
# ROW 1
website_label = Label(text=’Website:’, bg=NAVY, fg=BEIGE)
website_label.grid(row=1,column=0,sticky=”W”)website_entry = Entry()
website_entry.grid(row=1,column=1, columnspan=2,sticky=”EW”)
website_entry.focus()website_search = Button(text='Search', bg=GREY, command=search_website)
website_search.grid(row=1,column=2,sticky="EW")# ROW 2
email_label = Label(text=’Email/Username:’, bg=NAVY, fg=BEIGE)
email_label.grid(row=2,column=0,sticky=”W”)email_entry = Entry()
email_entry.grid(row=2,column=1, columnspan=2,sticky=”EW”)
email_entry.insert(0, ‘myusername@gmail.com’)# ROW 3
password_label = Label(text=’Password:’, bg=NAVY, fg=BEIGE)
password_label.grid(row=3,column=0,sticky=”W”)password_entry = Entry()
password_entry.grid(row=3,column=1,sticky=”EW”)password_button = Button(text=’Generate Password’, bg=GREY, command=random_password)
password_button.grid(row=3,column=2,sticky=”EW”)# ROW 4
button = Button(text=’Add’, bg=GREY, command=saved_entries)
button.grid(row=4,column=1,columnspan=2,sticky=”EW”)
button.config(pady=2)
Creating function to save into output file
We start off by collecting the user inputs before serializing into a specifc json format.
def saved_entries():# GETTING THE USER INPUTS
user_website = website_entry.get()
user_email = email_entry.get()
user_password = password_entry.get() new_data = {
user_website: {
'email': user_email,
'password': user_password
}
}
...
Before we save the entry into our json file, it’d be wise to confirm if there are any blank fields or typos. Hence we imported the messagebox
module which is basically just a window pop-up.
Here’s a link to working with json data namely json.load(), json.update() and json.dump()
def saved_entries(): if len(user_website) != 0 and len(user_password) != 0: try:
## LOADING JSON FILE IF EXIST
with open('data.json', 'r') as data_file:
data = json.load(data_file)
data.update(new_data)
except FileNotFoundError:
## CREATE JSON FILE AND CREATE FIRST ENTRY IF DONT EXIST
with open('data.json','w') as data_file:
json.dump(new_data, data_file, indent=4)
else:
## CONFIRM DETAILS BEFORE DUMPING INTO JSON FILE
is_correct = messagebox.askyesno(
title=f"{user_website}",
message=f"\n'email': {user_email}\n
'password': {user_password}\n\n
Please confirm before saving!")
if is_correct:
with open('data.json','w') as data_file:
json.dump(data, data_file, indent=4)
website_entry.delete(0, END)
password_entry.delete(0, END)
else:
# IF WEBSITE OR EMAIL ENTRY IS BLANK
messagebox.showwarning(
title='Oops',
message="Please don't leave any fields empty!"
)
Creating function to generate a random password
The last function is probably the easiest one. The only tkinter-ish thing to highlight here is knowing how to work with Entry, particularly delete
and insert
.
For better user experience, the generated password is also auto-saved onto the clipboard using pyperclip
module.
Side note: “how to apply a function to every item in a list” is something I often googled
def random_password():letters = list(‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)
numbers = list(‘0123456789’)
symbols = list(‘!@#$%^&*()_+’)
letters_lower = list(map(str.lower,letters))
letters.extend(letters_lower)#Return a number between a and b (both included):
num_letters = random.randint(8,10)
num_numbers = random.randint(1,2)
num_symbols = random.randint(1,2)# Creating password in one big list
rand_letters = [random.choice(letters) for i in range(num_letters)]
rand_numbers = [random.choice(numbers) for i in range(num_numbers)]
rand_symbols = [random.choice(symbols) for i in range(num_symbols)]created_password = rand_letters + rand_numbers + rand_symbols# Shuffle the positions and join to create str
random.shuffle(created_password)
created_password = ‘’.join(created_password)# Insert into password_entry label upon clicking “Generate password”
password_entry.delete(0, END)
password_entry.insert(0, created_password)# Copy created password to clipboard
pyperclip.copy(created_password)
Conclusion
The full code can be found here: https://github.com/hxkoey/passwordmanager-tkinter