Search code examples
pythontimelinepsychopy

psychopy reset variable for each trial


I made my own rating scale, which is a timeline from 0 to 28:18. It moves according to how fast people press the 't' or 'b' keys for each trial. What comes up on the screen looks like this:

experiment

I want the time at the top to reset to 14:09 for each trial. After each trial is a 0.5 second inter trial interval during which a '+' is presented on the screen. The problem I'm having is that if you press 't' or 'b' during the ITI, the next trial won't start at 14:09. Instead, it will be shifted by the direction in which a 't' or 'b' press shifts the timeline. Here's my current code where I tried to correct for this:

prevPos = 0
trialNum=0
b_list=[]
t_list=[]
key=[]

# loop through pictures
for eachPic in catPictures:
    b_list=[]
    t_list=[]
    timer = core.CountdownTimer(TrialDuration)
    while timer.getTime() > 0:
        for key in event.getKeys():
            if key in ['escape']:
                core.quit() # quit if they press escape
            if key in ['b']:
                # add keypress to list for each keypress. then move cursor proportionally to length of this list
                b_list.append(key)
                prevPos+=len(b_list)
            if key in ['t']:
                t_list.append(key)
                prevPos-=len(t_list)

    # set upper and lower limits to where cursor can go (which will later be halved to restrict range of cursor on the screen)
        if prevPos <= -849:
            prevPos = -849
        elif prevPos >=849:
            prevPos = 849
        # make absolute position so pos_absolute becomes a range from 0 to 300 (based on 28:18 min movie)
        pos_absolute = prevPos + 849
        # need to have range of 1698 (# of seconds in 28:18)
        # need to have range of 1698 (# of seconds in 28:18)
        # current range is 0 to 849 (which is 50% of 1698)
        seconds =  pos_absolute

        Image2 = visual.ImageStim(window)
        #curbImage2.setSize = ((0.5,0.5), units = 'norm')
        # make a little higher than the absolute middle
        Image2.setPos([0,100])
        # use each image (i in curbImages)
        Image2.setImage(catPictures[trialNum])

        # define cursor that moves along timeline
        cursorImage = visual.ImageStim(window)
        cursorImage.setImage(directoryStim+'cursor.png')
        # make cursor move by however big prevPos is
        cursorImage.setPos([int(prevPos)*.5,int(400)])
        # make the line
        timeline = visual.SimpleImageStim(win=window, image=directoryStim+'line.png', units='pix', pos=[0, 400])
    event.clearEvents() # get rid of other, unprocessed events

        # print min and max values next to timeline
        min = visual.TextStim(window, '0:00', color='Black', pos=[-500, 400])
        max = visual.TextStim(window, '28:18', color='Black', pos=[500, 400])
        # print constantly updating time value
        timeText = visual.TextStim(window,'%d:%02d' % (seconds/60, seconds % 60),color='Black',wrapWidth=1080,font='Verdana', pos=[0,465], height=50)

        ## now put everything on the screen
        Image2.draw(window)
        min.draw(window)
        max.draw(window)
        timeText.draw(window)
        timeline.draw(window)
        cursorImage.draw(window)
    ## flip so it actually appears
        window.flip()

    ITI = visual.TextStim(window, '+', pos=[0,0], height=50, color='Black')
    ITI.draw(window)
    window.flip()
    core.wait(.5,.5)
    trialNum+=1
    prevPos = 0
    b_list =[]
    t_list=[]
    key=[] 

How can I make my timeline reset to 14:09 (aka prevPos = 0) at the beginning of each trial, even if people press 't' or 'b' as the trial ends or during the inter trial interval?


Solution

    1. Indent stuff under the inner for-loop.
    2. Move your event.clearEvents() to just before your while-loop or after the core.wait. That's where you start listening for new key presses. Presses during the wait-period will be returned on the next call to event.getKeys() in the next cycle of the while-loop. That's why it moves the cursor immediately. There's no real reason to have the event.clearEvents() where you have it since you only listen for events in the while-loop. That's why I suggest that you move it rather than inserting a new one.
    3. Initiating psychopy stimuli is quite reseource-heavy and can sometimes take several hundred milliseconds. There's no reason to create several new ImageStims and TextStims on each trial.
    4. pos_absolute and seconds are redundant. You could do with only seconds.
    5. A style-thing: rather than stim.setPos(x) you can now do stim.pos = x and so on for all other attributes. This is the preferred way to set stimulus attributes from now on (cleaner code and allows for more operations on the attributes).

    Here's a cleaned up code with the above changes:

    # Stimuli
    Image2 = visual.ImageStim(window)
    cursorImage = visual.ImageStim(window)
    min = visual.TextStim(window, '0:00', color='Black', pos=[-500, 400])
    max = visual.TextStim(window, '28:18', color='Black', pos=[500, 400])
    timeText = visual.TextStim(window,color='Black',wrapWidth=1080,font='Verdana', pos=[0,465], height=50)
    ITI = visual.TextStim(window, '+', pos=[0,0], height=50, color='Black')
    timeline = visual.SimpleImageStim(win=window, image=directoryStim+'line.png', units='pix', pos=[0, 400])
    timer = core.CountdownTimer(TrialDuration)
    
    # loop through pictures
    trialNum=0
    for eachPic in catPictures:
        prevPos = 0
        key=[]
        b_list=[]
        t_list=[]
        timer.reset()
        event.clearEvents() # get rid of other, unprocessed events
        while timer.getTime() > 0:
            for key in event.getKeys():
                if key in ['escape']:
                    core.quit() # quit if they press escape
                if key in ['b']:
                    # add keypress to list for each keypress. then move cursor proportionally to length of this list
                    b_list.append(key)
                    prevPos+=len(b_list)
                if key in ['t']:
                    t_list.append(key)
                    prevPos-=len(t_list)
    
            # set upper and lower limits to where cursor can go (which will later be halved to restrict range of cursor on the screen)
            if prevPos <= -849:
                prevPos = -849
            elif prevPos >=849:
                prevPos = 849
            # make absolute position so pos_absolute becomes a range from 0 to 300 (based on 28:18 min movie)
            # need to have range of 1698 (# of seconds in 28:18)
            # need to have range of 1698 (# of seconds in 28:18)
            # current range is 0 to 849 (which is 50% of 1698)
            seconds =  prevPos + 849
    
            #curbImage2.size = ((0.5,0.5), units = 'norm')
            # make a little higher than the absolute middle
            Image2.pos = [0,100]
            # use each image (i in curbImages)
            Image2.image = catPictures[trialNum]
    
            # define cursor that moves along timeline
            cursorImage.image = directoryStim+'cursor.png'
            # make cursor move by however big prevPos is
            cursorImage.pos = [int(prevPos)*.5,int(400)]
            timeText.text = '%d:%02d' % (seconds/60, seconds % 60))
    
            ## now put everything on the screen
            Image2.draw(window)
            min.draw(window)
            max.draw(window)
            timeText.draw(window)
            timeline.draw(window)
            cursorImage.draw(window)
            ## flip so it actually appears
            window.flip()
    
        ITI.draw(window)
        window.flip()
        core.wait(.5,.5)
        trialNum+=1
    

    Notice that I also removed several seemingly unnecessary resets of b_list, key etc.