UdemyでPythonを勉強した結果を残すブログ。

40歳でプログラミング始めて転職までいけるのかを実録してみます。

DAY34 APIでデータを自動的に取得して毎回質問の違うクイズアプリを作る!

今回はかなり難しくてできなかったのでいい復習になった。

それぞれの機能をわけて1つのアプリとして作ります。

 

今回はDAY17でやったクイズアプリを、コンソールではなく

tkinterGUIを使ってより遊びやすくするアプリを作成します。

 

また質問もTRIVIA APIから毎回取得して

毎回違う問題を出すようにします。

 

opentdb.com

 

UIはこんな感じ。

 

 

 

APIからデータを取得するファイルを作成する

data.py

import requests

params = {
"amount": 10,
"category": 18,
"difficulty": "easy",
"type": "boolean"
}
response = requests.get("https://opentdb.com/api.php", params=params)
response.raise_for_status()
response = response.json()
quiz_list = response["results"]

今回のAPIはキーも要らず簡単なものなので、シンプルに。

requestsでapiのエンドポイントにアクセス。

raise_for_statusをすることでアクセスエラーが起きた時にどのエラーかを教えてくれるようにしています。

問題なかったらjson()でjsonフォーマットに。

quiz_listで余計な部分は飛ばして整形しています。

 

クイズ内容を質問・答えのセットとしたオブジェクトとして管理する

整形したjsonをアプリで出力しやすくするためにオブジェクト化します。

クイズモデルとして1つのクラスを作成します。

 

quiz_model.py

class Question:

def __init__(self, q_text, q_answer):
self.text = q_text
self.answer = q_answer

これはjsonの中にあるクイズ内容と答えをセットにするテンプレみたいなものを作っています。

 

main.py

from data import quiz_list
from quiz_model import Question

quiz_bank = []

for quiz in quiz_list:
q_text = quiz["question"]
q_ans = quiz["correct_answer"]
new_quiz = Question(q_text, q_ans)
quiz_bank.append(new_quiz)

quiz = QuizMachine(quiz_bank)

起動ファイルでjsonデータとモデルデータをインポートして、

for文で1つずつ質問と答えをとりだし、new_quizでオブジェクト化をして

quiz_bankのリストに入れていきます。

 

最後のQuizMachineでは整形したデータを取得して

クイズゲームを担当する機能で、次に実装していきます。

 

 

 

クイズを扱う機能のファイルを作成する

quiz_machine.py

import html


class QuizMachine:

def __init__(self, quiz_bank):
self.question_number = 0
self.score = 0
self.quiz_bank = quiz_bank
self.current_question = None

def still_has_questions(self):
return self.question_number < len(self.quiz_bank)

def next_question(self):
self.current_question = self.quiz_bank[self.question_number]
self.question_number += 1
q_text = html.unescape(self.current_question.text)
return f"Q.{self.question_number}: {q_text}"

def check_answer(self, user_answer):
correct_answer = self.current_question.answer
if user_answer.lower() == correct_answer.lower():
self.score += 1
return True
else:
return False

QuizMachineというクラスを作ります。

 

整形したデータはmain.pyでさらに出題しやすいように抽出されたデータを作るのですが、

そのデータからクイズを出す機能を作ります。

主に

  • データを取得する
  • スコアの設定
  • データがある分次々と出題する
  • 正解かを判断する

です。

上から説明すると、init部分では初期状態として、スコアと取得したデータ、今何問目かを初期化します。selfの次にquiz_bankがある通り、このクラスを使う時は()にquiz_bankを入れることで機能します。

 

still_has_questionは取得したデータの数を数えて、その分だけ数を出力するもの。

出力されなかったらクイズ終了の表示をUIのファイルがする予定。

 

next_questionでは初期値のquestion_numberのリストの内容を取得してから1を足して

質問内容を出力しています。

これをすることでリストとしては0番目の内容が見た目は1番目として見せることができます。

 

htmlをインポートしているのは、質問の中には &quot;+= 1&quot のように

ウェブサイト上で表記するための記号がある場合があります。

