Weird echo when drawing stimuli with two windows

I am having an issue with a Coder experiment that I haven’t been able to find any clear guidance on.

In my experiment, I have two windows open (on two monitors). On one monitor, the stimuli (images) are presented to the participant. On the other monitor, I have text displayed to the experimenter (what kind of stimulus is being displayed, etc).

However, in order to make both screens update on each trial, I’ve run into the following issue. I’ve attached a gif of a segment of the experiment. On the second trial in the gif, when the picture of the snow-covered trees is displayed, there is a fraction of a second where the previous image is displayed.

Oddly enough, this is both erratic and consistent. That is, it happens very rarely (maybe 1 in every 10-20 trials), but if I rerun the experiment multiple times with the same settings, the exact same trials will be affected each time.

Any thoughts? I can also upload the script if that would be helpful (though it’s awfully long).

Thanks in advance from a novice PsychoPy user!

Hi. Uploading the whole script almost certainly wouldn’t be helpful, but if you give us the selected highlights, it would help immensely. e.g. the bits where:

  • you create the windows,
  • you update the stimuli, and
  • you draw them and flip the windows (with an indication of the trial and drawing loops within which the drawing occurs).

Cheers,
Michael

1 Like

Great, thank you! I’ve tried to include the most relevant bits of the script below:

# Setup the Window
# (This is the window where the image is displayed)
win = visual.Window(size=(1024, 768), fullscr=False, screen=1, allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[-1, -1, -1], colorSpace='rgb',
    blendMode='avg', useFBO=True, name='win')

# Setup the Counter Window
win2 = visual.Window(size=(1024, 768), fullscr=False, screen=0, allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[-1, -1, -1], colorSpace='rgb',
    blendMode='avg', useFBO=True, name='win2', waitBlanking=False)

#########################################################################################

# Initialize components for Routine "RetrievalTrial_1A"
RetrievalTrial_1AClock = core.Clock()
# (This is the section that sets up the image stimulus)
retrievalScene_1A = visual.ImageStim(win=win, name='retrievalScene_1A', units='pix',
    image=None, mask=None,
    ori=0, pos=[0, 0], size=[600, 450],
    color=[1,1,1], colorSpace='rgb', opacity=1,
    flipHoriz=False, flipVert=False,
    texRes=128, interpolate=True, depth=-1.0)

# Components for Visual Participant Response Screen
# (This is the section that sets up the text on the experimenter screen)
visual_resp_text = visual.TextStim(win=win2, ori=0, name='visual_resp_text',
    text=None, font='Arial',
    pos=[0, 0], height=0.075, wrapWidth=None,
    color='white', colorSpace='rgb', opacity=1,
    depth=0.0, alignHoriz='center', alignVert='center')

#########################################################################################

# set up handler to look after randomisation of conditions etc
RetrievalBlock_1A = data.TrialHandler(nReps=1, method='sequential',
    extraInfo=expInfo, originPath=None,
    trialList=Retrieval_1A_SceneList,
    seed=None, name='RetrievalBlock_1A')
thisExp.addLoop(RetrievalBlock_1A)  # add the loop to the experiment
thisRetrievalBlock_1A = RetrievalBlock_1A.trialList[0]  # so we can initialise stimuli with some values
# abbreviate parameter names if possible (e.g. rgb=thisRetrievalBlock_1A.rgb)
if thisRetrievalBlock_1A != None:
    for paramName in thisRetrievalBlock_1A.keys():
        exec(paramName + '= thisRetrievalBlock_1A.' + paramName)

