psychopy.org | Reference | Downloads | Github

Help with Movie files in a loop

Hi everyone, I’m trying to create an experiment that would let me play mp4 files as the stimuli. It calls “turn” and “angle” from a csv and that defines which of two videos plays, and which orientation it plays at. I’m having issues with my trial loop as well as getting the key responses to work. I want the participant to be able to press left or right to start the next trial. I’m very new to coding! This :frowning:

#starting the trials 
for thisTrial in trials:
    currentLoop = trials
    rt = []
    corr = 0
    turn = str(thisTrial[u'turn'])
    angle = str(thisTrial[u'angle'])
    
    print('angle:', angle)
    print('turn:', turn)
    
    keys = event.getKeys()
    
    if turn == 'left':
        mov = visual.MovieStim3(win, 'Left_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)
        globalClock = core.Clock()
        
        mov.setOri(int(angle))
        
        while mov.status != visual.FINISHED:
            mov.draw()
            win.flip()
        
            if keys:
                if keys[0] == 'Esc':  
                   shutdown()
                print(keys[0])
                if keys[0] == 'left':
                   corr = 1
                   mov.status = visual.FINISHED
                elif keys[0] == 'right':
                   corr = 0
                   mov.status = visual.FINISHED
            
    elif turn == 'right': 
        mov = visual.MovieStim3(win, 'Right_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)
        globalClock = core.Clock()
        mov.setOri(int(angle))
        
        while mov.status != visual.FINISHED:
            mov.draw()
            win.flip()
            
            if keys:
                if keys[0] == 'Esc':  shutdown()
                print(keys[0])
                if keys[0] == 'right':
                    corr = 1
                    mov.status = visual.FINISHED
                elif keys[0] == 'left':
                    corr = 0
                    mov.status = visual.FINISHED
    
    # Proceed to next trial
    trialnum += 1
    thisTrial = trials.trialList[trialnum]

Hi,

This is just an issue of needing to consider what happens within each loop. e.g. some things are constant and shouldn’t be in a loop at all. Other things change once per iteration of the for thisTrial in trials: loop (i.e. once per trial), while other things need to happen once per iteration of the while mov.status != visual.FINISHED: loop (i.e. once per screen refresh).

These things don’t change and so should happen before the trial loop even starts, so they just happen once:

currentLoop = trials # you don't refer to this variable
# anyway so it could probably just be dropped entirely.

Nothing about the movies changes, so you should just create two of them in advance, and use the correct one as required. This is particularly because creating movies is time-expensive, so ideally should be done outside of your trials, as it can cause delays and slow downs:

left_mov = visual.MovieStim3(win, 'Left_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)

right_mov = visual.MovieStim3(win, 'Right_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)

Then inside your trial loop, you just do this, which avoids re-creating the movies (instead, you just point your mov variable to the required movie stimulus):

if turn == 'left':
    mov = left_mov

and

elif turn == 'right': 
    mov = right_mov

Lastly, the issue with no being able to exit the trial is that you only check the keyboard once per trial, before the movie even starts playing. So if someone pushed a key during the movie, it wouldn’t be detected. So check the keyboard during the movie drawing loop (i.e. once per screen refresh):

while mov.status != visual.FINISHED:
    mov.draw()
    win.flip()

    keys = event.getKeys() # needs to be done here

    if keys:
        # do whatever

Lastly, you are doubling up on converting types, e.g.

turn = str(thisTrial[u'turn']) # turn it into a string
print('angle:', angle) # print it
mov.setOri(int(angle)) # turn it back into an integer

Instead you could just do this:

angle = thisTrial['angle'] # no need to convert if it is already a number
print(f'angle:{angle}') # no need to convert in a formatted string (Python 3 only)
mov.setOri(angle) # use it directly as a number

Even angle = thisTrial['angle']) may be unnecessary if you are using a TrialHandler, as it will probably automatically do that for you.

Lastly lastly, notice also that by using just one variable for the movie name, you can remove the duplication of code. So here is a rough attempt at revising all of that code, taking into account the above suggestions:

# prepare for the trials:
left_mov = visual.MovieStim3(win, 'Left_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)
right_mov = visual.MovieStim3(win, 'Right_Turn.mp4', size=(800, 800), flipVert=False, flipHoriz=False, loop=False)

currentLoop = trials # doesn't seem to be necessary 
rt = [] # not used and probably better to use the TrialHandler to store data

#starting the trials 
for thisTrial in trials:

    # probably unnecessary if using a TrialHandler:
    turn = thisTrial['turn']
    angle = thisTrial['angle']
    
    print(f'angle: {angle}')
    print(f'turn: {turn}')
    
    if turn == 'left':
        mov = left_mov
    else:
        mov = right_mov

    globalClock = core.Clock() # not sure why this is being set here (and unused)
    mov.setOri(angle)
        
    while mov.status != visual.FINISHED:
        mov.draw()
        win.flip()

        keys = event.getKeys(keyList = ['esc', 'left', 'right']) # restrict the possible keys
        
        if keys:
            print(keys[0])

            if keys[0] == 'esc':  
                shutdown()        
            elif keys[0] == turn: # only one check needed now
               corr = 1
            else: # must be wrong, whether left or right
               corr = 0

            # NB need to store this in the data, easy if using a TrialHandler:
            trials.addData('correct', core)

           mov.status = visual.FINISHED

Hopefully that is much more readable now and plainly contains less duplication.

Lastly lastly lastly, this sort of code:

    # Proceed to next trial
    trialnum += 1
    thisTrial = trials.trialList[trialnum]

is unnecessary in Python for loops. The line for thisTrial in trials: does everything for you automatically and you don’t need to count iteration numbers like you do in many other languages. Python simply iterates over your list of trials (hopefully you are using a PsychoPy TrialHandler object for that purpose).