それをpython上でもきちんと表示されるように変換するためのファンクション = unescapeを使うためにインポートしています。

 

UIを作成する

 

design.py

 

from tkinter import *
from quiz_machine import QuizMachine

THEME_COLOR = "#efefef"


class QuizUI:

def __init__(self, quiz_brain: QuizMachine):
self.quiz = quiz_brain

self.window = Tk()
self.window.title("Quizzler")
self.window.config(padx=20, pady=20, bg=THEME_COLOR)

self.score_label = Label(text="Score: 0", fg="#333", bg=THEME_COLOR)
self.score_label.grid(row=0, column=0, columnspan=2)

self.canvas = Canvas(width=300, height=250, bg="white")
self.question_text = self.canvas.create_text(
150,
125,
width=280,
text="Some Question Text",
fill="#333",
font=("Arial", 20, "italic")
)
self.canvas.grid(row=1, column=0, columnspan=2, pady=50)

true_image = PhotoImage(file="images/maru.png")
self.true_button = Button(image=true_image, highlightthickness=0, command=self.true_pressed)
self.true_button.grid(row=2, column=0)

false_image = PhotoImage(file="images/batsu.png")
self.false_button = Button(image=false_image, highlightthickness=0, command=self.false_pressed)
self.false_button.grid(row=2, column=1)

self.get_next_question()

self.window.mainloop()

 

UIにQuizMachineをインポートすることでUIファイルでQuizMachineの機能が使えるようになります。

innitに(self, quiz_brain: QuizMachine)を入れて、

self.quiz = quiz_brainを入れることで継承ができます。

 

あとはtkinterの今まで使った機能と同じ感じ。

丸ボタンを押すとtrue_pressed、バツボタンでfalse_pressedという機能を発火します。

 

一通りのUIができたので、最後にクイズを取得する機能 = get_next_question()を入れて

innit部分は終了。

 

次に、UI上の変化として

  • クイズが残っていればクイズを表示する
  • 丸ボタンを押すとTrue、バツボタンを押すとFalseを返す
  • 答えと合っていればスコアを+1にする
  • 背景を正解の色(丸なら青、バツなら赤)に変化する。

を実装していきます。

 

design.py


def get_next_question(self):
self.canvas.config(bg="white")
if self.quiz.still_has_questions():
self.score_label.config(text=f"Score: {self.quiz.score}")
q_text = self.quiz.next_question()
self.canvas.itemconfig(self.question_text, text=q_text, fill="#333")
else:
self.score_label.config(text=f"Final Score: {self.quiz.score}")
self.canvas.itemconfig(self.question_text, text=f"Quiz is End!\nYour Score is {self.quiz.score}", fill="#333")
self.true_button.config(state="disabled")
self.false_button.config(state="disabled")

def true_pressed(self):
self.give_feedback(self.quiz.check_answer("True"))

def false_pressed(self):
is_right = self.quiz.check_answer("False")
self.give_feedback(is_right)

def give_feedback(self, is_right):
if is_right:
self.canvas.config(bg="blue")
self.canvas.itemconfig(self.question_text, fill="#fff")
else:
self.canvas.config(bg="red")
self.canvas.itemconfig(self.question_text, fill="#fff")
self.window.after(1000, self.get_next_question)

 

self.quiz.機能 は、QuizMachineに記述したdefの機能を読み込ませています。

一番最初に背景を白に設定しているのは答えを押した後に背景が青か赤になってしまっているのでそれをクリアするためです。

最初にstill_has_questionsを使うことでクイズがあるかどうかを判断しています。

そこからクイズがあればテキストをnext_question()を起動して

return として出力している Q1: クイズ のテキストをq_text の変数に格納し、

クイズ表示部分に表示をしています。

 

 

true_pressed, false_pressedはcheck_answer()を起動して

True,またはFalseを入れることで

correct_answer と同じかどうかをチェックしてあっていたらスコア+1でTrueを返す、

間違っていたらFalseを返します。

という機能にしています。

 

