Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Typing-Test
Submodule Typing-Test added at 8463ec
157 changes: 134 additions & 23 deletions typing_speed_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import customtkinter as ctk
import random
import time
import winsound
import json
import os
from datetime import datetime, timedelta

ctk.set_appearance_mode("light")
ctk.set_default_color_theme("blue")

TEST_DURATION = 60
DATA_FILE = "streak_data.json"

SENTENCES = [
"Technology is the most effective way to change the world.",
Expand All @@ -16,13 +21,37 @@
]


# ======================
# LOAD / SAVE DATA
# ======================

def load_data():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, "r") as f:
return json.load(f)

return {
"daily_streak": 0,
"last_practice_date": "",
"improvement_streak": 0,
"personal_best_streak": 0,
"best_wpm": 0,
"last_wpm": 0
}


def save_data(data):
with open(DATA_FILE, "w") as f:
json.dump(data, f)


class TypingSpeedTest(ctk.CTk):

def __init__(self):
super().__init__()

self.title("Typing Speed Test")
self.geometry("700x520")
self.geometry("720x580")

self.current_sentence = ""
self.start_time = None
Expand All @@ -31,13 +60,15 @@ def __init__(self):
self.paused = False
self.countdown = 3

self.data = load_data()

# TITLE
self.title_label = ctk.CTkLabel(
self,
text="Typing Speed Test",
font=("Helvetica", 30, "bold")
)
self.title_label.pack(pady=20)
self.title_label.pack(pady=15)

# TIMER
self.timer_label = ctk.CTkLabel(
Expand All @@ -47,12 +78,11 @@ def __init__(self):
)
self.timer_label.pack()

# SENTENCE FRAME (for better visibility)
# SENTENCE FRAME
self.sentence_frame = ctk.CTkFrame(self, width=620, height=100)
self.sentence_frame.pack(pady=20)
self.sentence_frame.pack_propagate(False)

# SENTENCE LABEL
self.sentence_label = ctk.CTkLabel(
self.sentence_frame,
text="Press Start to begin",
Expand All @@ -72,27 +102,35 @@ def __init__(self):
self.input_textbox.pack(pady=10)
self.input_textbox.configure(state="disabled")

# RESULT
self.input_textbox.bind("<KeyRelease>", self.handle_typing)

# RESULT LABEL
self.result_label = ctk.CTkLabel(
self,
text="",
font=("Helvetica", 20, "bold")
font=("Helvetica", 18, "bold")
)
self.result_label.pack(pady=10)

# STREAK LABEL
self.streak_label = ctk.CTkLabel(
self,
text=self.get_streak_text(),
font=("Helvetica", 16)
)
self.streak_label.pack(pady=5)

# BUTTON FRAME
self.button_frame = ctk.CTkFrame(self)
self.button_frame.pack(pady=10)

# START BUTTON
self.start_button = ctk.CTkButton(
self.button_frame,
text="Start Test",
command=self.start_test
)
self.start_button.grid(row=0, column=0, padx=10)

# PAUSE BUTTON
self.pause_button = ctk.CTkButton(
self.button_frame,
text="Pause",
Expand All @@ -101,31 +139,35 @@ def __init__(self):
)
self.pause_button.grid(row=0, column=1, padx=10)

# RESULT BUTTON
self.result_button = ctk.CTkButton(
self.button_frame,
text="Check Result",
command=self.check_result,
state="disabled"
# ======================
# STREAK TEXT
# ======================

def get_streak_text(self):
return (
f"πŸ”₯ Daily Streak: {self.data['daily_streak']} days\n"
f"πŸ“ˆ Improvement Streak: {self.data['improvement_streak']}\n"
f"πŸ† Personal Best Streak: {self.data['personal_best_streak']}\n"
f"⭐ Best WPM: {self.data['best_wpm']}"
)
self.result_button.grid(row=0, column=2, padx=10)

# ======================
# START TEST
# ======================

def start_test(self):

self.result_label.configure(text="")
self.countdown = 3
self.input_textbox.configure(state="disabled")

self.start_button.configure(state="disabled")

self.show_countdown()

# ======================
# COUNTDOWN
# ======================

def show_countdown(self):

if self.countdown > 0:
Expand All @@ -141,12 +183,12 @@ def show_countdown(self):
else:

self.sentence_label.configure(text="GO!")

self.after(800, self.begin_test)

# ======================
# BEGIN TEST
# ======================

def begin_test(self):

self.current_sentence = random.choice(SENTENCES)
Expand All @@ -163,13 +205,13 @@ def begin_test(self):
self.paused = False

self.pause_button.configure(state="normal")
self.result_button.configure(state="normal")

self.update_timer()

# ======================
# TIMER
# ======================

def update_timer(self):

if not self.timer_running:
Expand All @@ -184,42 +226,105 @@ def update_timer(self):
)

self.time_left -= 1

self.after(1000, self.update_timer)

else:

self.check_result()

# ======================
# PAUSE / RESUME
# PAUSE
# ======================

def toggle_pause(self):

if not self.timer_running:
return

if not self.paused:

self.paused = True
self.pause_button.configure(text="Resume")

else:

self.paused = False
self.pause_button.configure(text="Pause")
self.update_timer()

# ======================
# HANDLE TYPING + SOUND
# ======================

def handle_typing(self, event):

if not self.timer_running:
return

typed = self.input_textbox.get("1.0", "end-1c")
index = len(typed)

if event.keysym == "BackSpace":
winsound.Beep(500, 40)
return

if index <= len(self.current_sentence) and index > 0:

expected = self.current_sentence[index-1]

if typed[-1] == expected:
winsound.Beep(800, 30)
else:
winsound.Beep(300, 80)

# Finish early if sentence completed
if typed.strip() == self.current_sentence.strip():
self.check_result()

# ======================
# UPDATE STREAKS
# ======================

def update_streaks(self, wpm):

today = datetime.now().date()
last_date = self.data["last_practice_date"]

if last_date:
last_date = datetime.strptime(last_date, "%Y-%m-%d").date()

if today == last_date + timedelta(days=1):
self.data["daily_streak"] += 1
elif today != last_date:
self.data["daily_streak"] = 1
else:
self.data["daily_streak"] = 1

self.data["last_practice_date"] = str(today)

if wpm > self.data["last_wpm"]:
self.data["improvement_streak"] += 1
else:
self.data["improvement_streak"] = 0

self.data["last_wpm"] = wpm

if wpm > self.data["best_wpm"]:
self.data["best_wpm"] = wpm
self.data["personal_best_streak"] += 1

save_data(self.data)

# ======================
# RESULT
# ======================

def check_result(self):

if not self.start_time:
return

self.timer_running = False

winsound.Beep(1200, 300)

typed_text = self.input_textbox.get("1.0", "end-1c")

elapsed_time = time.time() - self.start_time
Expand All @@ -231,10 +336,16 @@ def check_result(self):
else:
wpm = (chars / 5) / (elapsed_time / 60)

self.update_streaks(wpm)

self.result_label.configure(
text=f"Typing Speed: {wpm:.2f} WPM"
)

self.streak_label.configure(
text=self.get_streak_text()
)

self.input_textbox.configure(state="disabled")
self.pause_button.configure(state="disabled")
self.start_button.configure(state="normal")
Expand Down
Loading