PythonのTkinterでGUIを用いたPassword Managerを作成してみた

### 前置き
最近、Tkinterを使ったGUIを学び出して、今日はPassword Managerを作りたくなった。
Passwordとは、機密保護などのため、正式の登録者であることを確認するものである。
一般的に、安全なパスワードにするには、最低でも10桁以上の英数字と記号を組み合わせることが推奨されている。今回は、10文字から22文字のパスワードを作成するプログラムコードにした。Password Managerの画像はCanvasを使って、自分で作成した。

### プログラムコード
``` python
from tkinter import *
from tkinter import messagebox
from random import choice, randint, shuffle
import pyperclip
import json 
import search

# -----Password Generator ------------- #
#Password Generator Project
def generate_password():
    letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
               'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 
               'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
               'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']

    password_letters = [choice(letters) for _ in range(randint(4, 10))]
    password_symbols = [choice(symbols) for _ in range(randint(3, 6))]
    password_numbers = [choice(numbers) for _ in range(randint(3, 6))]

    password_list = password_letters + password_symbols + password_numbers
    shuffle(password_list)

    password = "".join(password_list)
    password_entry.insert(0, password)
    pyperclip.copy(password)
#---- Save Password----
def save():
    website = website_entry.get()
    email = email_entry.get()
    password = password_entry.get()
    new_data = {
        website:{
            "email": email,
            "password":password,
        }
    }

    if len(website_entry.get()) == 0 or len(password_entry.get()) == 0:
        messagebox.showinfo(title="Oops", message= 
        "Please make sure you haven't left any fields empty.")
    else:
        try:
            with open("data.json", "r") as data_file:
             #Reading old data
             data = json.load(data_file)
        except FileNotFoundError:
            with open("data.json","w") as data_file:
                json.dump(new_data, data_file, indent = 4)
        else:
            #Updating old data with new data
            data.update(new_data)

            with open("data.json","w") as data_file:
              #Saving updated data
              json.dump(new_data, data_file, indent = 4)
        finally:
            website_entry.delete(0, END)
            password_entry.delete(0, END)
# ----- Find password-------
def find_password():
    website = website_entry.get()
    try:
      with open("data.json") as data_file:
        data = json.load(data_file)
    except FileNotFoundError:
        messagebox.showinfo(title = "Error", message = "No Data File Found.")
    else:
        if website in data:
            email = data[website]["email"]
            password = data[website]["password"]
            messagebox.showinfo(title = website, 
                                message = f"Email:{email}\nPassword:{password}")
        else:
            messagebox.showinfo(title = "Error", 
                                message = f"No details for {website} exists.")

# ---------------------------- UI SETUP ------------------------------- #

window = Tk()
window.title("Password Manager")
window.config(padx=60, pady=60)

canvas = Canvas(height=200, width=200)
logo_img = PhotoImage(file="Screenshot 2024-07-02 at 9.56.03.png")
canvas.create_image(100, 100, image=logo_img)
canvas.grid(row=0, column=1)
#Labels
website_label = Label(text="Website:")
website_label.grid(row=1, column=0)
email_label = Label(text="Email/Username:")
email_label.grid(row=2, column=0)
password_label = Label(text="Password:")
password_label.grid(row=3, column=0)

#Entries
website_entry = Entry(width=21)
website_entry.grid(row=1, column=1)
website_entry.focus()
email_entry = Entry(width=35)
email_entry.grid(row=2, column=1, columnspan=2)
email_entry.insert(0, "teru@gmail.com")
password_entry = Entry(width=21)
password_entry.grid(row=3, column=1)

# Buttons
search_button = Button(text = "Search", width = 12, command = find_password)
search_button.grid(row=1, column=2)
generate_password_button = Button(text="Generate Password", command=generate_password)
generate_password_button.grid(row=3, column=2)
add_button = Button(text="Add", width=36, command=save)
add_button.grid(row=4, column=1, columnspan=2)

window.mainloop()
```

### 出力結果
![Screenshot 2024-07-02 at 10.37.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518583/0bcc2055-4c67-351b-3f40-de08245a878c.png)
まず、このような画面が表示される。
今回は、Googleのパスワードをランダムで生成してみた。

### Googleのパスワードをランダムで生成してみた結果
![Screenshot 2024-07-02 at 10.38.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518583/40183083-20f7-4df4-75be-8bd0b5e50d9c.png)
今回は、18文字のパスワードになっている。

次に、このパスワードをdata.jsonを使い、保存してみた。
その場合、Addというボタンを押したら、出力結果の画面に戻る。
その後、WebsiteのところにGoogleと入力し、Searchを押したら、同じパスワードがこのように出力された。
![Screenshot 2024-07-02 at 10.41.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518583/469ed53f-3350-3230-1b14-97878882478b.png)

data.jsonに戻ったら、同じ6F(T3%Q%ZU1t+6(3loというパスワードが保存されていた。
``` python
{
    "Google": {
        "email": "teru@gmail.com",
        "password": "6F(T3%Q%ZU1t+6(3lo"
    }
}
```
注意：私のメールアドレスはteru@gmail.comではありません。
Gmailでまだ使用されていないアドレスを使って、実験してみただけです。

今回は、Googleのパスワードを保存する方法を紹介しましたが、他のサイトももちろん保存できます。皆さんもこのプログラムコードを参考にして、GUIでPassword Managerを、作ってみてはいかがでしょうか。