Record responses during pauses (blank screens)

Hi all,

I have created the experiment where I have 2 images presented randomly (2 response options) and a sequence of test stimuli presented sequentially (other 2 response options). The stimuli are presented together and only for a short duration. The text file that saves the responses actually saves the presses with exactly the same response time (which indicates that it actually only takes time from the first response not from the first and then the second).
It would also be great for me to save correct and incorrect responses separately, but I can’t do it now due to this problem.

EXP.py (4.5 KB)
beachcity

Hi Monika,

I’m afraid you’re really going to need to explain in more detail exactly how your task (should) work. It’s not clear from your code what should be happening either: the first keyboard check occurs after the fixation stimulus and the second occurs after the first set of stimuli. No check is done after the last set of stimuli.

Part of your difficulty may be the format of the file you are saving. It is generally far easier to analyse a datafile file that is stored in tabular format rather than as a log-like structure. That makes it instantly clear if responses are missing for a given trial, for example.

Actually, on reviewing your code, it seems like the first reaction time (assuming that any response is made) will be ~1.5 s (the duration of the fixation stimulus). Subsequent reaction times (if responses are made) will all be multiples of TextStop + PauseStop frames + the original 1.5 seconds.

Can you see that the responses are being measured with regard to the stimulus events rather than with regard to when the response was actually made? i.e. if you display a fixation stimulus for 1.5 s and then check for a response, regardless of when the response was made during that period, the measured reaction time will either be 1.5 s or it will be null (if no response was made at all). If you want to get closer to when the response was made, you need to embed your call to getKeys() inside the drawing loop, so that you will be checking for a response during every win.flip() interval. That will get your reaction time to within 16.66 ms of when it actually occurred on a 60 Hz display (more precisely, it will lag the actual response time by a mean of 8.33 ms).

Dear Michael,

The initial fixations is just needed to the participant to get ready. It is presented once.
Thanks about the tabular comment, I will try it out.
My stimuli are presented intermittently, hence the coding. The task should go that image and text stimuli are presented together for 0.5 sec and then a 1.5 pause (this goes on until the psychopy loops over all of the text stimuli). Image stimuli are presented randomly, while text stimuli are presented sequentially. When participants see the stimuli they have to press one of the 2 buttons to respond to the image stimulus (‘b’ or ‘v’) and one of the other 2 buttons (‘n’ or ‘m’) to respond to the text stimuli. But at the moment the button presses are recorded correctly, but the timing for the presses are identical :S for example, the participant pressed ‘b’ and ‘n’ and I get the Reaction Time for them as for example 0.487976557 for one and 0.487976557 for another. Also, I can’t get psychopy to accept the reply during the pause, that is a real headache.
I have tried a similar solution that you mention with “get keys” after every “winflip”, I am not sure it worked. I will try it again.

Thanks a lot for your help!

Let’s say a person pushes 'b' at 0.25 seconds and then 'n' at 0.40 seconds. Both of these keypresses get stored in a buffer, because they aren’t being actively monitored. Then at approximately 0.5 seconds, you finally check the content of the buffer. Both keys get the same time (approximately 0.5 s), because they are both being effectively ‘received’ at the same point, when event.getKeys() is called. If you want to get the actual time they are received, you need to be continuously monitoring during the drawing loop, calling event.getKeys() on every screen refresh. That way, you would detect the 'b' within one screen refresh of its actual time of 0.25, and then would separately detect the 'n' within one screen refresh of its actual time of 0.40. Make sense?

Dear Michael,

I have tried to changing the location of event.getKeys(), but unfortunately in that case I get 0 values for my presses and no RTs. I have simulated the presses in builder and in such a case all the responses are accepted and the correct timings are shown. However, I am still not able to recreate the presses in my code. Could you explain in more detail how one can call event.getKeys() on every screen refresh? I understand your idea, but unfortunately I do not understand how to implement it… I hope you don’t mind explaining one more time

