Recording All Key Presses

This seems like a simple problem, but I can’t figure out the issue. I have a dual-task experiment where a video plays on one side of the screen and some text appears on the other side. Participants respond to the text by pressing z, x, c or v. When they respond or when 4 seconds have passed (whichever comes first), the text disappears and new text appears. This continues until the video is over.

I want to record each key that they press throughout this time, and also record if they do not press anything in the 4 seconds. When I choose the option to record all keys in the data tab of my keyboard component, it only records some of the keys. I cannot figure out a pattern for what it does or doesn’t record because each time I run the experiment, it will save a different number of key presses and sometimes it saves repeated keys and other times it does not.

Then I tried to build a code component to save the keys. In the Begin Routine tab, I created a keyboard object called kb and an empty array called allKeysResp. In the Each Frame tab, I had this code:

if currentT >= 4 or kb.getKeys(['z','x','c','v']):
    allKeysResp.append(kb.getKeys)

and separately, I tried this code:

keys = kb.getKeys()
if currentT >= 4 or kb.getKeys(['z','x','c','v']):
   for key in keys:
      print(key.name)

Then in the End Routine I had it write allKeysResp to the data output. However, allKeysResp is just an array of empty arrays (looks like: [ ] [ ] [ ] [ ] [ ] ) with each little array representing a key press. I also tried to append the allKeysResp with kb.key, kb.getKeys[ ], and a number of other iterations of that type of call, but they either gave me the empty arrays or errors.

I found this topic and this topic and this topic that are similar to my issue, but couldn’t seem to apply their solutions to my experiment.

I also need to record the correct response for each of these text appearances. I have the correct response saved in the excel sheet where the text is drawing from, but it is not currently saving that information (even though that box is checked in the data menu for the keyboard component).

As a side note, I would like each key press to appear in it’s own row, rather than an array in a single cell in the excel sheet, if anyone can help me do that. I’ve only been trying to put it in an array for now because that’s all I knew how to do.

Thanks!

Could you make your experiment available to download?

How can you record all key presses if the first key press ends the routine?

Sorry, I should have been more clear. The text disappears with custom code in the Every Frame tab. The code essentially says “if current time > 4 or if there is a keyboard response, show the next set of text from the array”. The routine ends when the video is over.

I’m in the process of making my experiment available to download and will put it here, so hopefully that will clarify things too.

Here is a version of my experiment available to download.
Dowloadable.zip (115.4 KB)

There are no instructions in it, but the video on the left will play and it will end on its own or when you press the dispatch button (ending the routine either way) and the text on the right side of the screen should change every 4 seconds or each time you press either z, x, c or v. There are only two trials in this version.

Hi @cpat,

I got it to work making some small changes. You can download it here. Let me know if you need clarification on anything.

Regarding your need for the data populating different rows: I believe that this reformating is to be done after the fact. As far as I know, PsychoPy will only go to a new row if a new routine began. But in your experiments, all of the trials take place in one routine.

Thank you SO much! This is working and I’ll just do some post-processing of the code to get it into different rows.

For anyone that may come across this in the future, the code inserted into the Every Frame tab that made this work is:

if currentT >= 4 or len(keys) > 0:
        
    if len(keys) > 0:
        allKeysResp.append(response.keys[-1])
        allKeysCorr.append(int(response.keys[-1] == cur_CR))
    else:
        allKeysResp.append('None')
        allKeysCorr.append(0)

Where allkeysResp = [ ] and allKeysCorr = [ ] is defined in the start routine tab.

1 Like

Hi again -

I’ve been using this code for other routines where the layout is the same as the previous trials but they are shown after some branching activity so they have to be their own routines. When I do this, it gives me the error “list index out of range” for the allKeysResp.append(response.keys[-1]) as soon as the trial starts, regardless of a response. I changed the [-1] to [0] just to troubleshoot I got the same error so I’m not sure that the -1 is the actual problem. I also made sure that “response” was changed to the new keyboard response variable name.

Any ideas why this is happening?

Hi, I suspect you forgot the line

keys = response.getKeys()

as it’s also not present in the code you posted above. This would lead to allKeysResp.append(response.keys[-1]) triggering in the beginning of the routine and failing because response.keys at this point is only an empty list.

Hi,

Thanks so much for the quick response! I did actually include that code in my experiment, but forgot to put it here.

The code in the new routine under “Each Frame” is

currentT = round(timer.getTime())
keys = response_AEChoice.getKeys(['z','x','c','v'])

if currentT >= 4 or len(keys) > 0:
        
    if len(keys) > 0:
        allKeysResp.append(response_AEChoice.keys[-1])
        allKeysCorr.append(int(response_AEChoice.keys[-1] == cur_CR))
    else:
        allKeysResp.append('None')
        allKeysCorr.append(0)
        
    cur_TargetPackage = cur_TargetPackage + 1
    cur_match1 = cur_match1 + 1
    cur_match2 = cur_match2 + 1
    cur_match3 = cur_match3 + 1
    cur_match4 = cur_match4 + 1
    cur_CR = cur_CR + 1
    timer.reset()

    

if cur_match1 == 301:
    cur_CR = 0
    cur_TargetPackage = 0
    cur_match1 = 0
    cur_match2 = 0
    cur_match3 = 0
    cur_match4 = 0
    timer.reset()

