psychopy.org | Reference | Downloads | Github

Creating a Progress Bar


#1

Hello,

I’m trying to create a progress bar for participants to view throughout the entire course of the experiment. I’ve made progress bars before, but the issue I’m running into now is that I want to display the progress bar while the experiment is going on. I think that constantly using win.flip() is causing my problem. Here is a simplified version of my code:

win = visual.Window(size = (2560, 1600), fullscr = True, pos = (0,0), units = "height", color = [-1,-1,-1])
cumulativeTimer = core.Clock()
width,height = [1.6,1]
run_length = 2 #in minutes

prog_bar_outline = visual.Rect(win=win, pos=(0,height-0.6), size=(width*1.9,0.1), lineColor='White')
prog_bar = visual.Rect(win=win, pos=(prog_bar_outline.size[0]),prog_bar_outline.pos[1]), fillColor='White')

cumulativeTimer.reset()
while cumulativeTimer.getTime() < run_length*60:
    escape_option()
    prog_bar.size = (cumulativeTimer.getTime(), 0.1)
    prog_bar_outline.setAutoDraw(True)
    #prog_bar.setAutoDraw(True)
    prog_bar.draw()
    win.flip()
    for i in xrange(start, end):
        #Rest of experiment (aka, display other text and images)

I’ve tried using prog_bar.setAutoDraw(True), but this doesn’t allow my to dynamically change the size or position of my progress bar. Is there a way I can constantly update the size of the progress bar without the subsequent win.flip() commands in the rest of my code removing it?

Thank you!


#2

You must use win.flip() or else your updated stimulus will never get displayed.

Your actual problem is not with win.flip() but likely due to incorrect nesting of your for loop within your while loop. You probably need to describe what the sequence of events across and within trials is in order for us to suggest a better code structure. i.e. I’m not sure which loop should be inner-most, as it isn’t clear what start and end represent. But it is likely that you simply want to shift the win.flip() into the inner-most portion of the loop, so that the progress bar will appear on screen with all of the other stimuli.


#3

Hi Michael,

Thank you for your response. The way I present the experiment is that I have a .csv file where each row represents a trial with the specified imagaes/text to present. I’ve been using my for loop to iterate over the .csv file to keep track of which trial I’m currently on. I’ve been playing around with taking the for loop out completely or moving it outside the while loop, but so far it hasn’t helped with presenting the progress bar.

Dan


#4

Again, we need a description of what you want to present, it’s hard to tell from the code snippet above. i.e. currently it seems as if the entire task must be completed within two minutes and the trials are cycled through within that time period. So how do you want to present the progress bar? Is it supposed to be on screen for the entire two minutes, updating continuously as the other stimuli appear, or should it appear only between trials, to just periodically indicate how much time is remaining (this is how it is currently structured). If so, how long should it be displayed for?

i.e. to give proper help on how to structure your code, we need to know the sequence of how the stimuli (including the progress bar) should be presented, within and across trials, and how this interacts with the two minute time limit.


#5

I would like to present and update the progress bar continuously over the entire course of the run (two minutes), this way it will appear more fluid and less jittered than if I only updated every trial. I actually found a way to do this that seems to work, but I’m unsure if it’s a good way to go about it. Here’s a more comprehensive version of my code and what I’m doing:

win = visual.Window(size = (2560, 1600), fullscr = True, pos = (0,0), units = "height", color = [-1,-1,-1])
cumulativeTimer = core.Clock()
trialTimer = core.Clock()
width,height = [1.78,1]
run_length = 2 #in minutes

#Define functions
def progBar():
    prog_bar.width = rate*cumulativeTimer.getTime()
    prog_bar.pos[0] = -prog_bar_outline.size[0]/4 + prog_bar.width/2
    prog_bar.draw()
    win.flip()

def fixation(time): #Sets up fixation times. Requires a time length arguement 
    tstart = trialTimer.getTime()
    text.text = "+"
    text.pos = (0,0)
    text.setAutoDraw(True)
    while trialTimer.getTime() < time + tstart:
        if cumulativeTimer.getTime() >= run_length*60:
            break
        progBar()
    text.setAutoDraw(False)

#Stimuli for experiment:
stim_df = pd.read_csv('stim_df.csv')
text = visual.TextStim(win=win, text='', height=height/20, pos=(0,0), color="White")  
image = visual.ImageStim(win=win, pos=(0,0), size=(width/4,0.5), image=stim_df['Stim_Path'][0])
prog_bar_outline = visual.Rect(win=win, pos=(0,height-0.6), size=(width,0.1), lineColor='White')
prog_bar = visual.Rect(win=win, width=0, height=prog_bar_outline.size[1]/2, pos=(-prog_bar_outline.size[0]/4, prog_bar_outline.pos[1]), fillColor='White')
rate = (prog_bar_outline.size[0]/2)/(run_length*60)

cumulativeTimer.reset()
while cumulativeTimer.getTime() < run_length*60:
    trialTimer.reset()
    prog_bar_outline.setAutoDraw(True)
    
    fixation(1.0)
    image.image = stim_df['Stim_Path'][counter]
    
    if cumulativeTimer.getTime() >= run_length*60:
        break
        
    image.setAutoDraw(True)
    trialTimer.reset()
    while True:
        if stim_df['Col_A'][counter] == 1:
            while trialTimer.getTime() <  0.5:
                if cumulativeTimer.getTime() >= run_length*60:
                    break
                progBar()
                #Give participant brief shock for 0.5 sec
        else:
            while trialTimer.getTime() < 0.5: 
                if cumulativeTimer.getTime() >= run_length*60:
                    break
                progBar()
                #No shock. Just keep image on screen for 0.5 sec
        break
    image.setAutoDraw(False)
        
    #0.5-sec quick break period before starting next trial:
    trialTimer.reset()
    text.text = ''
    text.setAutoDraw(True)
    while trialTimer.getTime() < 0.5:
        progBar()
        if cumulativeTimer.getTime() >= run_length*60:
            break
    text.setAutoDraw(False)
    
    #Determine if run_length time limit has been reached, or if can go on to next trial:
    if cumulativeTimer.getTime() >= run_length*60:
        break
    counter += 1

#End of run
prog_bar_outline.setAutoDraw(False)
win.flip()

With this code I’m able to continuously update the progress bar, but only by repeatedly calling my function that updates the progress bar, so it may not be efficient. I’ve also noticed that the image.image = stim_df[‘Stim_Path’][counter] command sometimes takes up to 1.0-sec, which seems odd since.