Could you just post the relevant bit of your code here? Will be easier to give specific advice on your particular code, if the general explanation isn’t helping.

text_2.setText("+")
for frameN in range (90):
    text_2.draw()
    win.flip()
for word in words:
    text_2.setText(word)
    RespEvent=event.getKeys() 
    win.flip()
    for i, duration in enumerate(imageDurations):
        rand_item = random.choice(stims)
        win.flip()
    if 'v' in RespEvent:
        writer.writerow(['city', 'v', ConditionTimer.getTime()])
        city.append(1)
    elif 'b' in RespEvent:
        writer.writerow(['beach', 'b', ConditionTimer.getTime()])
        beach.append(1)
    elif 'n' in RespEvent:
        writer.writerow(['letter', 'different', 'n', ConditionTimer.getTime()])
        letter_different.append(1)
    if 'm' in RespEvent: 
        writer.writerow(['letter', 'same', 'm', ConditionTimer.getTime()])
        letter_same.append(1)
    for frameN in range (TextStop):
        rand_item.draw()
        text_2.draw()
        RespEvent=event.getKeys(keyList=['v', 'b'])
        win.flip()
    for frameN in range(PauseStop): #present blank screen for 1.5 sec (59 frames)
        win.flip()

I have kept changing my code yesterday, but none of the ways were helpful to make what I want:

for example: a person presses v and later on m and both replies are accepted and have their on RTs rather than identical RT. Other changes make either everything 0, or only 2 out of 4 responses options are accepted etc etc.

Any kind of help is greatly appreciated! :slight_smile:

Hi Monika, it would help if you have comments in your code to explain what should be happening. I can give some hints as to what is going wrong, although it is hard to tell you exactly what to do, as I’m not clear what the task is. But here goes.

  • Your first RespEvent=event.getKeys() will happen just once per word. It happens before any stimuli have been drawn. It is unlikely it will detect any keypress here, although it is possible that it will detect keypresses that were made earlier and are still waiting in the buffer.

  • You then draw rand_item but while it is being displayed, you don’t do any keyboard checking via getKeys() so no new keypresses will be detected.

  • You then have a series of checks of the RespEvent variable. Note that this was created before rand_item was drawn, so it will either be empty, or reflect a keypress made much earlier. It won’t have changed during the presentation of rand_item.

  • You then continue drawing rand_item but now with text_2 as well. During this interval, you are checking for keypresses and assigning them to RespEvent. Note if there is more than one response, only the last will be kept, as each new response overwrites the previous one. No action is taken in response to any keypress (e.g. calculating a reaction time or storing the response).

  • You then go back to the beginning of the loop and do a single getKeys(). This will have the effect of erasing anything stored in RespEvent.

  • After showing the rand_stim again, you do various checks of RespEvent, but it very likely contains nothing. etc etc.

So you need to work through your code line by line and imagine what is happening with keypresses. It is only during this period:

for frameN in range (TextStop):
    rand_item.draw()
    text_2.draw()
    RespEvent=event.getKeys(keyList=['v', 'b'])
    win.flip()

that you are continually monitoring the keyboard, and yet nothing is done in response to it.

Perhaps what you should do is re-post the code above but add comments to say what you want to achieve at each point (i.e. what is being displayed, what the subject should do, and what we are measuring (e.g. reaction time relative to event x, judgement of category y, etc)). You know what you are doing, but we can’t really tell just from the bare code.

text_2.setText("+") 
for frameN in range (90):
    text_2.draw() # just a small pause for the participant to get ready (presented only once in the beginning of the script) 
    win.flip()
