1.
SYNOPSIS
The Typing Test Application is a comprehensive desktop-based software system
developed using Python (Tkinter) for the graphical interface and MySQL for
database management, designed to evaluate and enhance typing speed and
accuracy in an interactive manner. The application provides multiple modes to suit
different user needs, including a Standard Typing Test that fetches random
passages for real-time assessment, a Practice Mode for stress-free improvement
without saving results, an Alphabet Drill Mode for focused training on uppercase
and lowercase letters, and a Number Typing Mode to strengthen numerical typing
skills. Users can securely register and log in, after which their typing performance
is tracked and stored, recording essential metrics such as Words Per Minute
(WPM), accuracy percentage, error count, and timestamps. The system maintains a
personal history log for each user, allowing them to analyze their progress over
time, and also features a competitive leaderboard that highlights the highest WPM
achieved by each participant, encouraging motivation and consistency. With its
clean, intuitive GUI and robust backend support, the project not only serves as a
practical learning tool for students, professionals, and typing enthusiasts but also
demonstrates the effective integration of Python GUI programming with relational
database systems for scalable and educational software solutions.
1|Page
2. ABSTRACT
In today’s digital era, typing proficiency has become a fundamental skill for
students, professionals, and individuals preparing for competitive examinations,
making the need for an effective typing improvement tool more important than
ever. The Typing Test Application, developed using Python (Tkinter) and MySQL,
addresses this need by offering a robust and user-friendly platform that enables
users to practice, test, and monitor their typing performance in real time. The
application allows secure registration and login, provides multiple typing modes
such as Standard Test, Practice Mode, Alphabet Drill, and Number Typing Mode,
and evaluates users based on key performance metrics including Words Per
Minute (WPM), accuracy percentage, and error count. Each test attempt is
recorded in the database with timestamps, enabling users to maintain a detailed
history log of their progress, while a leaderboard motivates healthy competition by
showcasing top performers. Designed with a clean and intuitive GUI, the software
ensures accessibility for all types of users and demonstrates strong backend
integration for data persistence. By combining usability, engagement, and
scalability, this project not only serves as an effective educational and self-
improvement tool but also stands as a practical demonstration of integrating
Python-based graphical applications with relational database systems.
2|Page
3. OBJECTIVE
The main objectives of the Typing Test Application are:
To design and develop a robust and interactive typing platform that enhances
typing speed and accuracy.
To provide a secure and user-friendly interface that supports user registration,
login, and data storage.
To allow users to track their typing performance over time through detailed
statistics and history logs.
To offer multiple test modes including Standard Test, Practice Mode, Alphabet
Drill Mode, and Number Typing Mode for diverse learning needs.
To deliver instant analytical feedback after each test, helping users identify
strengths and areas for improvement.
To maintain a leaderboard that encourages competition and motivation by
showcasing top WPM scores.
To ensure scalability and reliability of the system by integrating a MySQL
backend for persistent data management.
3|Page
4. FUNCTION
The Typing Test Application consists of the following core functional modules:
User Authentication
o Registration and login system with input validation.
o Secure storage of usernames and passwords in the database.
Typing Test Module
o Fetches random passages from the MySQL database.
o Starts the timer automatically when the user begins typing.
o Calculates Words Per Minute (WPM), accuracy, and error count after
submission.
Practice Mode
o Allows users to practice typing without saving results.
o Useful for warm-up sessions and stress-free improvement.
Alphabet Drill Mode
o Displays all uppercase and lowercase letters in random order.
o Improves typing consistency and efficiency with the Shift key.
Number Typing Mode
o Generates random numeric strings for focused number typing practice.
o Enhances accuracy and speed in digit-based input.
Leaderboard
o Stores and displays the highest WPM achieved by each user.
o Updates in real time when a user breaks their previous best score.
History Tracking
o Records each test attempt with WPM, accuracy, errors, and timestamp.
o Enables users to review their progress over time.
Database Integration
o Uses MySQL to manage users, passages, history, and leaderboard.
o Ensures data persistence and easy retrieval across sessions.
4|Page
5. FEATURES
The key features of this application include:
- Graphical User Interface (GUI): Built using Tkinter for clean navigation and
interaction.
- User Management System: Enables new users to register and existing users to
login.
- Dynamic Paragraphs: Randomly selects different passages for variety in
testing.
Typing Performance Metrics:
- Words Per Minute (WPM)
- Accuracy (in percentage)
- Error count
- Multi-mode Support:
- Normal Test Mode with result tracking.
- Practice Mode for casual typing without saving results.
- Leaderboard: Displays highest WPM scores among all users.
- Attempt History: Maintains logs of every user's typing attempts with
timestamps.
- Data Security: Uses input validation and handles errors gracefully.
- Extensibility: Easily upgradable to include more features like level selection or
online multiplayer.
5|Page
6. SCOPE
Educational Scope:
- Ideal for schools and institutes that teach typing skills.
- Can be used in typing training centers to help students assess their
performance.
Professional Scope:
- Useful for individuals preparing for typing-based recruitment exams (e.g.,
data entry jobs, clerical exams).
- HR departments can use it as a pre-screening tool for candidates.
Technical Scope:
- Demonstrates integration of Python GUI with MySQL.
- Scalable project that can be upgraded into a web or mobile application.
User Scope:
- Suitable for beginners and experienced typists.
- Can be personalized based on difficulty levels and themes.
Future Integration Scope:
- Integration with speech-to-text systems for accessibility.
6|Page
- Cloud database support for cross-device performance tracking.
7. SOFTWARE AND HARDWARE REQUIREMENTS
Software Requirements:
- Python 3.8+ (Recommended)
- Tkinter (comes pre-installed with Python)
- MySQL Server (XAMPP or standalone installation)
- MySQL Connector for Python:
pip install mysql-connector-python
- Text Editor or IDE (e.g., Visual Studio Code, PyCharm, Thonny)
Hardware Requirements:
- Minimum 2 GB RAM
- Intel i3 or equivalent processor
- At least 500 MB of disk space
- Keyboard and Mouse for input
- Monitor with 1024x768 resolution or higher
- Stable power supply and working environment for database server
7|Page
[Link] CODE
import tkinter as tk
from tkinter import messagebox
import [Link]
import random
import time
from functools import partial
# === MYSQL CONNECTION ===
conn = [Link](
host="localhost",
user="root",
password="12345",
database="typing_test"
cursor = [Link]()
start_time = 0.0
# === LOGIN / REGISTER ===
def login_window():
8|Page
for widget in root.winfo_children():
[Link]()
def show_rules(username):
rules_win = [Link]()
rules_win.title("Typing Test - Rules")
rules_win.geometry("1600x900")
rules_text = """
Typing Test Rules:
1. The test starts as soon as you click inside the typing box.
2. Type the text shown exactly as it appears (including capital letters, numbers, and
symbols if present).
3. Avoid extra spaces or missing characters — accuracy matters!
4. Press 'Submit' when you finish typing.
5. Your WPM (words per minute), accuracy, and total errors will be shown at the end.
6. Practice Mode results are not saved.
7. Special Modes:
• Alphabet Drill Mode – Shows all letters (A–Z and a–z) in random order.
Helps improve speed in typing both uppercase and lowercase letters, and
trains your use of the Shift key efficiently.
• Number Typing Mode – Generates random numbers for you to type exactly.
Helps improve speed and accuracy in number typing.
8. Leaderboard stores only the highest WPM for each user.
9|Page
9. Press 'Back' or 'Logout' to return to the menu or login screen.
"""
[Link](rules_win, text="Welcome to Typing Test!",
font=("Arial",20)).pack(pady=10)
[Link](rules_win, text=rules_text, justify="left",
wraplength=1000,font=("Arial", 14),bg='light blue').pack(padx=20,
pady=10)
[Link](rules_win, text="Continue", command=lambda: [rules_win.destroy(),
main_menu(username)],font=("Arial", 14)).pack(pady=20)
def login_user():
username = login_username.get()
password = login_password.get()
[Link]("SELECT * FROM users WHERE username = %s AND
password = %s", (username, password))
result = [Link]()
if result:
[Link]("Login Successful", f"Welcome, {username}")
[Link]()
show_rules(username)
else:
[Link]("Error", "Wrong username or password")
10 | P a g e
def register_user():
username = reg_username.get()
password = reg_password.get()
if username == "" or password == "":
[Link]("Error", "All fields are required")
return
try:
[Link]("INSERT INTO users (username, password) VALUES (%s,
%s)", (username, password))
[Link]()
[Link]("Success", "Registration successful. Please login.")
register_window.destroy()
except [Link]:
[Link]("Error", "Username already exists")
def open_register():
global register_window, reg_username, reg_password
register_window = [Link]()
register_window.title("Register")
register_window.geometry("1600x900")
[Link](register_window, text="Username",font=('Arial',15)).pack(pady=5)
reg_username = [Link](register_window,font=('Arial',15))
11 | P a g e
reg_username.pack(pady=5)
[Link](register_window, text="Password",font=('Arial',15)).pack(pady=5)
reg_password = [Link](register_window, show="*",font=('Arial',15))
reg_password.pack(pady=5)
[Link](register_window, text="Register",
command=register_user,font=('Arial',15)).pack(pady=5)
[Link](root, text="Username",font=('Arial',15)).pack(pady=5)
login_username = [Link](root,font=('Arial',15))
login_username.pack(pady=5)
[Link](root, text="Password",font=('Arial',15)).pack(pady=5)
login_password = [Link](root, show="*",font=('Arial',15))
login_password.pack(pady=5)
[Link](root, text="Login", command=login_user,font=('Arial',15)).pack(pady=5)
[Link](root, text="Register",
command=open_register,font=('Arial',15)).pack(pady=5)
# === MAIN MENU ===
def main_menu(username):
menu = [Link]()
[Link]("Typing Test - Main Menu")
[Link]("1600x900")
[Link](menu, text=f"Welcome, {username}",
12 | P a g e
font=("Arial", 14, "bold")).pack(pady=20)
[Link](menu, text="Take Test", width=20,
command=partial(take_test,username),font=('Arial',15)).pack(pady=5)
[Link](menu, text="Practice Mode", width=20,
command=partial(practice_mode, username),font=('Arial',15)).pack(pady=5)
[Link](menu, text="Leaderboard", width=20,
command=show_leaderboard,font=('Arial',15)).pack(pady=5)
[Link](menu, text="History", width=20,
command=partial(show_history,username),font=('Arial',15)).pack(pady=5)
[Link](menu, text="Alphabet Drill Mode", width=20,
command=partial(alphabet_drill_mode, username),
font=('Arial',15)).pack(pady=5)
[Link](menu, text="Number Typing Mode", width=20,
command=partial(number_typing_mode, username),
font=('Arial',15)).pack(pady=5)
[Link](menu, text="Logout", width=20,
command=lambda:[[Link](),[Link](),login_window()],
font=('Arial',15)).pack(pady=20)
# === FULL FUNCTION: TAKE TEST ===
def take_test(username):
global start_time
13 | P a g e
def start_timer(event):
global start_time
start_time = [Link]()
def fetch():
[Link]("SELECT content FROM passages")
return [Link]([Link]())[0]
def calculate():
typed = input_box.get("1.0", [Link]).strip()
t = max([Link]() - start_time, 1)
wpm = (len(typed) / 5) * 60 / t
acc = sum(1 for a, b in zip(text, typed) if a == b) / len(text) * 100
err = sum(1 for a, b in zip(text, typed) if a != b) + abs(len(text) - len(typed))
show(wpm, acc, err)
save(username, wpm, acc, err)
def save(username, wpm, acc, err):
[Link]("SELECT COUNT(*) FROM history WHERE username=%s",
(username,))
attempt = [Link]()[0] + 1
[Link]("INSERT INTO history (username, attempt_no, wpm, accuracy,
14 | P a g e
total_errors) VALUES (%s,%s,%s,%s,%s)",
(username, attempt, wpm, acc, err))
[Link]("SELECT highest_wpm FROM leaderboard WHERE
username=%s", (username,))
r = [Link]()
if r:
if wpm > r[0]:
[Link]("UPDATE leaderboard SET highest_wpm=%s WHERE
username=%s", (wpm, username))
else:
[Link]("INSERT INTO leaderboard (username, highest_wpm)
VALUES (%s, %s)", (username, wpm))
[Link]()
def start(e): start[0] = [Link]()
def show(wpm, acc, err):
for w in frame.winfo_children(): [Link]()
[Link](frame, text=f"WPM: {wpm:.2f}\nAccuracy: {acc:.2f}%\nErrors: {err}",
font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Try Again", command=lambda: [[Link](),
take_test(username)],font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Back",
15 | P a g e
command=[Link],font=("Arial",14)).pack(pady=10)
frame = [Link]()
[Link]("1600x900")
text = fetch()
[Link](frame, text=text, wraplength=600, bg="lightyellow",
font=("Arial", 14)).pack(pady=10)
input_box = [Link](frame, height=20, width=80)
input_box.pack()
input_box.bind("<FocusIn>",start_timer)
[Link](frame, text="Submit", command=calculate,
font=("Arial", 14)).pack(pady=5)
# === FULL FUNCTION: PRACTICE MODE ===
def practice_mode(username):
global start_time
def fetch():
[Link]("SELECT content FROM passages")
return [Link]([Link]())[0]
def calculate():
16 | P a g e
typed = input_box.get("1.0", [Link]).strip()
t = max([Link]() - start_time, 1)
wpm = (len(typed) / 5) * 60 / t
acc = sum(1 for a, b in zip(text, typed) if a == b) / len(text) * 100
err = sum(1 for a, b in zip(text, typed) if a != b) + abs(len(text) - len(typed))
show(wpm, acc, err)
def show(wpm, acc, err):
for w in frame.winfo_children(): [Link]()
[Link](frame, text=f"WPM: {wpm:.2f}\nAccuracy: {acc:.2f}%\nErrors: {err}",
font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Try Again", command=lambda: [[Link](),
practice_mode(username)],font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Back", command=[Link],
font=("Arial", 14)).pack(pady=10)
def start_timer(event):
global start_time
start_time = [Link]()
frame = [Link]()
[Link]("1600x900")
text = fetch()
17 | P a g e
input_box = [Link](frame, height=20, width=80)
[Link](frame, text=text, wraplength=600,
bg="lightcyan",font=("Arial",14)).pack(pady=10)
input_box.pack()
input_box.bind("<FocusIn>",start_timer)
[Link](frame, text="Submit", command=calculate,
font=("Arial", 14)).pack(pady=5)
# === FULL FUNCTION: LEADERBOARD ===
def show_leaderboard():
win = [Link]()
[Link]("Leaderboard")
[Link]("1600x900")
[Link](win, text="\U0001F3C6 Leaderboard",
font=("Arial", 14, "bold")).pack(pady=10)
[Link]("SELECT username, highest_wpm FROM leaderboard ORDER BY
highest_wpm DESC")
rows = [Link]()
for idx, row in enumerate(rows, start=1):
[Link](win, text=f"{idx}. {row[0]} - {row[1]:.2f} WPM",
font=("Arial", 13),fg='blue').pack()
[Link](win, text="Back", command=[Link],
18 | P a g e
font=("Arial", 14)).pack(pady=10)
# === FULL FUNCTION: HISTORY ===
def show_history(username):
win = [Link]()
[Link]("History")
[Link]("1600x900")
[Link](win, text=f"{username}'s History",
font=("Arial", 14, "bold")).pack(pady=10)
[Link]("SELECT attempt_no, wpm, accuracy, total_errors, attempt_time
FROM history WHERE username = %s ORDER BY attempt_no",
(username,))
for row in [Link]():
attempt, wpm, acc, err, dt = row
[Link](win, text=f"Attempt {attempt} | WPM: {wpm:.2f} | Accuracy:
{acc:.2f}% | Errors: {err} | Date&Time: {dt}",
font=("Arial", 13),fg='red').pack()
[Link](win, text="Back", command=[Link],
font=("Arial", 14)).pack(pady=10)
# === FULL FUNCTION: ALPHABET DRILL ===
def alphabet_drill_mode(username):
import random
19 | P a g e
global start_time
# Generate shuffled alphabet without spaces
alphabet=list('AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVv
WwXxYyZz')
[Link](alphabet)
drill_text = "".join(alphabet) # no spaces
def start_timer(event):
global start_time
start_time = [Link]()
def calculate():
typed = input_box.get("1.0", [Link]).strip()
t = max([Link]() - start_time, 1)
wpm = (len(typed) / 5) * 60 / t
acc = sum(1 for a, b in zip(drill_text, typed) if a == b) / len(drill_text) * 100
err = sum(1 for a, b in zip(drill_text, typed) if a != b) + abs(len(drill_text) -
len(typed))
show(wpm, acc, err)
def show(wpm, acc, err):
for w in frame.winfo_children():
20 | P a g e
[Link]()
[Link](frame, text=f"WPM: {wpm:.2f}\nAccuracy: {acc:.2f}%\nErrors: {err}",
font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Back", command=[Link],
font=("Arial", 14)).pack(pady=10)
frame = [Link]()
[Link]("1600x900")
[Link](frame, text=drill_text, wraplength=600, bg="lightgreen",
font=("Arial", 20, "bold")).pack(pady=10) # bigger font for clarity
input_box = [Link](frame, height=3, width=30, font=("Arial", 16))
input_box.pack()
input_box.bind("<FocusIn>", start_timer)
[Link](frame, text="Submit", command=calculate,
font=("Arial", 14)).pack(pady=5)
# === FULL FUNCTION: NUMBER PRCTICE ===
def number_typing_mode(username):
import random
global start_time
21 | P a g e
# Generate a string of random digits (20 digits here)
numbers = "".join(str([Link](0, 9)) for _ in range(20))
def start_timer(event):
global start_time
start_time = [Link]()
def calculate():
typed = input_box.get("1.0", [Link]).strip()
t = max([Link]() - start_time, 1)
wpm = (len(typed) / 5) * 60 / t
acc = sum(1 for a, b in zip(numbers, typed) if a == b) / len(numbers) * 100
err = sum(1 for a, b in zip(numbers, typed) if a != b) + abs(len(numbers) -
len(typed))
show(wpm, acc, err)
def show(wpm, acc, err):
for w in frame.winfo_children():
[Link]()
[Link](frame, text=f"WPM: {wpm:.2f}\nAccuracy: {acc:.2f}%\nErrors: {err}",
font=("Arial", 14)).pack(pady=10)
[Link](frame, text="Back", command=[Link],
font=("Arial", 14)).pack(pady=10)
22 | P a g e
frame = [Link]()
[Link]("1600x900")
[Link](frame, text=numbers, wraplength=600, bg="lightblue",
font=("Arial", 20, "bold")).pack(pady=10)
input_box = [Link](frame, height=3, width=30, font=("Arial", 16))
input_box.pack()
input_box.bind("<FocusIn>", start_timer)
[Link](frame, text="Submit", command=calculate,
font=("Arial", 14)).pack(pady=5)
# === START PROGRAM ===
root = [Link]()
[Link]("Typing Test Login")
[Link]("1600x900")
login_window()
[Link]()
23 | P a g e
9. MYSQL TABLES
History
Leaderboard
Passages
Content in passages table:
24 | P a g e
25 | P a g e
26 | P a g e
27 | P a g e
28 | P a g e
29 | P a g e
User
30 | P a g e
10. OUTPUT
Login window
Register window
31 | P a g e
Rules window
Main menu window
Take Test
32 | P a g e
Practice Mode
Leaderboard
33 | P a g e
History
34 | P a g e
Alphabet drill mode
Number typing mode
35 | P a g e
10. FUTURE SCOPE
- Multiplayer Online Mode: Compete with users over a network or the
internet.
- Mobile App Integration: Build Android/iOS versions using Kivy or Flutter.
- Difficulty Levels: Easy, Medium, Hard passages for progressive learning.
- Analytics Dashboard: Charts and graphs to show typing trends over time.
- Certificate Generation: Issue certificates for milestones achieved.
- Admin Panel: For managing users, passages, and results.
- Dark Mode / Custom Themes: Enhance user experience with UI
personalization.
- Gamification: Add achievements, rewards, and badges to boost
engagement.
36 | P a g e
11. BIBLIOGRAPHY
1)Preeti Arora – Computer Science with Python (Class XII), Sultan Chand
Publications
2)Sumita Arora – Computer Science with Python (Class XII), Dhanpat Rai
Publications
3)[Link] – Tkinter Course – Create Graphic User Interfaces in Python
Tutorial
[Link]
4)John Elder (Codemy) – Python GUI with Tkinter - Full Course
[Link]
5)GeeksforGeeks – Python GUI Programming with Tkinter
[Link]
6)Chatgpt
37 | P a g e
38 | P a g e