Displaying Stimuli Only After a Key is Pressed a Certain Amount of Time

Dear all,

What I want: The experiment that I want to build includes pressing a specific key for a certain amount of time.
In detail: Participants need to click “space” for 500ms and only after the “space” key is pressed for 500ms the target stimuli (i.e., a word) can appear on the screen.
If the participant clicks “space” and releases his finger at the 400th ms (after the space keypress) I do not want stimulus to appear.

What I have tried: I have tried to add a code component (on each frame) which gets the keypresses event.getKeys() however as far as I understood this only saves the key presses on that specific frame. So if I want to see how long the key is pressed, I cannot get that information.

I have read some related topics however I could not find an exact answer. I would be really grateful if someone knows which code needs to be used.

Many Thanks,
Emre

I would probably stick all the event.getKeys() calls into a list and then query the length of that list, for example:
Start Experiment

isPressed = []

Each Frame

# Append True to list if a key is pressed, clear list if not
if ```desired key``` in event.getKeys():
    isPressed.append(```desired key``` in event.getKeys())
else:
    isPressed = []
# If key has been pressed long enough, end routine
if len(isPressed) > ```desired duration, in frames```:
    continueRoutine = False

@TParsons Thank you so much for your response!

In my code component, I do not have a “Start experiment” section. Is it a “begin experiment” or a new component of 2020.2 version of PsychoPy?
Currently, I am using v2020.1.3.

Oops, my poor wording! We did add a “Before Experiment” tab in 2020.2 but I just meant “Begin Experiment” - the only difference between Before and Begin is that Before is before the screen is drawn, so either will work if you’re just defining an empty variable.

1 Like

Hi, the event module isn’t really designed for timing the onset or duration of key presses, and should really be thought of as deprecated. The suggested code above wouldn’t work as-is, because event.getKeys() will clear the event queue when the key is first detected, and not re-detect it on subsequent checks, until it is released and pressed again.

It would be much better to shift to the Keyboard module:

The latter won’t report a duration of a keypress until it has actually been released, but, unlike the event module, each keypress is stamped with the time it was actually first pressed, so you can work out timing relative to that.

You should set clear=False in the call to the keyboard’s .getKeys() so that it will keep reporting the status of a previously-pressed key.

1 Like

Hi @Michael! Thank you for your message!

I have worked on the keyboard component a bit but I guess I have not managed to do what I want.

At the moment my routine:

The code component (in each frame) has:

keys = key_resp.getKeys(['space'], waitRelease=False,clear=False)

for key in keys:
    if key == 'space' and t > 0.5:
        continueRoutine=False
    else:
        continueRoutine=True 

Keyboard component:
force end of Routine: False
allowed keys: ‘space’
store: all keys

Routine only needs to end when ‘space’ is pressed for 500 ms.
So at the moment, I can get all the keypress but I cannot see whether the ‘space’ is pressed and also held pressed for a certain amount of time (500 ms in my case). So I was not sure how to structure my code.

Is there any way to get key presses each frame repeatedly?

Many thanks!

Don’t mix and match your custom Keyboard checking with a graphical Builder Keyboard component - that will also be checking for keypresses on every frame and hence will quite possibly conflict with your code. So delete the Builder component, and make your own Keyboard object in code, in the “begin routine” tab:

kb = keyboard.Keyboard()

Then in the “each frame” tab, you will want to check the keyboard for a key press, but not clear the queue, so you can keep checking for the space bar being held down. The key won’t have a duration while it is being held down (it will be None), but you can check the time it was pressed compared to the current time (t), and if it is >= 0.5 s ago, end the trial.

Adapting your code above, it should be something like this:

keys = kb.getKeys(['space'], waitRelease=False, clear=False)

for key in keys:
    if key == 'space' and t - key.tDown >= 0.5:
        continueRoutine=False
    else:
        continueRoutine=True 

Does it work for you? I also need to display a stimulus after some key (e.g., space) is pressed for some time (e.g., 500ms), but this code does not work on my machine…

How to force end of the routine when key is not pressed anymore?

Hi @Francisco_Contreras ,

One efficient way would be to set a Stop Duration to a certain time period. It would force the end of routine at the end of that time duration irrespective of any key pressed or not.

When solving this issue myself, I have tended to go for a mouse button press instead, because PsychoPy is better at checking the button status each frame.

event.getKeys() works differently offline and online.

1 Like