自分の答えが合っていると+1をしてTrueを返す quiz.check_answerですが、

Falseの時だけ is_rightという機能が付いています。

それは下のgive_feedbackの部分で正解だったら青背景、間違っていたら赤背景にするためです。

 

 

UI部分もできたのでmain.pyの最後にUIのクラスを追加して出来上がり。

 

ファイルを行き交う継承とか結構ややこしい。。。

 

Gitはこちら

github.com

sourcetree で git がプッシュできなくなってたけど解決した

udemyのレッスン途中からなぜかpushできなくなって

あきらめてsourcetree使うのやめてターミナルからやってたんだけど

 

plaza.rakuten.co.jp

 

こちらの記事を見てようやく解決した。

 

トークンをとって、gitのアドレスのhttp://の後にいれて、@をつける

そのあとに残りのgitアドレスを追加。

 

https://ghp_tokenhogehoge@github.com/user_name/リポジトリ名).git

 

な感じ。

DAY33 APIからデータをもらってISSが頭上に来た時にメールして知らせるアプリを作る!

今回のレッスンはAPIを呼び出してデータを取得し、

自分のプログラムに活用する練習としてISSが見えそうな時にメールをする簡易アプリを作成します。

 

 

このサイトの時間帯近くにメールが送られると思います。

lookup.kibo.space

 

 

ではやってみます。

このコードの流れは

  • 日の出日の入り時間をAPIで取得し、変数に入れる
  • ISSが自分の場所から見えそうな場所になるのを確認する
  • 時刻が夜かつISSが見えそうな場合にメールを送る

 

こんな感じです。

 

今回使うAPIを2つ紹介しておきます。

 

日の出日の入りのわかるAPI

sunrise-sunset.org

 

緯度と経度を

https://api.sunrise-sunset.org/json

の後ろにパラメーターで入れることで日の出日の入りをjsonで出力してくれます。

 

api.open-notify.org

 

ISS LOCATION NOWのリンクで常に今どこにいるかが緯度経度でjson出力されます。

 

 

 

まずこのサイトとかで自分の場所の緯度経度を取得します。

user.numazu-ct.ac.jp

 

今回は大阪駅を観測場所にしています。

 

準備から。

 

 

import time

import requests
import datetime
import smtplib

import os
from os.path import join, dirname
from dotenv import load_dotenv
dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
password = os.environ.get("PASSWORD") # 環境変数
my_email = os.environ.get("MY_EMAIL")

LAT = 34.700836
LON = 135.493179
params = {
"lat": LAT,
"lon": LON
}

 

今回もセキュリティとか実務も考えて環境変数を使っています。

dotenvはターミナルでpip install python-dotenv でインストールします。

requestsはapiからデータを取得する際に使うモジュール。

 

paramsは日の出日の入りの時間をapiから取得する際にいるリストです。

ISSの緯度経度の時にも使うため固定変数として格納しています。

 

 

夜の時間に限定した機能を実装


def night_time():
now = datetime.datetime.now()
response2 = requests.get(url="https://api.sunrise-sunset.org/json", params=params).json()
sunset_hour = response2["results"]["sunset"].split(":")[0]
sunrise_hour = response2["results"]["sunrise"].split(":")[0]
if now.hour > int(sunset_hour) or now.hour < int(sunrise_hour):
return True

 

夜じゃないと見えないので、夜だけの条件をファンクションとしています。

datetimeで今の日付を取得し、apiで日の出日の入り時間を取得しています。

●時だけわかればいいので、splitで:のつくところで分けて最初の数字だけ取得します。

 

今の時間が24時間換算で日の入り時刻よりあと、日の出時刻より前であれば

1つ目の上件が当てはまる= Trueというようにしています。

 

 

ISSが見える場所ならTrueを出力する機能を実装


def iss_overhead():
response = requests.get(url="http://api.open-notify.org/iss-now.json").json()
iss_lat = float(response["iss_position"]["latitude"])
iss_lon = float(response["iss_position"]["longitude"])
if LAT - 3 <= iss_lat <= LAT + 3 and LON - 3 <= iss_lon <= LON + 3:
return True

 

