Builder - Presenting a sequence of images (within a trial) controlled by button press

407 views
Skip to first unread message

Anna Mestres

unread,
Nov 27, 2015, 11:13:54 AM11/27/15
to psychopy-users
Hello,

I am very new to PsychoPy, and struggling with what should be an easy to programme task. I have successfully programmed this task with other software, but I just can't get it right with PsychoPy. In my experiment one trial consists of:

Fixation cross for 500ms

Presentation of Element1 (image component-> $E1)
end with button press ('space') or after 4 seconds (save RT to Element1) (keyboard component, KeyE1)

Presentation of Element2 (image component->$E2)
end with button press ('space') or after 4 seconds (save RT to Element2) (keyboard component, KeyE2)

Presentation of Element3 (image component->$E3)
end with button press ('space') or after 4 seconds (save RT to Element3) (keyboard component, KeyE3)

Presentation of a question mark (text component) (Question)
end with button press ('a','s','d','f','g','h','j','k','l',';') or after 4 seconds (save RT to Question and correct answer/button (corrBu)) (keyboard component, KeyQ)

Presentation of correct answer (Monitor)
end with button press ('space') or after 4 seconds (save RT to Monitor) (keyboard component, KeyM)

Blank screen duration 500 ms


Loop randomly through all trials

The excel file is defined as:

trialNum   condition   E1                 E2                   E3                condName        corrAns      corrBu
1                  1         2E1_3.bmp    4AE_4.bmp       6E12.bmp        NA_A12             7                ;
2                  1         2E1_4.bmp    4AE_3.bmp       6E12.bmp        NA_A12             7                ;
3                  1         2E1_2.bmp    4AE_4.bmp       6E12.bmp        NA_A12             6                l
.
.
.
48

So each row is a trial.

In order to define the start and end of each stimulus in the trial I have used:

For the start: $FixCross.status == STOPPED being the name of the component, the previous one; for Element1 being $FixCross.status == STOPPED, for Element 2 being $Element1.status == STOPPED, and so on

For stop: $KeyE1.keys == 'space' for Element1, $KeyE2.keys == 'space' for Element2, $KeyE3.keys == 'space' for Element3, and so on (I couldn't figure out how to specify stop = button press or > 4 seconds)

Each keyboard component is defined to start with its corresponding element (image or text component), for instance, $Element1.status == STARTED for KeyE1, stop is left blank, allowed keys defined (e.g. 'space'), force end of routine unchecked, discard previous and sync RT with screen checked.

When running the experiment, it presents the fixation cross, it presents the first image and it disappears when i press 'space', it presents the second image, but then it gets stuck there, 'space' doesn't end the stimulus and continue with the next one as it is supposed to. It doesn't save anything also.

I have checked the examples, read through the posts, manual, and although I've found bits of information which I have implemented (using the component status and *.keys to control start and end), I have tried to eliminate the keyboard component and insert codes before each stimulus (unsuccessfully, well actually with a similar outcome where presentation doesn't go passed the second image). I cannot figure out why it works for the first image but not for the rest of the stimuli.

I am a bit ashamed I cannot figure this out as it is a simple: stimulus -> response via keyboard x 5 different stimuli (3 images, 2 text) presented one after the other. Record and save all RT and responses, save all columns of the excel.

I have attached the experiment I have built (builder) and excel file, I have not attached the images though (if needed please let me know).

I would be very grateful I anyone could help me.

Thank you very much,
Anna

test4.psyexp
B1_12list1.xlsx

Richard Höchenberger

unread,
Dec 1, 2015, 4:06:36 PM12/1/15
to psychop...@googlegroups.com
Dear Anna,

I see your message is already a couple of days old; have you managed to resolve the issues in the meantime, or do you still need assistance? I could look into this and help you by the end of this week.

Best wishes,

    Richard

Anna Mestres

unread,
Dec 2, 2015, 4:57:45 AM12/2/15
to psychopy-users
Hi Richard,

I have changed lots of things, and now it is more or less working. I am not sure about the response times though, if it is recording them properly.

What I have done now is:

