You need something, which calls for example on
KeyPress
a callback, which does the logic.
You can record for each
KeyPress
a timestamp/timeout in a list. Another repeated function could delete the data which is for example older than 5 seconds. This is your observation window. To get the KeyPress per minute, count the elements after old values have been deleted from the list and then divide it by observation window and multiply by 60.
In addition, you can also count words, but this requires additional work to get it correctly.
Example with tkinter:
import time
from threading import Lock
from tkinter import END, Button, Label, StringVar, Text, Tk, INSERT
class KPM:
def __init__(self, time_window):
self.time_window = time_window
self.lock = Lock()
self.window = Tk()
self.kpm = StringVar()
self.wpm = StringVar()
Label(self.window, textvariable=self.kpm, font=36).pack()
Label(self.window, textvariable=self.wpm, font=36).pack()
self.text_widget = Text(self.window, font=24)
self.text_widget.pack()
Button(self.window, text="Clear", command=self._clear).pack()
Button(self.window, text="Exit", command=self.window.destroy).pack()
self.window.bind_all("<KeyPress>", self.on_press)
self.strokes = []
self.words = []
self._last_char = None
self._show()
def _clear(self):
self.text_widget.delete("1.0", END)
self.strokes.clear()
self.words.clear()
def _show(self):
with self.lock:
now = time.monotonic()
self.strokes = [timeout for timeout in self.strokes if now < timeout]
self.words = [timeout for timeout in self.words if now < timeout]
kpm = len(self.strokes) / self.time_window * 60
wpm = len(self.words) / self.time_window * 60
self.kpm.set(f"{kpm:.0f} key presses per minute")
self.wpm.set(f"{wpm:.0f} words per minute")
self.window.after(200, self._show)
def on_press(self, event):
with self.lock:
timeout = time.monotonic() + self.time_window
self.strokes.append(timeout)
# cursor_position = self.text_widget.index(INSERT)
# too complicated to look backwwards ...
if event.char == " " and self._last_char != " ":
self.words.append(timeout)
self._last_char = event.char
def start(self):
self.window.mainloop()
if __name__ == "__main__":
KPM(time_window=5).start()
Other libraries which can read keyboard events: