Failure to detect pressed key

I’m currently programming a task in which participants move asterisks on screen by holding down certain buttons. However, I’ve run into some issues with checking whether a key remains pressed every frame using the method described here: How to detect whether if a key is being pressed down?

I’ve pared it down to the simplest level in Builder - a routine that contains only a code component and a keyboard component that has no allowed keys and waits forever for a response. The code component contains the following in “begin routine”:

kbLeft = keyboard.Keyboard(0)

…and the following in “each frame”:

currentKeys = kbLeft.getKeys('f', waitRelease=False, clear=False)
if not len(currentKeys)==0:
    if currentKeys[0] == 'f':
        print ("holding f")

As I understand it, setting waitRelease and clear to false should mean that getKeys returns any key that is pressed down, every frame, for as long as the key is pressed down. This should mean that the code prints “holding f” once per frame while I’m holding the key down. But this doesn’t happen - the message is displayed only once, on press, not repeated unless I release and press again.

I tried reinstalling Psychopy, but no dice. Am I making some dumb mistake that I’m not seeing?

Appreciate any help!

edit: unclear error description

Dear @AKrishna

Please try this:

# begin routine
kb = keyboard.Keyboard()
# each frame
keys = kb.getKeys(['f', 's'], waitRelease = False, clear=False)
for thisKey in keys:
    if thisKey == 'f' and thisKey.duration == None:
        print('holding F')
    elif thisKey =='s' and thisKey.duration == None:
        print('holding S')

One problem with this implementation is that sometimes the keyboard class may act like you’re still holding down that key, maybe it was fixed in the future versions but if you ever see such behaviour, you may want to add an accumulator under each key press code and reset the keyboard events after it reaches a value with:

kb.clearEvents()

Thanks for your response!
I copy-pasted your code and dummied out all of mine. Unfortunately, each key still prints exactly once (when pressed) instead of printing each time the frame refreshes. If the code is working for you, this might perhaps be a hardware issue on my end?

The code works fine for me on PsychoPy v2020.2.10

Which version are you using?

Which OS are you using?

PsychoPy v.2021.2.3 on Windows 10.

Well my code is a year old, the correct implementation may have changed. This is why I don’t update Psychopy when I have ongoing experiments.

Right now I do not have time to update and check for myself to help you out, so if you are well-versed with Python, you can go ahead and study the source code, and play around to find the proper implementation: psychopy/keyboard.py at release · psychopy/psychopy · GitHub

Keyboard API information: Keyboard — PsychoPy v2021.3

I’ll try that. I’ve already looked around in the source code where I could, but didn’t find much - but I’m not used to working in python. Thanks for your help, though!

Still glad for any help here, if anyone has any ideas!

Without ever clearing the keyboard events, once a key is pressed it will never be removed from the list. However using kb.clearEvents() will result in all events being removed, not just the released ones, so that is not really ideal in this case.

This custom code should track press / release events and only clear the event on release (tested using Windows standalone PsychoPy 2021.2.3):

# On each frame

# Get, but don't clear, pressed keys
keys = kb.getKeys(['f', 's'], waitRelease=False, clear=False)
# Get and clear key when released
keys_released = kb.getKeys(['f', 's'], waitRelease=True, clear=True)
    
if keys:
    print("pressed: ", [(k.rt, k.name) for k in keys])
    
if keys_released:
    print("released: ", [(k.rt, k.name, k.duration) for k in keys_released])

Here is example output from:

  1. press and hold ‘s’
  2. Press and hold ‘f’
  3. Release ‘s’
  4. Release ‘f’:
pressed:  [(5.245981400134042, 's')]
pressed:  [(5.245981400134042, 's')]
[....repeated every frame.....]
pressed:  [(5.245981400134042, 's')]
pressed:  [(5.245981400134042, 's')]
pressed:  [(5.245981400134042, 's'), (5.813663400011137, 'f')]
pressed:  [(5.245981400134042, 's'), (5.813663400011137, 'f')]
[....repeated every frame.....]
pressed:  [(5.245981400134042, 's'), (5.813663400011137, 'f')]
pressed:  [(5.245981400134042, 's'), (5.813663400011137, 'f')]
released:  [(5.245981400134042, 's', 1.4556524998042732)]
pressed:  [(5.813663400011137, 'f')]
pressed:  [(5.813663400011137, 'f')]
[....repeated every frame.....]
pressed:  [(5.813663400011137, 'f')]
pressed:  [(5.813663400011137, 'f')]
released:  [(5.813663400011137, 'f', 1.3918924001045525)]
2 Likes

Thanks! Unfortunately, though, this code produces the same results as my own above. The “pressed” message appears precisely once, when the key is pressed. The “released” message never appears.

I tested this on my PC using Windows PsychoPy 2021.2.3 (I think the standalone version?). My output after:

  1. pressing and holding ‘s’, then releasing
  2. pressing and holding ‘f’, then releasing
  3. repeating 1. and 2. once each

…looks like this, with no lines omitted.

pressed:  [(0.4725864000211004, 's')]
pressed:  [(4.40223770000739, 'f')]
pressed:  [(12.62610210001003, 's')]
pressed:  [(19.159173400024883, 'f')]

Could this be an issue with how the keyboard device is being loaded? So my keyboard is different from yours?

I wonder if a different keyboard event handling backend is being used on your setup compared to mine by default. Can you try adding the following code right after you create your Keyboard and report what gets printed,

print("Using keyboard backend: ", kb.backend)

for example:

# In "Begin Experiment" custom code
kb = keyboard.Keyboard()

print("Using keyboard backend: ", kb.backend)

I get:

Using keyboard backend:  ptb

If your system also reports using the ptb keyboard backend, could you provide a minimal working example of a project that shows the issue? Maybe something else in the project is grabbing keys.

Thanks again

Sure! My system reports using the ptb backend as well. I get the problem in a completely minimal setup like the one attached. tester.psyexp (9.1 KB)

Thanks for your help!

Your mention of “something else grabbing keys” triggered a thought in me. My test project included a keyboard component that was set to accept no keys and run forever. Once I removed that and replaced it with a mouse component instead, your code ran as expected! So thanks for that.

Is this expected behavior by the program? Would be good to know! :slight_smile:

Yes, that makes sense. If you look at the compiled python code for the example you sent, you can see that on each frame the custom keyboard kb is being checked first, followed by the keyboard component key_resp that was in the project. key_resp will consume / clear any keyboard events not first picked up by keyboard kb:

snippet from compiled code:

    keys = kb.getKeys(['f', 's'], waitRelease=False, clear=False)
    # Get and clear key when released
    keys_released = kb.getKeys(['f', 's'], waitRelease=True, clear=True)
        
    if keys:
        print("pressed: ", [(k.rt, k.name) for k in keys])
        
    if keys_released:
        print("released: ", [(k.rt, k.name, k.duration) for k in keys_released])
    
    # *key_resp* updates
    waitOnFlip = False
    if key_resp.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        # ....
    if key_resp.status == STARTED and not waitOnFlip:
        theseKeys = key_resp.getKeys(keyList=None, waitRelease=False)
        # ....