project / August 6th, 2021
This is a desktop app that assesses the typing speed of a user in characters per minute (CPM) and words per minute (WPM). When the user clicks the text input bar, a 60-second timer is activated and a random word is displayed above the bar. The user then attempts to type the random word in the text input bar and once it is entered, the number of correct characters typed is counted and added to the total amount. A new word is displayed and the process is repeated. Throughout the test, the CPM and WPM is calculated and displayed every second. After the test is finished, the final CPM and WPM scores are calculated, saved, and displayed at the bottom of the screen. The user can click the text input bar to start another test. At any time, a restart button can be used to reset the scores and timer.
This is a professional portfolio project that was created after completing the 100 Days of Code Python Bootcamp.
After completing the 100 Days of Code Python Bootcamp, this project was offered as a suggested project idea to apply my knowledge of the Python Tkinter Iibrary from the course. Just like other professional portfolio projects, developing skills in planning and executing a project from scratch without guidance was the most valuable lesson. Additionally, the challenges of creating a functional timer and calculating the CPM and WPM tested many programming skills.
Python
Tkinter
For this project, I wanted to take aspects of this example typing speed test and combine them with my own ideas to make a desktop GUI version. Instead of waiting for the timer expiration to display the CPM and WPM, I wanted these scores to be shown in real-time to motivate the user as they take the test. I also wanted to display just one word at a time, because showing multiple words can distract the user.
The first step was to plan out the various functionalities this program requires. Eventually, I devised a solution that would use eleven functions:
A function called start_test that will generate a unique list of words to be displayed and call the start_timer and new_word functions.
A function called start_timer that starts the 60-second timer, displays the time, calculates the score by calling calculate_score, and monitors the timer to see if there is still time remaining or not. If there is time still remaining, the function will call itself again and pass in the time subtracted by one second. If the time expires, the timer is stopped and the final scores are calculated with calculate_score, saved with update_recent_score, and displayed with read_recent_score.
A function called new_words that takes a random word from the unique list of words and displays it on-screen for the user.
A function called click_input_bar that will start the typing test by calling start_test after the user clicks the text input bar. The total amount of correct characters typed by the user is reset to 0.
A function called leave_input_bar that inserts the message "type words here" in the text input bar when the user hovers or clicks their mouse outside the bar.
A function called enter_word that will compare the user-inputted word to the current random word and checks how many characters the user typed correctly. The amount of characters correctly typed is added to the current total amount and a new word is loaded by calling new_word.
A function called calculate_score that uses the current total amount of correctly typed characters and time elapsed to calculate the current CPM and WPM. The current CPM and WPM is then updated on-screen and a list containing these values is returned.
A function called update_recent_score that takes the final CPM and WPM from calculate_score and writes them into a CSV file that only contains the newest score. The current date and time are also written into the CSV file and the newest scores, date, and time is displayed at the bottom of the GUI.
A function called read_recent_score that reads the CPM, WPM, date, and time of the most recent completed test from the CSV file and returns a string containing these pieces of info.
A function called restart that stops the timer, ends the test, and resets the displayed CPM, WPM, timer, and labels back to their initial values during start-up.
Lastly, a function called close_window that closes the window and ends the program.
After planning out the functionalities of the program, Tkinter is used to build a GUI layout. To start, I added a canvas widget that contains the logo I selected for this app. Next, I added various labels and text input boxes to display the instructions and current time, CPM, and WPM directly below the logo. The restart button is also placed in this area. Next, a large box to display the current word is placed underneath the previous area. Below this box is the text input for typing words, which is bound to the click_input_bar, leave_input_bar, and enter_word functions using the bind method. The bottom of the screen contains text displaying the CPM, WPM, date, and time from the most recent completed test. Finally, an exit button to close the window is added to the bottom-right corner.
The next step was to implement each of the functions described above. Clicking the text input box will trigger start_test. To generate a random list of words in start_test, the lorem_text module is used and each word is converted to all lower case letters. List comprehension is then used to remove the duplicate words and form a new list of unique words. The new_words function is called with this new list passed in and start_timer is also called. new_words uses the choice method from the random module to select and remove a random word from the unique list of words and display it for the user.
start_timer will use the remaining time as an argument, starting with the starting 60 seconds during the first call. After updating the timer input box to show the current time remaining, this function will call calculate_score to calculate and display the current CPM and WPM. start_timer will then call itself again with a time of one less second and repeat the same process until there is no time remaining. After the timer expires, the final score is calculated, saved, and displayed for the user.
When the user presses the enter/return key or space bar, their typed word is submitted and handled using the enter_word function. The strip function is used on the entered word to remove trailing or leading spaces. The entered word is then compared to the current random word to see how many characters were typed correctly by the user. In order for each letter to be considered correctly typed, the letter and letter positioning (or index) in the typed word must match that of the random word. Enumeration is used to ensure that the index and content of each letter in both words are the same. After the total amount of correctly typed characters is counted, it is added to the overall total for the test and new_words is called to display another word. For each typed word, 1 is always added to the total amount of correctly typed letters since pressing enter or space bar counts as a keystroke.
calculate_score will calculate and display the current CPM for every second of the test by dividing the total amount of correct characters by the number of seconds elapsed and multiplying the result by 60. The WPM is calculated by dividing the CPM by 5, since the average word contains 5 keystrokes. Exception handling is added for the ZeroDivisionError that occurs when zero seconds have elapsed, and the CPM and WPM will be simply shown as 0. After the CPM and WPM is displayed, these values are returned in a list which will be accessed in the function update_recent_score.
update_recent_score is called when the timer expires and saves the CPM, WPM, date, and time of the most recent completed test into a CSV file. First, the datetime module is used to acquire the date and time as soon as the timer expires. The strftime method is used to format the date and time strings, and this information is written with the final CPM and WPM into a CSV file. Because I only want information from the newest test to be displayed, this function will always overwrite the previous information in the CSV. Finally, read_recent_score is called to read this information from the CSV file and display it at the bottom of the screen upon start-up and after each completed test. If there is no CSV file and a FileNotFoundError occurs, the message "No recent score listed" will be shown.
The smaller functions such as click_input_bar, leave_input_bar, restart, and close_window are quickly added using basic Tkinter methods from previous projects.
Finishing touches are made by adding padding around each button and label to make the GUI less cluttered, adding colour, and extra lines of code are added to clear, enable, or disable certain text boxes during certain function calls.
Building the Typing Speed Test was another valuable experience in creating a desktop GUI app that applied my existing knowledge of Tkinter. Figuring out how to create a functioning timer and calculate and display the CPM and WPM in real-time were challenges that required research on Google and looking at my previous notes. Overall, I am very happy about this project and the Watermarker as I feel far more comfortable using Tkinter to build GUIs.
This app can be further improved by keeping track of high scores and displaying them as another way to motivate the user. It may also be a good idea to keep track of the number of correctly or incorrectly spelled words to provide another layer of information for the user. Providing the user an option to display regular words instead of Lorem ipsum text may help in making the test easier.