apiからjsonで取得したデータを緯度と経度の変数で格納したあと、

現在地とISSの緯度経度の差異が+-3の範囲でならTrueというようにしています。

 

60秒ごとにプログラムを回して該当したらメールを送る


while True:
time.sleep(60)
if night_time() and iss_overhead():
with smtplib.SMTP("smtp.gmail.com", port=587) as connection:
connection.starttls()
connection.login(user=my_email, password=password)
connection.sendmail(
from_addr=my_email,
to_addrs=my_email,
msg=f"Subject:find ISS!\n\nLook Up!")
break

 

whileを使って、2つの条件が重なったらメールを送るようにしています。

レッスンではbreakがなかったので、breakなかったら該当の時間になったら

1分ごとにメールが送られるんじゃないか?と思ったのですがどうなんでしょう。

テストしていないのでわかりませんが…

 

一応今回はPCで起動した場合でのコーディングですが、

毎日このプログラムを起動させるのであれば前回記事に書いた

pythonanywhereに登録するとPCを起動させずともクラウド上で動いてくれます。

 

Gitはこちら。

github.com

 

 

 

 

クラウドで自動でpythonを起動してくれるサービス pythonanywhere

www.pythonanywhere.com

 

毎日自分でPCを開かなくても勝手にpythonを起動してくれるクラウドサービスです。

 

自動メール送信のレッスン時に使ったので別個で投稿。

 

今回は自動メール送信のファイルをアップロードしてみます。

go-python.hatenablog.com

 

 

 

アカウントを作って、Filesにファイルを入れて(1つずつなのが若干面倒だが)

 

consoleで.pyを起動してきちんと起動するか確認する。

 


最後にtaskで日付を設定する。


時間はUTC(世界協定時間)なので、日本だと9時間早いので

日本時間に合わせたかったらUTCの時間 +9 で設定。

 

無料分では1つしかできないので、勉強がてら練習するくらいですね。

 

環境変数も使える

consoleで環境変数を設定できます。

help.pythonanywhere.com

 

オフィシャルでの設定方法ですが、ざっくり言うとconsoleで

echo "export SECRET_KEY=sekritvalue" >> .env
echo "export OTHER_SECRET=somethingelse" >> .env

で.envにPCで設定した変数を入れ込みます。(""はいらない)

また同じくconsoleで

pip install --user python-dotenv

を入力してモジュールもインストールします。

 

あとはファイルをアップロードしたパスが違っていれば調整すれば使えます。

 

 

DAY32 csvに登録された人の誕生日の日に自動でメールを送るアプリを作る!

今回はsmtplibというモジュールを使って

python上からメールを送るレッスン。

 

pythonはライブラリのインポートが簡単で使いやすいですね。

(他の言語は知らないけども)

 

ついでに、Gitなどでアップする時に間違えて大事な情報とかもアップしないように

.envを使った環境変数を使ったコーディングもしていきます。

 

環境変数とは…

https://wa3.i-3-i.info/word11027.html

 

ここのリンクでざっくりわかりますが、さらにざっくり言うならば

python上の変数ではなく、使っているPCのos上で作られる変数。

なのでpythonファイルに直接大事な情報はコーディングしなくても動かせます。

 

 

自動メール送信アプリの流れはこちら

  • 誕生日や名前、メアドを入力したcsvをインポートする
  • 今日と誕生日を参照して、今日が誕生日の人がいたらメールを送る
  • メールは毎年同じにならないようにランダムにメールテンプレートを作る

 

smptモジュールは型を覚えるだけ

go-python.hatenablog.com

 

ちょうどこのレッスンを最初に受けた時に覚えとこうと思って書いたtips。

これは環境変数使ってない直書きなので公開の際には使えないけど。。

 

 

コーディングしていきます。

 

import smtplib
import datetime as dt
import pandas as pd
import random

import os
from os.path import join, dirname
from dotenv import load_dotenv

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
password = os.environ.get("PASSWORD") # 環境変数の値をAPに代入
my_email = os.environ.get("MY_EMAIL")

