Search code examples
pythontimekeyboard-eventskeystrokepynput

How to make a python program measuring nanoseconds between keystroke dynamics?


I'm trying make a python program initiating a nanosecond count when a key on the keyboard is pressed, stopping it when the key is released and calculating how long the key was pressed down for.

So far I have tried several different structures using the modules pynput and time. I release the code below is wrong, but I'm adding it anyway, so somebody could potentially pinpoint me in the right direction.

When I run the code below it does print a time in nanoseconds. However, the time printed keeps increasing for every key I try, no matter for how long I press the key down. It seems the method: time.perf_counter_ns() does not stop counting even when the function block is finished and called again after that.

import pynput, time
from pynput.keyboard import Key,Listener

timer = 0
timer2 = 0

def press(Key):
    timer = time.perf_counter_ns()

def release(Key):
    timer2 = time.perf_counter_ns()
    print(timer-timer2)

with Listener(on_press=press, on_release=release) as listener:
    listener.join()

Solution

  • Source of the bug

    It looks like your problem is that the line timer = time.perf_counter_ns() in press is not doing what you expect.

    Variables assigned to within Python functions hide variables with the same name from the parent scope(s). This means that you're assigning to a local variable called timer, rather than the global variable timer as you expect. When you calculate timer-timer2 in release, you're using the global variable timer, the value of which never changes from 0.

    Solution

    Wrap the timers in a class that contains timer as a member.

    class Timer:
        def __init__(self):
            self.timer = None
    
        def press(self, key):
            self.timer = time.perf_counter_ns()
    
        def release(self, key):
            timer2 = time.perf_counter_ns()
            print(timer2 - self.timer)
    
    timer = Timer()
    with Listener(on_press=timer.press, on_release=timer.release) as listener:
        listener.join()
    

    Another way is to just use the global version of timer in your code. This is considered bad practice, but here is how you'd do it.

    def press(Key):
        global timer
        timer = time.perf_counter_ns()