1) Because the three images are the three elements of a sequence, in my excel file I have put in one column the three images names. So it is the same as before but now I only have one column called seq (sequence) (instead of three called E1, E2 and E3).

2) I have made different routines for the different stimuli: 1) Fixation (cross for 500 ms). 2) Sequence (I explain below as it is the complicated bit). 3) Question (text component: question mark, keyboard component). 4) Monitor (text component: $corrAns, keyboard component). 5) ISI (blank for 500ms)

3) For the sequence:
I start with a code component which:

In begin routine:

seqList = seq.split()
elementNumber = 0
Element.setImage(seqList[elementNumber])

then in each frame:

keypresses = event.getKeys() # returns a list of keypresses 

if len(keypresses) > 0: # at least one key was pushed 

    if 'space' in keypresses: 
        thisResponseTime = t
        elementNumber = elementNumber + 1
        if elementNumber < len(seqList):
           #if elementNumber == 1:
               timeOfLastResponse = 0
               thisExp.addData('E_' + str(elementNumber), thisResponseTime - timeOfLastResponse) 
               timeOfLastResponse = thisResponseTime
          
               Element.setImage(seqList[elementNumber])
        else:
           continueRoutine = False
    elif 'escape' in keypresses: 
        core.quit()

and in end routine:

if elementNumber > len(seqList):
  continueRoutine = False

Then on the image element, duration is empty and Image is empty (and constant).

Then I have a loop (random) called trials which in conditions calls $blockNum, and another loop (sequential) called block with in conditions an excel with one column called blockNum that specifies the names of the excel files (8 files, 8 blocks). I still need to add the practice and end of experiments parts, but that I'll do after the core works.

These code before I adapted from a previous post about a self-paced reading task (as this is similar to a self-paced task but with sequences of images instead of words).

My problem is that it only saves the RT for the first two images, that is E_1 and E_2, but not E_3. I have tried to change if elementNumber < len(seqList): to if elementNumber <= len(seqList):, but then I get an error in Element.setImage(seqlist[elementNumber]), IndexError: list index out of range. But importantly in the excel file now there is a E_3

In the original post, in the begin routine code ElementNumber was equal to -1, and then in each frame  if elementNumber == 0: (it was initialized). This doesn't work in my case, as then it presents four images instead of three, starting with the last listed and repeating it. I had commented  #if elementNumber == 1: because it was not doing anything (I had it at 0 before, and of course it would never enter the loop because it would always be elementNumber = elementNumber + 1 before that line).

I am not sure if this is the best way to record the RT to each image, so I am opened to any suggestions. Now it doesn't record the one for the 3rd image (which should be the third time it loops around), and I am not sure if the RT recorded are accurate. 

To sum up, the bit that I am missing now it is recording and saving the response times to each of the three images in the routine sequence.

Many thanks,
Anna



Richard Höchenberger

unread,
Dec 3, 2015, 9:03:53 AM12/3/15
to psychop...@googlegroups.com
Hello Anna,

I think it would help if you could send me the latest version of your experiment (this includes the psyexp file, conditions file, and, if possible, stimuli) off-list (richard.ho...@gmail.com); I could then use a debugger to pinpoint where things are going wrong.

    Richard

Richard Höchenberger

unread,
Dec 6, 2015, 6:16:27 AM12/6/15
to psychopy-users
Dear Anna,

taking this back to the list for everyone to read.

(Anna sent me her complete experiment including all stimuli and conditions files off-list).


You were almost there, I just had to do some very minor adjustments, and it seems to be working now.

The contents of the Begin Routine tab of code component in the Sequence routine was left unchanged:
seqList = seq.split()
elementNumber = 0
Element.setImage(seqList[elementNumber])


I adjusted the code in the Each Frame tab the following way:

keypresses = event.getKeys() # returns a list of keypresses 

if keypresses:  # at least one key was pushed
    if 'space' in keypresses:
        thisResponseTime
=
 t

       
if elementNumber == 0:
            timeOfLastResponse
= 0
        else:
            timeOfLastResponse
= thisResponseTime

        thisExp
.addData(
               
'E_' + str(elementNumber + 1),
                thisResponseTime
- timeOfLastResponse
       
)
        timeOfLastResponse