now = dt.datetime.now()
data = pd.read_csv("birthdays.csv")
data_dic = data.to_dict(orient="records")

 

import os〜("MY_EMAIL")のコードは先ほど言っていた環境変数を使うモジュールで、

普通環境変数を使う時はターミナルを使ってやるのだけども

検索してみたらこれが出てきたので、これならプロジェクトごとに環境変数管理しやすいし見やすいしいいじゃんと思って採用しています。

 

こちらを参考にしました。

qiita.com

 

あとは今日を取得するためにdatetime、レターのテンプレをランダムに取得するためにrandomモジュールをインポートしています。

 

pandasでcsvを整形して辞書型リストにフォーマットしています。

 

csvの入力例はこんな感じ

name,email,year,month,day
dad,dad-sample@emailadmin.com,1961,5,10

 

今日が誕生日の人を確認して該当するならメールを送る


for i in range(len(data_dic)):
if now.month == data_dic[i]["month"] and now.day == data_dic[i]["day"]:
with open(f"letter_templates/letter_{random.randint(1,3)}.txt") as mail_temp:
letter = mail_temp.read().replace("[NAME]", data_dic[i]["name"])
with smtplib.SMTP("smtp.gmail.com", port=587) as connection:
connection.starttls()
connection.login(user=my_email, password=password)
connection.sendmail(
from_addr=my_email,
to_addrs=data_dic[i]["email"],
msg=f"Subject:Happy Birthday!\n\n{letter}")
else:
print("Not birthday")

 

forでリストの中にある人を全部見ていき、

月と日が同じであればメールテンプレをランダムで取得し、

replaceで[NAME]の部分をcsvのnameに入れている名前に変更。

 

次にメールを開いて型通りに入力していきます。

smtpのサーバーは今回はgmailにしています。gmailにしている場合、port=587がないと正常に動かなかったので入れています。

 

詳しく知りたい方はこちら

qiita.com

 

誕生日が該当しない場合はNot birthdayというのを出力しますが、

特になくてもいいです。

 

 

今回はこれだけ。Gitはこちらです。

github.com

DAY31 単語を覚えるフラッシュカードアプリをtkinterで作る!

今回はtkinterを駆使してフラッシュカードアプリを作ります。

デジタル単語帳みたいな感じで、表に覚えたい言語、裏に母国語を書いて覚えるというもの。

あんまりこれで覚えても実用的じゃない気もしますが、レッスンとして。

 

 

 

表部分はフランス語、裏は英語としています。

3秒間フランス語が出た後に自動的に裏カードとして英単語が出てきます。

下のバツとチェックのボタンは、覚えたらチェックボタンを押すと

その単語は次から出なくなるという動作をします。

 

 

流れとしては

・前回までに覚えていない部分のcsvを読み込む

・初めての場合は全ての単語が入っているcsvを読み込む

・フラッシュカードがスタートし、csvからランダムに単語を選ぶ。

・3秒間覚える単語を表示する

・ボタンを押さずにそのまま何もしない場合は3秒後に母国語を表示する

・チェックボタンを押すと全ての単語が入っているcsvから今表示した単語を削除して前回に覚えていない部分のcsvとして保存をする

バツボタンを押すと削除はしない

・どちらのボタンを押しても、押した後に次の単語がランダムに表示される。

・ウィンドウを閉じると終了する



 

こんな感じ。

 

では始めます。

 

tkinterでUIをレイアウトする

 

from tkinter import *
import pandas as pd
import random

BG_COLOR = "#B1DDC6"

window = Tk()
window.title("Flash Card")
window.minsize(width=800, height=650)
window.config(bg=BG_COLOR, pady=50, padx=50)


img_right = PhotoImage(file="right.png")
img_wrong = PhotoImage(file="wrong.png")
img_card_front = PhotoImage(file="card_front.png")
img_card_back = PhotoImage(file="card_back.png")

//
def 入れるところ
//