for thisRetrievalBlock_1A in RetrievalBlock_1A:
    currentLoop = RetrievalBlock_1A
    # abbreviate parameter names if possible (e.g. rgb = thisRetrievalBlock_1A.rgb)
    if thisRetrievalBlock_1A != None:
        for paramName in thisRetrievalBlock_1A.keys():
            exec(paramName + '= thisRetrievalBlock_1A.' + paramName)


    if:  # if statement cut out, simply refers to which values of "Filename" not to display
    else: 
        #------Prepare to start Routine "RetrievalTrial_1A"-------
        t = 0
        RetrievalTrial_1AClock.reset()  # clock
        frameN = -1
        # set scene
        # (This updates the stimulus image)
        retrievalScene_1A.setImage(scenes_filepath + Filename) #Filename refers to a column from an imported spreadsheet with a list of .jpg filenames
        # set visual_resp text
        if int(OldTrue) == 1:
            print "Old"
            visual_resp_text.setText(text="Old")
        else:
            print "New"
            visual_resp_text.setText(text="New")
        # update component parameters for each repeat
        retrievalTrialResponse_1A = event.BuilderKeyResponse()  # create an object of type KeyResponse
        retrievalTrialResponse_1A.status = NOT_STARTED
        # keep track of which components have finished
        RetrievalTrial_1AComponents = []
        RetrievalTrial_1AComponents.append(retrievalScene_1A)
        RetrievalTrial_1AComponents.append(retrievalTrialResponse_1A)
        RetrievalTrial_1AComponents.append(visual_resp_text)
        for thisComponent in RetrievalTrial_1AComponents:
            if hasattr(thisComponent, 'status'):
                thisComponent.status = NOT_STARTED

        #-------Start Routine "RetrievalTrial_1A"-------
        continueRoutine = True
        syncpulsetrial = 0
        while continueRoutine:
            # get current time
            t = RetrievalTrial_1AClock.getTime()
            synccheck = event.getKeys(keyList=['5'])
            frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
            # update/draw components on each frame


            # visual response window
            if t >= 0.0 and visual_resp_text.status == NOT_STARTED:
                # keep track of start time/frame for later
                visual_resp_text.tStart = t  # underestimates by a little under one frame
                visual_resp_text.frameNStart = frameN  # exact frame index
                visual_resp_text.setAutoDraw(True)

            # *retrievalScene_1A* updates
            # (This is where the stimulus image is drawn) 
            if t >= 0 and retrievalScene_1A.status == NOT_STARTED:
                # keep track of start time/frame for later
                retrievalScene_1A.tStart = t  # underestimates by a little under one frame
                retrievalScene_1A.frameNStart = frameN  # exact frame index
                retrievalScene_1A.setAutoDraw(True)
            elif retrievalScene_1A.status == STARTED and t >= (0 + (1.9-win.monitorFramePeriod*0.75)): #most of one frame period left
                retrievalScene_1A.setAutoDraw(False)

            # *retrievalTrialResponse_1A* updates
            if t >= 0 and retrievalTrialResponse_1A.status == NOT_STARTED:
                # keep track of start time/frame for later
                retrievalTrialResponse_1A.tStart = t  # underestimates by a little under one frame
                retrievalTrialResponse_1A.frameNStart = frameN  # exact frame index
                retrievalTrialResponse_1A.status = STARTED
                # keyboard checking is just starting
                retrievalTrialResponse_1A.clock.reset()  # now t=0
                event.clearEvents(eventType='keyboard')
            if retrievalTrialResponse_1A.status == STARTED:
                theseKeys = event.getKeys(keyList=['1', '2', '3', '4', '6', '7', '8', '9'])

                # check for quit:
                if "escape" in theseKeys:
                    endExpNow = True
                if len(theseKeys) > 0:  # at least one key was pressed
                    retrievalTrialResponse_1A.keys = theseKeys[-1]  # just the last key pressed
                    retrievalTrialResponse_1A.rt = retrievalTrialResponse_1A.clock.getTime()
                if len(synccheck) > 0:
                    syncpulsetrial += 1
                    syncpulsecount += 1
                    print syncpulsecount
                    retrievalFixationPoint_Response.keys = synccheck[-1]  # just the last key pressed
                    retrievalFixationPoint_Response.rt = retrievalFixationPoint_Response.clock.getTime()
                    if syncpulsetrial > 1:
                        # sync pulse ends the routine
                        continueRoutine = False

            # check if all components have finished
            if not continueRoutine:  # a component has requested a forced-end of Routine
                routineTimer.reset()  # if we abort early the non-slip timer needs reset
                break
            continueRoutine = False  # will revert to True if at least one component still running
            for thisComponent in RetrievalTrial_1AComponents:
                if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
                    continueRoutine = True
                    break  # at least one component has not yet finished

            # check for quit (the Esc key)
            if endExpNow or event.getKeys(keyList=["escape"]):
                core.quit()

            # refresh the screen
            if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
                win.flip()
                win2.flip()

You should try setting useFBO for each window to False. See the thread here: https://groups.google.com/forum/#!topic/psychopy-users/vCsQHBGyC50

Also, you get better performance if the subject screen is set to fullscr=True. There are issues with which monitor can be set to full screen on OS X but I don’t think you mentioned your operating system.

I agree that’s worth a shot, but I’m not honestly sure why it would lead to that issue!

Thanks for the suggestion! I set useFBO to False for both windows, and it doesn’t seem to have affected the issue.

Also, thanks for the reminder on my operating system. I completely forgot to include that. I’m using OSX (El Capitan) and Psychopy version v1.82.01 (I’ve tried the exact same code on v1.83.01 with no change). For that reason, I’m not able to set the subject screen (which is on the secondary monitor) to fullscreen.