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