= thisResponseTime

       
# Prepare the next stimulus, but only if there is
        # at least one more stimulus left the present after
        # the current one in this trial.
        if elementNumber < len(seqList) - 1:
            elementNumber
+= 1
            Element.setImage(seqList[elementNumber])
       
else:
            continueRoutine
= False

    elif 'escape' in keypresses:
        core
.quit()

This code is almost identical to yours, except:

I changed the test if we already have collected any keypresses to be more 'Pythonic'.
old:
if len(keypresses) > 0: # at least one key was pushed 
new:
if keypresses:  # at least one key was pushed

We collect response times at the beginning, and only after that increase the elementNumber counter.
old: You increase the counter, and after that collect RTs and select the next stimulus. To prevent access beyond the end of seqList, you tested 'if elementNumber < len(seqList); but since you increased the counter BEFORE that test, the code would only execute for the first two stimuli, and not record an RT for the third one.
    if 'space' in keypresses: 
        thisResponseTime = t
        elementNumber = elementNumber + 1
        if elementNumber < len(seqList):
           #if elementNumber == 1:
               timeOfLastResponse = 0
               thisExp.addData('E_' + str(elementNumber), thisResponseTime - timeOfLastResponse) 
               timeOfLastResponse = thisResponseTime
          
               Element.setImage(seqList[elementNumber])
        else:
           continueRoutine = False
new:
if 'space' in keypresses:
thisResponseTime = t

    if elementNumber == 0:
timeOfLastResponse = 0
else:
timeOfLastResponse = thisResponseTime

thisExp.addData(
'E_' + str(elementNumber + 1),
thisResponseTime - timeOfLastResponse
)
timeOfLastResponse = thisResponseTime

# Prepare the next stimulus, but only if there is
# at least one more stimulus left the present after
# the current one in this trial.
if elementNumber < len(seqList) - 1:
elementNumber += 1
        Element.setImage(seqList[elementNumber])
else:
continueRoutine = False

Please note that, since we are using zero-based indexing here for the element numbers (remember, we started off with 'elementNumber = 0' in the Begin Routine tab, and only increase the counter AFTER processing the current response and ONLY IF there are still stimuli left to present within the current trial), we have to ADD one to the column header that goes into the log file, therefore:
    thisExp.addData(
'E_' + str(elementNumber + 1),
thisResponseTime - timeOfLastResponse
)


Please remove the code from the End Routine tab completely, as it is not required (we end the routine by setting continueRoutine to False in the Each Frame code as soon as we determine we've presented the last stimulus in the current trial).

I didn't do a full experimental run, but ensured, using a debugger, that the code that save the response times is actually executed 3 times per trial. Please verify that the results you're getting are indeed sane. :)

All the best,

    Richard

Anna Mestres

unread,
Dec 7, 2015, 12:21:18 PM12/7/15
to psychopy-users
Hi Richard,

Many thanks! It works almost perfectly. It saves the RT for E_1, but for E_2 and E_3 the RT saved is 0. If I comment (as below) the "else:" part then it saves the RT, so now it works. Thanks again!

if elementNumber == 0:
timeOfLastResponse = 0
    #else:
#timeOfLastResponse = thisResponseTime


thisExp.addData(
'E_' + str(elementNumber + 1),
thisResponseTime - timeOfLastResponse
)
timeOfLastResponse = thisResponseTime

Best wishes,
Anna

Richard Höchenberger

unread,
Dec 7, 2015, 8:30:22 PM12/7/15
to psychop...@googlegroups.com
Hello Anna,

On Mon, Dec 7, 2015 at 6:21 PM, Anna Mestres <ames...@gmail.com> wrote:
> Hi Richard,
>
> Many thanks! It works almost perfectly. It saves the RT for E_1, but for E_2
> and E_3 the RT saved is 0. If I comment (as below) the "else:" part then it
> saves the RT, so now it works. Thanks again!

great to see it's working for you now! You were absolutely right in
commenting out the 'else' block, this was an accidental leftover I
forgot to remove :)

Cheers,

Richard
Reply all
Reply to author
Forward
0 new messages