When it quits out, it actually just keeps loading for a long time and I have to manually click the “Stop” button in the runner window. Then it spits out " allKeysResp.append(response_AEChoice.keys[-1])
IndexError: list index out of range"

In the “Begin Routine” tab I do have the arrays defined as

allKeysResp = []
allKeysCorr = []

I fixed it! It turns out that changing the response.keys() doesn’t need to be changed each time. Instead, the code in the new routine is working like this:

currentT = round(timer.getTime())
keys = response_AEChoice.getKeys(['z','x','c','v'])

if currentT >= 4 or len(keys) > 0:
        
    if len(keys) > 0:
        allKeysResp.append(keys[-1])
        allKeysCorr.append(int(keys[-1] == cur_CR))
    else:
        allKeysResp.append('None')
        allKeysCorr.append(0)
        #continue code from above 
1 Like

Hi cpat3,

I am trying to achieve something similar. Is there any way you could share the new working version of the experiment so I can get an idea of how this looks altogether (difficult for me to piece together just based on the comments alone as I am new to PsychoPy. A sub-set of the experiment would be fine too.

Thanks for any help you can give.

Hi ajus,

Would you be able to re-share this link? I cannot get it to work.

Thanks for any help.

The full experiment is pretty complex and requires a lot of excel spreadsheets and video files. If you’re just looking to record all the key presses, here is the code that I used. Do note that this may not work on Pavlovia - I only ran this locally.

In a routine at the very start of the experiment, create a code component and nothing else. In the “begin routine” tab:

import random, xlrd
from psychopy.hardware import keyboard
from psychopy import core


#randomize seed
random.seed()


#number of items to load
#num_Trucks = 2
#num_itemsPackage = 11


#counters to hold next stimulus
cur_TargetPackage = 0
cur_match1 = 0
cur_match2 = 0
cur_match3 = 0
cur_match4 = 0
cur_CR = 0

Then in your routine that hosts the actual trials:

Begin Routine

#open excel file
in_filePackage = 'PackageListEasy.xls'
inbookPackage = xlrd.open_workbook(in_filePackage)
insheetPackage = inbookPackage.sheet_by_index(0)


#arrays to hold stimuli
targetPackage = []
match1 = []
match2 = []
match3 = []
match4 = []
CorrectResponse = []

#loop through the rows in the file
for rowx in range(1, 302):
    #read in an entire row
    row = insheetPackage.row_values(rowx)
    targetPackage.append(row[0])
    match1.append(row[1])
    match2.append(row[2])
    match3.append(row[3])
    match4.append(row[4])
    CorrectResponse.append(row[5])
 
#create variable to get time in routine
timer = core.Clock()
currentT = timer.getTime()

#create keyboard variable
#kb = keyboard.Keyboard()
allKeysResp = []
allKeysCorr = []
correctResp = []
rec_target = []
rec_match1 = []
rec_match2 = []
rec_match3 = []
rec_match4 = []
key_RT = []


#get random number
ran_ind = randint(1,301)

cur_TargetPackage = ran_ind
cur_match1 = ran_ind
cur_match2 = ran_ind
cur_match3 = ran_ind
cur_match4 = ran_ind
cur_CR = ran_ind

Each Frame

currentT = timer.getTime()
keys = response.getKeys(['z','x','c','v'])
for thisKey in keys:
        keyName = thisKey.name
        
if currentT >= 4 or len(keys) > 0:
    correctResp.append(CorrectResponse[cur_CR])
    rec_target.append(targetPackage[cur_TargetPackage])
    rec_match1.append(match1[cur_match1])
    rec_match2.append(match2[cur_match2])
    rec_match3.append(match3[cur_match3])
    rec_match4.append(match4[cur_match4])

        
    if len(keys) > 0:
        key_RT.append(currentT)
        allKeysResp.append(keyName[-1])
        if keyName == CorrectResponse[cur_CR]:
            allKeysCorr.append(1)
        else:
            allKeysCorr.append(0)
        #allKeysCorr.append(int(keyName[-1] == cur_CR))
    else:
        allKeysResp.append('None')
        allKeysCorr.append(0)
        
    ran_ind = randint(1,297)
    cur_TargetPackage = ran_ind
    cur_match1 = ran_ind
    cur_match2 = ran_ind
    cur_match3 = ran_ind
    cur_match4 = ran_ind
    cur_CR = ran_ind
    timer.reset()

End Routine

#these are just writing data to the output file 
thisExp.addData('keyResps', allKeysResp)
thisExp.addData('keyCorr', allKeysCorr)
thisExp.addData('correctResponse',correctResp)
thisExp.addData('targetPackage', rec_target)
thisExp.addData('match1', rec_match1)
thisExp.addData('match2', rec_match2)
thisExp.addData('match3', rec_match3)
thisExp.addData('match4', rec_match4)
thisExp.addData('keyRT', key_RT)

If you are having trouble, it might help to post the error code, or explain what is going wrong. That way we can pinpoint the issue.

1 Like

Actually, here is a version of the task that should run locally. I don’t know how to upload an experiment since it rejected my zip file, so I just put it into a folder and here is a link.

Simplified Experiment Version Share Link

1 Like