
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
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.
16-02-2025 | |
| All Works first time, great Program, clear instructions on how to use, Explained very well in the Vidio | |