GUI Quiz App in Python.
About the project: This is a project for GUI Quiz App in Python . This will be a standalone script that uses Python's built-in tkinter library to create a graphical user interface.
This application will present a series of multiple-choice questions, allow you to select an answer, and then provide feedback on whether your answer was correct.
At the end of the quiz, it will display your final score. You won't need to install any external libraries to run this, as tkinter comes with a standard Python installation.
This script provides a complete and interactive GUI Quiz App.
You can save the code as a Python file (e.g., quiz_app.py) and run it directly. The tkinter library will handle the window creation and button interactions for you.
Project Level: Intermediate
You can directly copy the below snippet code with the help of green copy button, paste it and run it in any Python editor you have.
Steps: Follow these stepsStep 1: Copy below code using green 'copy' button.
Step 2: Paste the code on your chosen editor.
Step 3: Save the code with filename and .py extention.
Step 4: Run (Press F5 if using python IDLE)
# quiz_app.py
import tkinter as tk
from tkinter import messagebox
class QuizApp:
def __init__(self, root):
"""
Initializes the main application window and quiz data.
"""
self.root = root
self.root.title("Python Quiz App")
self.root.geometry("600x400")
self.root.config(bg="#f0f0f0")
# Sample quiz questions
self.questions = [
{
"question": "What is the capital of France?",
"options": ["London", "Berlin", "Paris", "Rome"],
"answer": "Paris"
},
{
"question": "Which planet is known as the 'Red Planet'?",
"options": ["Earth", "Mars", "Jupiter", "Venus"],
"answer": "Mars"
},
{
"question": "What is the largest ocean on Earth?",
"options": ["Atlantic Ocean", "Indian Ocean", "Arctic Ocean", "Pacific Ocean"],
"answer": "Pacific Ocean"
},
{
"question": "Who wrote 'To Kill a Mockingbird'?",
"options": ["Harper Lee", "Mark Twain", "Ernest Hemingway", "F. Scott Fitzgerald"],
"answer": "Harper Lee"
},
{
"question": "What is the chemical symbol for gold?",
"options": ["Au", "Ag", "Fe", "Pb"],
"answer": "Au"
}
]
self.current_question_index = 0
self.score = 0
self.selected_option = tk.StringVar(value="")
self.create_widgets()
def create_widgets(self):
"""
Creates all the GUI elements for the quiz.
"""
# Create a frame for the quiz content
self.quiz_frame = tk.Frame(self.root, padx=20, pady=20, bg="#ffffff")
self.quiz_frame.pack(pady=20, padx=20, fill="both", expand=True)
# Question label
self.question_label = tk.Label(self.quiz_frame, text="", font=("Helvetica", 16), wraplength=500, bg="#ffffff")
self.question_label.pack(pady=20)
# Options frame
self.options_frame = tk.Frame(self.quiz_frame, bg="#ffffff")
self.options_frame.pack(pady=10)
self.option_buttons = []
for i in range(4):
option_button = tk.Radiobutton(self.options_frame, text="", font=("Helvetica", 12), variable=self.selected_option, value="", bg="#ffffff", activebackground="#e0e0e0")
option_button.pack(anchor="w", pady=5)
self.option_buttons.append(option_button)
# Submit button
self.submit_button = tk.Button(self.root, text="Submit", command=self.check_answer, font=("Helvetica", 14), bg="#4CAF50", fg="white", activebackground="#45a049", relief="flat")
self.submit_button.pack(pady=(0, 20), ipadx=10, ipady=5)
self.display_question()
def display_question(self):
"""
Updates the GUI to show the current question and options.
"""
if self.current_question_index < len(self.questions):
question_data = self.questions[self.current_question_index]
self.question_label.config(text=question_data["question"])
# Reset selected option
self.selected_option.set("")
# Update option buttons
for i in range(4):
self.option_buttons[i].config(text=question_data["options"][i], value=question_data["options"][i])
else:
self.show_results()
def check_answer(self):
"""
Checks if the selected option is correct, updates the score, and moves to the next question.
"""
user_answer = self.selected_option.get()
if user_answer:
correct_answer = self.questions[self.current_question_index]["answer"]
if user_answer == correct_answer:
self.score += 1
messagebox.showinfo("Result", "Correct!")
else:
messagebox.showerror("Result", f"Incorrect. The correct answer was: {correct_answer}")
self.current_question_index += 1
self.display_question()
else:
messagebox.showwarning("Warning", "Please select an option before submitting.")
def show_results(self):
"""
Displays the final score to the user and closes the app.
"""
messagebox.showinfo("Quiz Complete", f"Quiz finished!\nYour final score is: {self.score}/{len(self.questions)}")
self.root.destroy()
def main():
"""
Main function to initialize and run the application.
"""
root = tk.Tk()
app = QuizApp(root)
root.mainloop()
if __name__ == "__main__":
main()
Below is another code snippet with more robust, including a timer for each question, the ability to load questions from external files, and more elaborate styling.
- File Loading: You can now load questions from an external file via the File menu. The app supports both JSON and CSV formats.
- JSON Format: Your file should be an array of objects, with each object containing "question", "options", and "answer" keys.
- CSV Format: Your file should have a header row and each subsequent row should contain the question, followed by four options, and finally the correct answer.
- Timer: A countdown timer is now displayed in the top-right corner of the window. If the timer runs out before you submit an answer, the question is automatically marked as incorrect and the quiz moves on.
- Styling: The UI has been updated with a more modern, dark theme. The buttons have a flat style and the layout uses frames to provide better spacing and visual appeal.
# quiz_app.py
import tkinter as tk
from tkinter import messagebox, filedialog
import json
import csv
import os
class QuizApp:
def __init__(self, root):
"""
Initializes the main application window and quiz data.
"""
self.root = root
self.root.title("Advanced Python Quiz App")
self.root.geometry("700x500")
self.root.config(bg="#34495e") # Dark blue-gray background
# Sample quiz questions (used if no file is loaded)
self.questions = [
{
"question": "What is the capital of France?",
"options": ["London", "Berlin", "Paris", "Rome"],
"answer": "Paris"
},
{
"question": "Which planet is known as the 'Red Planet'?",
"options": ["Earth", "Mars", "Jupiter", "Venus"],
"answer": "Mars"
},
{
"question": "What is the largest ocean on Earth?",
"options": ["Atlantic Ocean", "Indian Ocean", "Arctic Ocean", "Pacific Ocean"],
"answer": "Pacific Ocean"
}
]
self.time_limit = 15 # seconds per question
self.timer_id = None
self.current_question_index = 0
self.score = 0
self.selected_option = tk.StringVar(value="")
# --- Styling & Fonts ---
self.font_title = ("Helvetica", 20, "bold")
self.font_question = ("Helvetica", 16)
self.font_options = ("Helvetica", 12)
self.font_button = ("Helvetica", 14, "bold")
self.create_widgets()
self.create_menu()
self.display_question()
def create_menu(self):
"""Creates the menu bar for loading questions from a file."""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Load Questions (JSON)", command=lambda: self.load_questions_from_file('json'))
file_menu.add_command(label="Load Questions (CSV)", command=lambda: self.load_questions_from_file('csv'))
def load_questions_from_file(self, file_type):
"""
Opens a file dialog to let the user select a question file.
"""
file_path = ""
if file_type == 'json':
file_path = filedialog.askopenfilename(
defaultextension=".json",
filetypes=[("JSON Files", "*.json")]
)
elif file_type == 'csv':
file_path = filedialog.askopenfilename(
defaultextension=".csv",
filetypes=[("CSV Files", "*.csv")]
)
if file_path:
try:
if file_type == 'json':
with open(file_path, 'r') as f:
new_questions = json.load(f)
elif file_type == 'csv':
new_questions = []
with open(file_path, 'r', newline='') as f:
reader = csv.reader(f)
header = next(reader) # Skip header
for row in reader:
if len(row) >= 5: # Ensure the row has enough columns
new_questions.append({
"question": row[0],
"options": [row[1], row[2], row[3], row[4]],
"answer": row[5] if len(row) > 5 else row[1] # Answer can be a specific column or the first option
})
if new_questions:
self.questions = new_questions
self.current_question_index = 0
self.score = 0
messagebox.showinfo("Success", "Questions loaded successfully!")
self.display_question()
else:
messagebox.showerror("Error", "The selected file is empty or invalid.")
except (IOError, json.JSONDecodeError, csv.Error) as e:
messagebox.showerror("Error", f"Could not read the file: {e}")
else:
messagebox.showwarning("Info", "File loading cancelled.")
def create_widgets(self):
"""
Creates all the GUI elements for the quiz.
"""
# Main frame for a centered, clean look
main_frame = tk.Frame(self.root, bg="#2c3e50", padx=15, pady=15)
main_frame.pack(pady=20, padx=20, fill="both", expand=True)
# Timer label
self.timer_label = tk.Label(main_frame, text=f"Time: {self.time_limit}", font=("Helvetica", 14), bg="#2c3e50", fg="white")
self.timer_label.pack(anchor="ne", padx=10, pady=5)
# Question label
self.question_label = tk.Label(main_frame, text="", font=self.font_question, wraplength=600, bg="#2c3e50", fg="#ecf0f1")
self.question_label.pack(pady=(20, 10))
# Options frame with a different color
options_frame = tk.Frame(main_frame, bg="#34495e")
options_frame.pack(pady=10)
self.option_buttons = []
for i in range(4):
option_button = tk.Radiobutton(options_frame, text="", font=self.font_options, variable=self.selected_option, value="",
bg="#34495e", fg="white", selectcolor="#2980b9", activebackground="#34495e",
indicatoron=0, width=40, relief="flat", padx=10, pady=10)
option_button.pack(anchor="w", pady=5)
self.option_buttons.append(option_button)
# Submit button
self.submit_button = tk.Button(main_frame, text="Submit Answer", command=self.check_answer, font=self.font_button,
bg="#27ae60", fg="white", activebackground="#2ecc71", relief="flat", padx=15, pady=8)
self.submit_button.pack(pady=(20, 0))
def display_question(self):
"""
Updates the GUI to show the current question and options.
"""
if self.current_question_index < len(self.questions):
question_data = self.questions[self.current_question_index]
self.question_label.config(text=question_data["question"])
# Reset selected option
self.selected_option.set("")
# Update option buttons
for i in range(4):
self.option_buttons[i].config(text=question_data["options"][i], value=question_data["options"][i])
# Start the timer for the new question
self.time_left = self.time_limit
self.countdown()
else:
self.show_results()
def countdown(self):
"""
Handles the timer for each question.
"""
self.timer_label.config(text=f"Time: {self.time_left}")
if self.time_left > 0:
self.time_left -= 1
self.timer_id = self.root.after(1000, self.countdown)
else:
# Time's up, auto-submit the answer
self.check_answer(timed_out=True)
def check_answer(self, timed_out=False):
"""
Checks if the selected option is correct, updates the score, and moves to the next question.
"""
# Stop the timer before checking
if self.timer_id:
self.root.after_cancel(self.timer_id)
self.timer_id = None
user_answer = self.selected_option.get()
correct_answer = self.questions[self.current_question_index]["answer"]
if not user_answer and not timed_out:
messagebox.showwarning("Warning", "Please select an option before submitting.")
self.time_left = self.time_limit # Reset timer and continue countdown
self.countdown()
return
if user_answer == correct_answer:
self.score += 1
messagebox.showinfo("Result", "Correct!", parent=self.root)
else:
messagebox.showerror("Result", f"Incorrect. The correct answer was: {correct_answer}", parent=self.root)
self.current_question_index += 1
self.display_question()
def show_results(self):
"""
Displays the final score to the user and closes the app.
"""
messagebox.showinfo("Quiz Complete", f"Quiz finished!\nYour final score is: {self.score}/{len(self.questions)}", parent=self.root)
self.root.destroy()
def main():
"""
Main function to initialize and run the application.
"""
root = tk.Tk()
app = QuizApp(root)
root.mainloop()
if __name__ == "__main__":
main()
← Back to Projects