canvas = Canvas(width=800, height=560, highlightthickness=0, bg=BG_COLOR)
canvas_bg = canvas.create_image(400, 280, image=img_card_front)
canvas.grid(row=0, column=0, columnspan=2)

right_btn = Button(image=img_right, highlightbackground=BG_COLOR, command=delete_word)
right_btn.grid(row=1, column=1)

wrong_btn = Button(image=img_wrong, highlightbackground=BG_COLOR, command=next_word)
wrong_btn.grid(row=1, column=0)

title = canvas.create_text(400, 100, text="", font=("Futura", 30, "italic"))
word = canvas.create_text(400, 250, text="", font=("Futura", 60, "italic"))

card_flip = window.after(3000, func=card_back)

next_word()
window.mainloop()

 

主にcanvasと2つのボタンのみで、canvasに今回はフランス語・英語のタイトルと

それぞれの単語を表示するようにします。

text=が空白なのは、すぐにランダムで単語を選んで表示するので表示する必要がないため。

 

チェックボタンにはdelete_word、バツボタンにはnext_wordという関数をいれていて、

delete_wordはcsvから単語を消して続ける機能、

next_wordは消さずに次の単語を表示するというもの。

 

アニメーションする機能は前回もやったwindow.afterの関数を使う。

3秒= 3000ms を超えたらfunc=card_backを起動します。

 

 

最初に読み込むcsvを決めて辞書型リストにする


try:
df = pd.read_csv("to_learn.csv")
except FileNotFoundError:
df = pd.read_csv("french_words.csv")

df_dic = df.to_dict(orient="records")

 

ここではtry、exceptを使って読み込むcsvを選びます。

最初の場合は覚えた単語を削除したcsvのto_learn.csvはないのでエラーになるので、

そうなった場合はデフォルトcsvのfrench_words.csvをpandasで読み込みます。

orient="records"というのは、通常であれば

