Wednesday, November 23, 2022

Python - Project - Quiz App (Day 34)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Project - Quiz App



Revise the Quiz Project in Day 17.
    
#1 Call Trivia API to get the question instead of hard-coded in txt file
    
#2 Use Tkinter to improve the User Experience

#3 Review OOP Design



Ex: data.py
import requests

# Prepare parameters for API
parameters = {
    "amount": 10,
    "category": 18, # Science - Computers
    "type": 'boolean',
}
# Sends a GET request.
response = requests.get("https://opentdb.com/api.php", params=parameters)
# Raises HTTPError, if one occurred.
response.raise_for_status()
# Returns the json-encoded content of a response, if any.
res= response.json()
question_data = res["results"]


Ex: question_model.py
class Question:

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


Ex: quiz_brain.py
import html

class QuizBrain:

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

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

    def next_question(self):
        self.current_question = self.question_list[self.question_number]
        self.question_number += 1
        question_text = html.unescape(self.current_question.text)

        return f"Q.{self.question_number}: {question_text} (True/False): "

    def check_answer(self, user_answer: str) -> bool:
        correct_answer = self.current_question.answer
       
        if user_answer.lower() == correct_answer.lower():
            self.score += 1
            return True

        return False


Ex: ui.py
import tkinter as tk
from quiz_brain import QuizBrain

THEME_COLOR = "#375362"


class QuizInterface:
    def __init__(self, quiz_brain: QuizBrain):
        self.quiz_brain = quiz_brain

        # Init Tkinter
        self.root = tk.Tk()
        self.root.title("Quizzler")
        self.root.config(padx=20, pady=20, background=THEME_COLOR)

        # Score Counter
        self.score_label = tk.Label(
            text="Score: 0",
            foreground='white',
            background=THEME_COLOR)
        self.score_label.grid(row=0, column=1)

        # Init canvas
        self.canvas = tk.Canvas(
            width=300, height=250, background='white', highlightthickness=0
        )
        # Question Text
        self.question_text = self.canvas.create_text(
            150, 125, width=280, text="",
            fill="black", font=('Arial', 15, "italic")
        )
        self.canvas.grid(row=1, column=0, columnspan=2, pady=50)

        # True Btn
        true_image = tk.PhotoImage(file="images/true.png")
        self.true_btn = tk.Button(
            image=true_image, command=self.true_btn_clicked)
        self.true_btn.config(
            background=THEME_COLOR,
            activebackground=THEME_COLOR)
        self.true_btn.grid(row=2, column=0)

        # False Btn
        false_image = tk.PhotoImage(file="images/false.png")
        self.false_btn = tk.Button(
            image=false_image, command=self.false_btn_clicked)
        self.false_btn.config(
            background=THEME_COLOR,
            activebackground=THEME_COLOR)
        self.false_btn.grid(row=2, column=1)

        # Get the next question
        self.get_next_question()

        # Start the Event Loop
        self.root.mainloop()

    def get_next_question(self):
        # Config white bg for canvas
        self.canvas.config(background='white')

        # No more questions
        if not self.quiz_brain.still_has_questions():
            # Show game over info
            self.canvas.itemconfig(self.question_text, text="No more questions")
            # Disable buttons
            self.true_btn.config(state='disabled')
            self.false_btn.config(state='disabled')
            return

        # Not finish all questions yet
        quiz_text = self.quiz_brain.next_question()
        self.canvas.itemconfig(self.question_text, text=quiz_text)

    def true_btn_clicked(self):
        self.show_feedback(self.quiz_brain.check_answer("true"))

    def false_btn_clicked(self):
        self.show_feedback(self.quiz_brain.check_answer("false"))

    def show_feedback(self, is_correct: bool):
        # Config bg of canvas based on the correction of answer
        if is_correct:
            self.canvas.config(background='green')
        else:
            self.canvas.config(background='red')

        # Update Score Label
        self.score_label.config(text=f"Score: {self.quiz_brain.score}")

        # 1 second delay show the next question
        self.root.after(1000, self.get_next_question)


Ex: main.py
from question_model import Question
from data import question_data
from quiz_brain import QuizBrain
from ui import QuizInterface

question_bank = []
for question in question_data:
    question_text = question["question"]
    question_answer = question["correct_answer"]
    new_question = Question(question_text, question_answer)
    question_bank.append(new_question)

quiz = QuizBrain(question_bank)
quiz_ui = QuizInterface(quiz)


No comments:

Post a Comment