Listening to keyboard events

Dear Psychopy,

We would like to be able to launch a keyboard ‘queue’ , do some stimulation, and check after that stimulation whether a key was pressed and recorded in the queue (and gather timing of the key press since initiating the queue). I wonder whether this is something that already exists in psychopy, or whether we would have to program this ourselves (perhaps using pyglet).

e.g. trial in a pseudo experiment

queue listen for keys

draw stuff

query whether anything happened with the keyboard during … draw stuff …

a similar procedure exists in PTB3:

http://psychtoolbox.org/docs/KbQueueCreate

This would allow more flexibility in gathering responses from participants in more complex tasks, where many things happen during a trial, and a traditional event.waitKeys wouldn’t be possible.

many thanks for your help,

ian

Are you talking about event.getkeys and event.waitkeys? http://www.psychopy.org/api/event.html bottom of the page.
getkeys lets you specify certain kb press, and records whatever are being pressed, while waitkeys is the same, but holds the operation until certain keypress is made. You can use logging to record all the events happening too.

1 Like

Hi Jin_Jeon,

Thanks for your reply. I don’t believe that event.getkeys and event.waitkeys can do what I am after.

with event.getkeys, you still have to implement something like:

def get_keypress(class_obj):
    keys = event.getKeys()
    if keys:
        return keys[0]
    else:
        return None

while True:
    # key logger
    if not response_made and self.trial_start.getTime() < tp['max response time']:
        key = get_keypress(self)
        if key and key in tp['possible responses']:
            self.rt = self.trial_start.getTime()
            self.response = key
            response_made = True

    # if trial is over here
    if self.trial_start.getTime() > tp['trial length']:
        break

    # Only draw the image during the defined image duration
    if self.trial_start.getTime() < tp['img duration']:
        im = tp['target image']
        im.setPos((0, 0))
        im.draw()

    # otherwise draw a fixation
    else:
        fixation.draw()
    self.win.flip()

Now this is useful but really what I would like to do is more:

queue = listen_keys()

for frame in frames:
    im = frame
    im.setPos((0, 0))
    im.draw()
    self.win.flip()

fixation.draw()
self.win.flip()

# or do anything here that can't rely on a while loop

keypress = collect_any_response(queue)

rt = keypress['time']
answer = keypress['key']
...

I hope this makes more sense now? Is this something that event.getKeys() can achieve?

Thanks for your help,

best wishes,

ian

Hey Ian,

Yeah, I think you are going in the right direction. I’ll try to write an example code. Hopefully the code makes sense:

exampleClock = core.Clock() #creates a clock time
time1_trial = exampleClock.getTime() # gets time at this point 
keyPressed = event.getKeys(keyList=['period','slash'], modifiers=False, timeStamped=exampleClock) 
    if keyPressed: 
        #any other stuff you want 
        time2_trial = exampleClock.getTime()
    reaction_time = time2_trial - time1_trial 

self.rt = reaction_time
# or 
self.rt = event.getKeys[0][1]

If we have something like the above,
event.getKeys[0][0] would give you the first key press info, while
event.getKeys[0][1] return the reaction time of that first key.
So then you can set something like self.rt = event.getKeys[0][1] for reaction time.

Also, there is self.rt = reaction_time. I think the code itself is direct by itself, but basically I measure timing before the keypress, then measure the time again once the key is pressed. Then I subtract the two to get the timing difference, and add it into the data frame. But yeah, if you have multiple keys you want to record, I think you might want to create a helper function or otherwise list them all individually.

I hope this helps!

Hi

Thanks again for your quick reply.

I am not sure we are thinking about the same thing. Maybe my question wasn’t asked clearly enough.

What you are refering to I think I already know how to do. I’ll try and better explain what I am after.

queue = listen_keys()
# from this moment on, if the user presses the keyboard, it gets recorded in the background

# now we draw stuff in a trial, yet we don't actively monitor for key presses here
for frame in frames:
    # the user can still press a key here
    im = frame
    im.setPos((0, 0))
    im.draw()
    self.win.flip()

#the user can respond here too, as it is on an independent process.

fixation.draw()
self.win.flip()

# or do anything here that can't rely on a while loop

# now my trial is over and I want to see if any key has been pressed in the queue

keypress = collect_any_response(queue)

rt = keypress['time']
answer = keypress['key']

In Psychtoolbox, what you are describing is the equivalent of KbCheck.

http://psychtoolbox.org/docs/KbCheck

Another way of doing things is by starting a parallel queue that listens for keys in the background:

http://psychtoolbox.org/docs/KbQueueCreate

I would like to know if there is an equivalent in PsychoPy to KbQueueCreate, KbQueueStart KbQueueStop and KbQueueCheck.

many thanks!

ian

Hey Ian,

Oops sorry I might have misunderstood. As far as I know, I don’t think there is something equivalent in PsychoPy to KbQueueCreate specifically. One thing I can think of implementing such would be just using getKeys at the beginning, and let it sit to register any keys being pressed to a separate list or data frame while certain tasks are being performed? For example:

for loop: 
    exampleClock = core.Clock() #creates a clock time
    time1_trial = exampleClock.getTime() # gets time at this point 
    keyPressed = event.getKeys(keyList=['period','slash'], modifiers=False, timeStamped=exampleClock)
    if keyPressed: 
        sampleList.append = keyPressed[0][0]
        #any other stuff you want 
        time2_trial = exampleClock.getTime()
    reaction_time = time2_trial - time1_trial 

and use sampleList to get all the list of keys pressed?

This is an old thread, but as it came up in a Google search for me, I thought I’d note that since this was posted, Mario Kleiner has ported the Psychtoolbox keyboard handling to PsychoPy. That Keyboard class should now be used instead of the event module:

https://www.psychopy.org/api/hardware/keyboard.html

So the sort of functionality sought here should be directly accessible in PsychoPy now.