{'French': {0: 'partie', 1: 'histoire', 2: 'seulement',...

というような辞書型リストになりますが、これを使うことにより

[{'French': 'partie', 'English': 'part'}, と表の1列ずつにまとまった辞書リストにフォーマットしてくれるオプションです。

 

 

単語をランダムに選んで表示する機能 = next_card を実装する

 

get_word = {}

def next_word():
global get_word, card_flip
window.after_cancel(card_flip)
get_word = random.choice(df_dic)
get_word_fr = get_word["French"]
canvas.itemconfig(title, text="French", fill="#000")
canvas.itemconfig(word, text=get_word_fr, fill="#000")
canvas.itemconfig(canvas_bg, image=img_card_front)
card_flip = window.after(3000, func=card_back)

 

最初にget_word を空の辞書リストで入れているのは、next_wordだけでなく

他の関数でも使うため。

globalでcard_flipを入れているのはmainloop手前のものを入れています。

 

というのもそれがないとエラーになってしまうのと

このあとにafter_cancel()を入れてボタンを3秒以内にクリックした時に

その時に起動しているcard_flipを一旦クリアしてから新しい単語で再度3秒カウントするのをバグなく起動したいからです。

 

ここで単語をランダムに選んでいるのと同時に、カードが裏返っているので

文字の色や背景などを初期状態に戻す処理をcanvas.itemconfigでいれています。

 

最後に再度3秒カウントするためのcard_flipを入れて終わり。

 

 

カードを裏返す機能= card_backを実装する


def card_back():
canvas.itemconfig(title, fill="white", text="English")
canvas.itemconfig(word, fill="white", text=get_word["English"])
canvas.itemconfig(canvas_bg, image=img_card_back)

 

これは先ほどの初期状態のものを裏返しバージョン。

 

 

覚えた単語を削除して新しいcsvに保存する処理を実装する


def delete_word():
df_dic.remove(get_word)
to_learn_df = pd.DataFrame(df_dic)
to_learn_df.to_csv("to_learn.csv", index=False)
next_word()

 

シンプルにremoveでその時表示している部分 = get_wordをごっそり削除した後に、

その辞書型リストをpandasでデータフレームに整形しなおし、csvで保存。

index=Falseにしているのはこれをしないとindexの数字がどんどん増えていってしまうため。

これが終わったらnext_word()で引き続き次の単語をランダムで選ぶようにしています。

 

 

 

ソースはGitにて。

github.com

DAY30 パスワード管理・作成ツールに保存しているパスワードを表示する機能を作る!try,except,else,finallyの勉強も

DAY29のGitにpushしたので結果的にソース一緒になってしまいましたが…

 

前回のパスワード管理ツールにsearchボタンを追加して、

websiteに入力されたサイトにパスワードが保存されていれば

表示する機能を追加します。

 

 

searchボタンを追加し、レイアウトをし直す

website_area = Label(text="website", font=("Futura", 15, "italic"),anchor="w")
website_area.grid(row=1, column=0)

website_input = Entry(textvariable="website url", width=26)
website_input.grid(row=1, column=1, sticky="w")

search_btn = Button(text="search", command=pass_search, width=10)
search_btn.grid(row=1, column=2, sticky="w")

user_area = Label(text="user/email", font=("Futura", 15, "italic"),anchor="w")
user_area.grid(row=2, column=0)

user_input = Entry(textvariable="user", width=40)
user_input.grid(row=2, column=1, columnspan=2, sticky="w")

pass_area = Label(text="password", font=("Futura", 15, "italic"),anchor="w")
pass_area.grid(row=3, column=0, )

pass_gen_btn = Button(text="generate", command=gen_pass, width=10)
pass_gen_btn.grid(row=3, column=2, sticky="w")

pass_input = Entry(width=26)
pass_input.grid(row=3, column=1, sticky="w")

searchボタンをwebsiteインプットの横に配置するので、

他のボタンを調整します。主にcolumnspan部分。

 

 

csvからjsonに保存ファイルを変更する

検索しやすいようにjsonファイルでの保存に変更します。

def pass_add():
website = website_input.get()
user = user_input.get()
password = pass_input.get()
new_data = {
website : {
"user" : user,
"password" : password,
}
}
if website == "" or user == "" or password =="":
messagebox.showinfo("Alert", "Fill All Input!")

else:
try:
with open("pass_list.json", "r") as json_data:
data = json.load(json_data)
except FileNotFoundError:
with open("pass_list.json", "w") as json_data:
json.dump(new_data, json_data, indent=4)

else:
data.update(new_data)
with open("pass_list.json", "w") as json_data:
json.dump(data, json_data, indent=4)
finally:
messagebox.showinfo("Alert", "Password Added!")
website_input.delete(0, END)
pass_input.delete(0, END)
user_input.delete(0, END)
website_input.focus()

 

new_dataの辞書リストでjsonに追加するようにデータを整えます。

次にtry、except、else、finallyを勉強がてら追加。

tryでpass_list.jsonを開き、ない場合はexceptを起動して最初のデータとしてnew_dataを入力します。

tryが成功した場合はすでにファイルがある状態なので、elseを起動。

data.update(new_data)でjsonの状態を追加した状態に更新してから

json.dumpで保存します。

jsonへの保存がこれで終わったので、

修正前と同じくfinally部分でアラートを追加して入力したデータを全て削除・

ウェブサイトのインプットがクリックされた初期状態に戻ります。

 

 

searchボタンの機能を実装する

入力したwebsite部分をgetし、jsonの中にあるかどうかをif ... in ... で確認。

あれば保存したuserとpasswordをアラートで表示するようにします。

 

 

def pass_search():
website = website_input.get()
try:
with open("pass_list.json") as data:
df = json.load(data)
except FileNotFoundError:
messagebox.showinfo("Alert", "Create first password.")
else:
if website in df:
messagebox.showinfo("Alert", f'user:\n{df[website]["user"]}\npassword:\n{df[website]["password"]}')
else:
messagebox.showinfo("Alert", "No User/Password exists.")

 

 

これで機能追加完了。

website部分に文字補完機能とかできればいいな。。。

 

Gitはこちら

github.com