1

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()
1
  • A simple debugging step that would have helped: print(timer) in release() and you'll see it's always 0. Also your arithmetic results in a negative number: you should reverse the operands. Commented Jun 17, 2020 at 13:21

1 Answer 1

2

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()
Sign up to request clarification or add additional context in comments.

2 Comments

@joecode In addition to marking it as accepted, you can up-vote it as well. Votes are unlimited and free to give away, use them liberally :)
Yeah I actually did that, but as I'm new to stackoverflow, a message appeared saying votes cast by those with less than 15 reputation are not publicly displayed.. 2 more to go and you'll see it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.