for word in words:
    text_2.setText(word) # a sequence of single letters on numbers (the sequence is specified and runs from the first stimulus till the last)
    RespEvent=event.getKeys() 
    win.flip()
    for i, duration in enumerate(imageDurations): # a selected list of images
        rand_item = random.choice(stims) # one image is presented randomly from the list of available images
        win.flip()
        if 'v' in RespEvent:
            writer.writerow(['city', 'v', ConditionTimer.getTime()]) # correct of incorrect response to the image (depends on what was presented in this example 'v' is correct for city images)
            city.append(1) # this need to be corrected, as at the moment it will accept all 'v' presses even though the image of the beach might be presented
        elif 'b' in RespEvent:
            writer.writerow(['beach', 'b', ConditionTimer.getTime()]) # correct of incorrect response to the image (depends on what was presented in this example 'b' is correct for beach images)
            beach.append(1)
        if 'n' in RespEvent:
            writer.writerow(['letter', 'different', 'n', ConditionTimer.getTime()]) # correct or incorrect response to the letter or number (depends on a list of predetermined answers which are also ordered sequentially)
            letter_different.append(1) #this has to be matched with the list of predetermined answers to be able to discern whether the response was correct or incorrect
        elif 'm' in RespEvent: 
            writer.writerow(['letter', 'same', 'm', ConditionTimer.getTime()]) # correct or incorrect response to the letter or number (depends on a list of predetermined answers which are also ordered sequentially)
            letter_same.append(1)
    for frameN in range (TextStop): # I had to include this otherwise the images were presented very very fast and I need a timing of 0.5 sec image + letter and 1.5 sec pause
        rand_item.draw() #random item
        text_2.draw() # sequential item
        RespEvent=event.getKeys()
        win.flip()
    for frameN in range(PauseStop): #present blank screen for 1.5 sec (59 frames)
        win.flip()

Sorry, the responses were intended wrongly since my last attempts to change everything “blindly”. I understand that each press is replaced by the subsequent one, but I am not able to fix this issue.

As I mention in the comments of the code I do draw the random item together with the word (letter or number to be more exact), because otherwise the random item is drawn more often than several times per second and I need the timing of the both stimuli to be 0.5 sec (and they are indeed presented together) and then get a pause of 1.5 sec. The answer should be also recorded during the pause.

About answers: I need which button was pressed (accuracy) and at what time (reaction time). Ideally, if I could get also the info whether the press was “correct” or “incorrect” and the 0 in time scale would be the presentation of images and letters, that would be perfect. However, if I just get reliable presses I could write data analyses scripts later, which would discern the correct/incorrect answers and make the RT an amount of time the participant took to respond with respect to the stimulus onset.

I attached my script and images to the first message of this post, it does run. I just can’t get reliable responses.

Dear Michael,

I am sorry to bother you, but maybe I didn’t explain my task properly? May I ask you how should I accomplish successfully the issue of getting the key presses for each screen refresh? How should I continuously monitor the drawing loop?

Yes, I’m struggling to understand the task a bit, but in essence, you need to be checking the keyboard on every screen refresh and respond to it immediately, or you will miss responses. Your code could be simplified to something like this:

 # just a small pause for the participant to get ready 
# (presented only once in the beginning of the script) 
text_2.setText("+") 
for frameN in range (90):
    text_2.draw()
    win.flip()

# run actual trials:
for trial_number, word in enumerate(words):
    text_2.setText(word) # a sequence of single letters on numbers 
    # (the sequence is specified and runs from the first stimulus till the last)
    rand_item = random.choice(stims) # one image is presented randomly 
    # from the list of available images
    
	for frameN in range (120): # a timing of 0.5 sec image + letter + 1.5 s pause
		if frameN < 30: # for the first 0.5 s
			rand_item.draw() #random item
			text_2.draw() # sequential item
		win.flip()

        if frameN == 0: # start timing from the first frame.
            trialTimer.reset()
   
        # check response on every frame:
		keys = event.getKeys()
		if keys:
			writer.writerow([trial_number, rand_item.name, keys[0], trialTimer.getTime()])

Note that I’m not doing anything about judging the correctness of responses. At the moment there doesn’t seem to be a a way to do that. You might want to look at using the TrialHandler class to allow you to easily group together all the information required for a trial. That will also simplify saving data for you, meaning that you don’t ned to write to disk yourself all the time.