We will learn how to build a Student Quiz System using Python Tkinter, SQLite, and SQLAlchemy.
This project is a Student Quiz System built using Tkinter and SQLAlchemy. The system allows students to take quizzes, records their scores, and displays results in a structured format.
import os
import random
import tkinter as tk
from tkinter import ttk, messagebox
from sqlalchemy import create_engine, text
import ttkbootstrap as tb
current_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(current_dir, "student_quiz.db")
DB_FILE = f"sqlite:///{db_path}"
engine = create_engine(DB_FILE, echo=False)
conn = engine.connect()
def get_questions():
result = conn.execute(text("SELECT id, question, option1, option2, option3, option4, correct_option FROM questions"))
return [dict(row) for row in result.mappings()]
questions = get_questions()
root = tb.Window(themename="superhero")
root.title("plus2net.com Student Quiz System")
root.geometry("650x500")
notebook = ttk.Notebook(root)
notebook.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
quiz_tab = ttk.Frame(notebook)
marks_tab = ttk.Frame(notebook)
notebook.add(quiz_tab, text="π Quiz")
notebook.add(marks_tab, text="π Student Marks")
def start_quiz():
if not student_id.get().isdigit():
messagebox.showerror("Error", "Enter a valid Student ID!")
return
start_btn.grid_forget()
question_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky='w')
show_question()
def next_question():
global current_question_index, score
if selected_option.get() == 0:
messagebox.showwarning("Warning", "Please select an answer!")
return
if selected_option.get() == questions[current_question_index]["correct_option"]:
score += 1
current_question_index += 1
show_question()
def load_student_marks():
tree.delete(*tree.get_children())
data = conn.execute(text("SELECT id, name, class_name, COALESCE(mark, 'Not Attempted') AS mark FROM student"))
for row in data:
tree.insert("", "end", values=row)
import os
import random
import tkinter as tk
from tkinter import ttk, messagebox
from sqlalchemy import create_engine, text
import ttkbootstrap as tb
# β
Database Connection Using SQLAlchemy
current_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(current_dir, "student_quiz.db")
DB_FILE = f"sqlite:///{db_path}"
engine = create_engine(DB_FILE, echo=False)
conn = engine.connect()
# β
Fetch All Questions from Database
def get_questions():
result = conn.execute(text("SELECT id, question, option1, option2, option3, option4, correct_option FROM questions"))
return [dict(row) for row in result.mappings()]
questions = get_questions()
# β
Tkinter Window Setup
root = tb.Window(themename="superhero")
root.title("plus2net.com Student Quiz System")
root.geometry("650x500")
# β
Create Notebook (Tab View)
notebook = ttk.Notebook(root)
notebook.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
# β
Create Tabs
quiz_tab = ttk.Frame(notebook)
marks_tab = ttk.Frame(notebook)
notebook.add(quiz_tab, text="π Quiz")
notebook.add(marks_tab, text="π Student Marks")
# β
Variables
student_id = tk.StringVar()
current_question_index = 0
score = 0
selected_option = tk.IntVar()
# β
GUI: Student ID Input (Inside Quiz Tab)
tk.Label(quiz_tab, text="Enter Student ID:", font=("Arial", 12)).grid(row=0, column=0, padx=10, pady=5, sticky="w")
tk.Entry(quiz_tab, textvariable=student_id, font=("Arial", 12)).grid(row=0, column=1, padx=10, pady=5)
start_btn = tb.Button(quiz_tab, text="Start Quiz", bootstyle="primary", command=lambda: start_quiz())
start_btn.grid(row=0, column=2, padx=10, pady=5)
# β
Question Frame (Initially Hidden)
question_frame = ttk.Frame(quiz_tab)
question_label = tk.Label(question_frame, text="", font=("Arial", 14), wraplength=500)
question_label.grid(row=1, column=0, columnspan=2, pady=10,sticky="nsew")
options = []
for i in range(4):
r = tk.Radiobutton(question_frame, text="", variable=selected_option, value=i+1, font=("Arial", 12))
r.grid(row=i+2, column=0, columnspan=2, sticky="w", padx=20)
options.append(r)
# β
Buttons
next_btn = tb.Button(question_frame, text="Next", bootstyle="success", command=lambda: next_question())
submit_btn = tb.Button(question_frame, text="Submit", bootstyle="danger", command=lambda: finish_quiz())
# β
Display Question
# β
Display Question with Left Alignment
def show_question():
global current_question_index
if current_question_index < len(questions):
q = questions[current_question_index]
question_label.config(text=f"Q{current_question_index+1}: {q['question']}", anchor="w", justify="left")
for i in range(4):
options[i].config(text=q[f"option{i+1}"])
selected_option.set(0)
else:
finish_quiz()
# β
Start Quiz
def start_quiz():
if not student_id.get().isdigit():
messagebox.showerror("Error", "Enter a valid Student ID!")
return
start_btn.grid_forget()
question_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10,sticky='w')
next_btn.grid(row=6, column=0, padx=5, pady=10)
show_question()
# β
Handle Next Question
def next_question():
global current_question_index, score
if selected_option.get() == 0:
messagebox.showwarning("Warning", "Please select an answer!")
return
if selected_option.get() == questions[current_question_index]["correct_option"]:
score += 1
current_question_index += 1
show_question()
# β
Finish Quiz & Update Score in Database
def finish_quiz():
global score
messagebox.showinfo("Quiz Finished", f"Your Score: {score} / {len(questions)}")
# β
Update student marks using SQLAlchemy
conn.execute(text("UPDATE student SET mark = :mark WHERE id = :id"), {"mark": score, "id": int(student_id.get())})
conn.commit()
question_frame.grid_forget()
load_student_marks()
# β
Show Student Marks in Treeview (Inside Marks Tab)
columns = ("ID", "Name", "Class", "Mark")
tree = ttk.Treeview(marks_tab, columns=columns, show="headings", height=8) # Reduced height
for col in columns:
tree.heading(col, text=col)
tree.column(col, width=100, anchor="center") # Reduced column width
tree.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
# β
Load Student Marks into Table
def load_student_marks():
tree.delete(*tree.get_children())
data = conn.execute(text("SELECT id, name, class_name, COALESCE(mark, 'Not Attempted') AS mark FROM student"))
for row in data:
id, name, class_name, mark = row # Ensure proper column mapping
tree.insert("", "end", values=(id, name, class_name, mark))
# β
Load Student Marks Initially
load_student_marks()
# β
Run Tkinter Loop
root.mainloop()
Before using the app we need to set up the database containing students details and quiz questions.
# Import required modules
import os
from sqlalchemy import create_engine, text
# Define database path
current_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(current_dir, "student_quiz.db")
DB_FILE = f"sqlite:///{db_path}"
# Create connection
engine = create_engine(DB_FILE, echo=False)
conn = engine.connect()
# Create Tables
conn.execute(text("DROP TABLE IF EXISTS student"))
conn.execute(text("DROP TABLE IF EXISTS questions"))
# Insert Sample Questions
questions = [
{"question": "What is 2+2?", "option1": "3", "option2": "4", "correct_option": 2}
]
conn.execute(text("INSERT INTO questions (question, option1, option2, correct_option) VALUES (:question, :option1, :option2, :correct_option)"), questions)
# Commit and Close
conn.commit()
conn.close()
import os
from sqlalchemy import create_engine, text
# β
Get the absolute path of the script's directory
current_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(current_dir, "student_quiz.db") # Ensure database is created in the same directory
DB_FILE = f"sqlite:///{db_path}"
# β
Create database connection using SQLAlchemy (Disabled echo to reduce logs)
engine = create_engine(DB_FILE, echo=False)
conn = engine.connect()
# β
Drop Tables (To Reset Database)
conn.execute(text("DROP TABLE IF EXISTS student"))
conn.execute(text("DROP TABLE IF EXISTS questions"))
# β
Create 'student' Table
conn.execute(text("""
CREATE TABLE student (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
class_name TEXT NOT NULL, -- Changed from 'class' to 'class_name' to avoid SQL keyword conflict
mark INTEGER NULL
)
"""))
# β
Create 'questions' Table
conn.execute(text("""
CREATE TABLE questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
option1 TEXT NOT NULL,
option2 TEXT NOT NULL,
option3 TEXT NOT NULL,
option4 TEXT NOT NULL,
correct_option INTEGER NOT NULL CHECK (correct_option BETWEEN 1 AND 4)
)
"""))
# β
Insert Sample Students using named parameters
students = [
{"id": 1, "name": "Alice", "class_name": "Grade 10", "mark": None},
{"id": 2, "name": "Bob", "class_name": "Grade 11", "mark": None},
{"id": 3, "name": "Charlie", "class_name": "Grade 12", "mark": None}
]
conn.execute(text("INSERT INTO student (id, name, class_name, mark) VALUES (:id, :name, :class_name, :mark)"), students)
# β
Insert Sample Questions using named parameters
questions = [
{"question": "What is 2 + 2?", "option1": "3", "option2": "4", "option3": "5", "option4": "6", "correct_option": 2},
{"question": "What is the capital of France?", "option1": "Berlin", "option2": "Madrid", "option3": "Paris", "option4": "Rome", "correct_option": 3},
{"question": "Which is the largest planet?", "option1": "Mars", "option2": "Jupiter", "option3": "Earth", "option4": "Saturn", "correct_option": 2},
{"question": "Who wrote 'Hamlet'?", "option1": "Shakespeare", "option2": "Hemingway", "option3": "Dante", "option4": "Tolstoy", "correct_option": 1},
{"question": "What is H2O?", "option1": "Oxygen", "option2": "Hydrogen", "option3": "Water", "option4": "Salt", "correct_option": 3}
]
conn.execute(text("""
INSERT INTO questions (question, option1, option2, option3, option4, correct_option)
VALUES (:question, :option1, :option2, :option3, :option4, :correct_option)
"""), questions)
# β
Commit and Close Connection
conn.commit()
result = conn.execute(text("SELECT * FROM student"))
for row in result:
print(row)
conn.close()
print(f"β
Database setup completed successfully at: {db_path}")
Next: Quiz using Json data
16-02-2025 | |
All Works first time, great Program, clear instructions on how to use, Explained very well in the Vidio |