initial commit
This commit is contained in:
Executable
+211
@@ -0,0 +1,211 @@
|
||||
#!/bin/python3
|
||||
|
||||
import tkinter as tk
|
||||
import os as os
|
||||
import argparse as ap
|
||||
import time as ti
|
||||
import random as ra
|
||||
import matplotlib.pyplot as pl
|
||||
from matplotlib.gridspec import GridSpec
|
||||
|
||||
class App(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# get cmd-line arguments
|
||||
self.args = self.get_args()
|
||||
|
||||
# properties
|
||||
self.resizable(False, False)
|
||||
self.title("Time^2")
|
||||
self.configure(bg="black")
|
||||
self.measure_missclick = False
|
||||
self.correct_number = 1 # the number that needs to be clicked next, aka the correct one
|
||||
|
||||
# width, height of canvas and window
|
||||
self.w = 1000
|
||||
self.h = 1000
|
||||
self.screen_w = self.winfo_screenwidth()
|
||||
self.screen_h = self.winfo_screenheight()
|
||||
self.center_x = self.screen_w//2 - self.w//2
|
||||
self.center_y = self.screen_h//2 - self.h//2
|
||||
self.geometry(f"{self.w}x{self.h}+{self.center_x}+{self.center_y}")
|
||||
|
||||
# start screen (init phase 1)
|
||||
self.btn_start = tk.Button(self, text="Start test", font=("Courier", 50))
|
||||
self.btn_start.config(command=lambda btn=self.btn_start: self.init_phase_2(btn))
|
||||
self.btn_start.pack(pady=450)
|
||||
|
||||
def init_phase_2(self, btn):
|
||||
# buttons
|
||||
self.taken = []
|
||||
self.num_gone = 0
|
||||
self.area = self.draw()
|
||||
btn.destroy()
|
||||
self.get_random()
|
||||
|
||||
# data needed for evaluation
|
||||
self.data = {
|
||||
"time_clicks": [],
|
||||
"time_missclicks": [],
|
||||
"time_test": 0,
|
||||
"num_clicks": 0,
|
||||
"num_missclicks": 0
|
||||
}
|
||||
self.start = ti.time()
|
||||
self.start2 = ti.time()
|
||||
self.start_entire_test = ti.time()
|
||||
self.end = ti.time()
|
||||
self.end_entire_test = ti.time()
|
||||
|
||||
def get_args(self):
|
||||
p = ap.ArgumentParser(prog="Time^2")
|
||||
p.add_argument("-d", "--debug", action="store_true", help="print debug messages")
|
||||
return p.parse_args()
|
||||
|
||||
# debug print
|
||||
def dp(self, payload: str):
|
||||
if self.args.debug:
|
||||
print(f"D: [{payload}]")
|
||||
|
||||
# drawing buttons on the screen
|
||||
def draw(self) -> list[list]:
|
||||
area = []
|
||||
for i in range(5):
|
||||
temp = []
|
||||
for j in range(5):
|
||||
button = tk.Button(self, width=50, height=5, font=("Courier", 40), text="", bg="#ffffff", fg="#000000")
|
||||
button.config(command=lambda button=button, x=j*200, y=i*200, w=self.w//5, h=self.h//5: [self.button_callback(button, x, y, w, h)], activebackground="lightgray", activeforeground="black")
|
||||
button.place(x=j*200, y=i*200, width=self.w//5, height=self.h//5)
|
||||
self.dp(f"Creating a button => x: {j*200}, \ty: {i*200},\tw: {self.w//5},\th: {self.h//5}")
|
||||
temp.append(button)
|
||||
area.append(temp)
|
||||
|
||||
return area
|
||||
|
||||
# function for buttons to work
|
||||
def button_callback(self, instance, x, y, w, h):
|
||||
self.dp(f"Button clicked => {instance['text']}")
|
||||
|
||||
# only if the number is correct
|
||||
if instance["text"] == str(self.correct_number):
|
||||
self.data["num_clicks"] += 1
|
||||
self.end = ti.time()
|
||||
instance.destroy()
|
||||
self.num_gone += 1
|
||||
self.dp(f"Correct number was => {self.correct_number}")
|
||||
self.correct_number += 1
|
||||
|
||||
if self.measure_missclick:
|
||||
self.measure_missclick = False
|
||||
self.data["time_missclicks"].append(self.end - self.start)
|
||||
for i in range(5):
|
||||
for j in range(5):
|
||||
try:
|
||||
if self.area[i][j]["bg"] == "#ff0000":
|
||||
self.area[i][j].config(bg="#ffffff", fg="#000000", activebackground="lightgray", activeforeground="#000000")
|
||||
self.area[i][j].place()
|
||||
except:
|
||||
pass
|
||||
|
||||
self.data["time_clicks"].append(self.end - self.start2)
|
||||
self.start = ti.time()
|
||||
self.start2 = ti.time()
|
||||
else:
|
||||
if instance["bg"] != "#ff0000":
|
||||
self.measure_missclick = True
|
||||
self.start = ti.time()
|
||||
self.data["num_clicks"] += 1
|
||||
|
||||
self.data["num_missclicks"] += 1
|
||||
self.dp(f"Number of missclicks => {self.data['num_missclicks']}")
|
||||
|
||||
instance.config(bg="#ff0000", fg="#ffffff", activebackground="darkred", activeforeground="#ffffff")
|
||||
instance.place(x=x, y=y, width=w, height=h)
|
||||
|
||||
# check for test end
|
||||
if self.num_gone == 25:
|
||||
self.destroy()
|
||||
self.end_entire_test = ti.time()
|
||||
self.data["time_test"] = self.end_entire_test - self.start_entire_test
|
||||
self.show_data()
|
||||
|
||||
self.dp("Data => ")
|
||||
for key in self.data:
|
||||
if type(self.data[key]) == float or type(self.data[key]) == int:
|
||||
self.dp(" "+key+" => "+
|
||||
"\n "+str(self.data[key]))
|
||||
if type(self.data[key]) == list:
|
||||
self.dp(" "+key+" => "+
|
||||
"".join(["\n "+str(i) for i in self.data[key]]))
|
||||
|
||||
# returns a random number that can be used as a text in a new button
|
||||
def get_random(self) -> str:
|
||||
for i in range(5):
|
||||
for j in range(5):
|
||||
r = ra.randint(1, 25)
|
||||
while r in self.taken:
|
||||
r = ra.randint(1, 25)
|
||||
self.taken.append(r)
|
||||
self.area[i][j]["text"] = str(r)
|
||||
|
||||
def show_data(self):
|
||||
if len(self.data["time_clicks"]) != 0:
|
||||
time_click_avg = sum(self.data["time_clicks"])/len(self.data["time_clicks"])
|
||||
else:
|
||||
time_click_avg = 0
|
||||
if self.data["time_clicks"] != []:
|
||||
time_click_longest = max(self.data["time_clicks"])
|
||||
time_click_shortest = min(self.data["time_clicks"])
|
||||
else:
|
||||
time_click_longest = 0
|
||||
time_click_shortest = 0
|
||||
|
||||
if len(self.data["time_missclicks"]) != 0:
|
||||
time_missclick_avg = sum(self.data["time_missclicks"])/len(self.data["time_missclicks"])
|
||||
else:
|
||||
time_missclick_avg = 0
|
||||
if self.data["time_missclicks"] != []:
|
||||
time_missclick_longest = max(self.data["time_missclicks"])
|
||||
time_missclick_shortest = min(self.data["time_missclicks"])
|
||||
else:
|
||||
time_missclick_longest = 0
|
||||
time_missclick_shortest = 0
|
||||
|
||||
pl.rcParams["toolbar"] = "None"
|
||||
|
||||
ax1 = pl.subplot(2, 1, 1)
|
||||
ax2 = pl.subplot(2, 1, 2)
|
||||
|
||||
ax1.set_title("Time between clicks on correct numbers", loc="left")
|
||||
ax1.plot(self.data["time_clicks"], marker="o")
|
||||
|
||||
ax2.set_title("Time between missclicks and corrections", loc="left")
|
||||
ax2.plot(self.data["time_missclicks"], "o")
|
||||
|
||||
pl.subplots_adjust(hspace=1)
|
||||
|
||||
tb = pl.table( cellText=[
|
||||
[round(time_click_avg, 2), round(time_missclick_avg, 2), ""],
|
||||
[round(time_click_longest, 2), round(time_missclick_longest, 2), ""],
|
||||
[round(time_click_shortest, 2), round(time_missclick_shortest, 2), ""],
|
||||
["", self.data["num_missclicks"], ""],
|
||||
["", "", round(self.data["time_test"], 2)]],
|
||||
rowLabels=
|
||||
["Average time", "Longest time", "Shortest time", "Count", "Time taken"],
|
||||
colLabels=
|
||||
["Between clicks", "Between missclicks and corrections", "To complete the test"],
|
||||
colColours=
|
||||
["lightgray", "lightgray", "lightgray"],
|
||||
rowColours=
|
||||
["lightgray", "lightgray", "lightgray", "lightgray", "lightgray"],
|
||||
loc=
|
||||
"top"
|
||||
)
|
||||
tb.set_fontsize(120)
|
||||
|
||||
pl.show()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = App()
|
||||
app.mainloop()
|
||||
Reference in New Issue
Block a user