psychopy.org | Reference | Downloads | Github

Presenting stimuli on two screens

I am wanting to design an experiment that presents stimuli onto two screens. The experiment will involve presented one image (for example) on one screen while another image is presented on the second screen. I haven’t had any luck with designing this experiment so far.

Is this possible using PsychoPy? I have access to both a Mac or a PC so if one version is better than the other for this that won’t be a problem.

This is perfectly possible with code but hasn’t been implemented directly in the Builder interface so you’d have to use some code components to handle the rendering in the second screen in that case. But yes, it is possible

Hello Daniel,

I’ve used dual screen displays numerous times with PsychoPy and it works very well on Windows and Linux. You’ll have better luck through the coder interface or using PsychoPy as a library. You simply need to instance two window objects and set their screen number to the appropriate display as so (a snippet from an experiment I’m running):

SCREEN_SIZE = (1920,1080)                 # screen res, my screens are identical 
win0 = visual.Window(SCREEN_SIZE, screen=0, fullscr=True)
win1 = visual.Window(SCREEN_SIZE, screen=1, fullscr=True)

Then you point to the screen you want stuff to appear when you instance your stimuli. I have to flip the text stim here since I’m using mirrors.

left_text = visual.TextStim(win0, text='Left', flipHoriz=True)
left_text.setAutoDraw(True)
right_text = visual.TextStim(win1, text='Right', flipHoriz=True)
right_text.setAutoDraw(True)

Call flip twice during your experiment’s main loop:

win0.flip()
win1.flip()
2 Likes

Hey, I know its been a while since you posted this, but I’m trying now to implement 2 monitors into my experiment that show different stimuli, and I’m encountering some problems due to me not fully understanding the syntax yet (I’m quite new to Python, I do understand the basics). I’m working from Builder to Code View.
Something that I dont fully understand is, whether I have to set everything up for two screens or not, so duplicate stimuli that are supposed to show up on both screens with “win1” and “win2”?
E.g. for the introduction, with one screen it would look like this:

Introduction = visual.TextStim(win=win, name='Introduction',
    text='Hello World!',
    font='Arial',
    pos=(0, 0), height=0.055, wrapWidth=None, ori=0, 
    color='white', colorSpace='rgb', opacity=1, 
    languageStyle='LTR',
    depth=0.0); 

If I want both screens to show the same thing, can I leave it then as “win=win”? I don’t fully understand, why it doesnt just say “win”. Or do I have to get up something like “Introduction1” and “Introduction2” to show it on two screens? Same for Stimuli. There are some that I want to show on both screens, but some that should be different (e.g. same pics, but different text).

Thank you very much!

The first win is the name of a parameter that should set to the name of the window the stimulus will be shown on. The second win here is the name of a particular window, but unlike the name of the parameter, the actual name you provide here is arbitrary. It could be called be win1, or my_favourite_window or whatever you want to call it. The key thing is that a stimulus is associated just with a single window name at any time.

So you could create two stimuli, one for each window. But as shown in the docs, you can however, instead change the window for a stimulus at any time:

https://www.psychopy.org/api/visual/textstim.html#psychopy.visual.TextStim.win

e.g.

stim.win = win1  # stimulus will be drawn in win1
stim.draw()  # stimulus is now drawn to win1
stim.win = win2  # stimulus will be drawn in win2
stim.draw()  # it is now drawn in win2
win1.flip(waitBlanking=False)  # do not wait for next
    # monitor update
win2.flip()  # wait for vertical blanking.

Note that this just changes default window for stimulus.
You could also specify window-to-draw-to when drawing:

stim.draw(win1)
stim.draw(win2)

Hey Michael, thank you for your advice! I understood the problem and was able to implement it into my code so that two windows would now open and display different things as I want them to be displayed.
The only trouble I’m still working on is that I want the program to display the same two Stimuli on both monitors simultaneously. Right now, the way I coded it will display both stimuli on win1, with the other staying empty.

So here I created two windows:

SCREEN_SIZE = (1000,500)
win0 = visual.Window(SCREEN_SIZE, screen=0, fullscr=False)
win1 = visual.Window(SCREEN_SIZE, screen=1, fullscr=False)

Then, I set up the stimuli

LeftStimuli = visual.ImageStim(
    win=win0, 
    name='LeftStimuli', 
    image='sin', mask=None,
    ori=0, pos=(-0.35, 0), size=(0.5, 0.5),
    color=[1,1,1], colorSpace='rgb', opacity=1,
    flipHoriz=False, flipVert=False,
    texRes=128, interpolate=True, depth=0.0)
RightStimuli = visual.ImageStim(
    win=win0,
    name='RightStimuli', 
    image='sin', mask=None,
    ori=0, pos=(0.35, 0), size=(0.5, 0.5),
    color=[1,1,1], colorSpace='rgb', opacity=1,
    flipHoriz=False, flipVert=False,
    texRes=128, interpolate=True, depth=-2.0)

Afterwards, I try to draw them to both windows.

if LeftStimuli.status == NOT_STARTED and tThisFlip >= 0.5-frameTolerance:
            LeftStimuli.win = win0
            LeftStimuli.setAutoDraw(True)
            LeftStimuli.win = win1
            LeftStimuli.setAutoDraw(True)
if RightStimuli.status == NOT_STARTED and tThisFlip >= 0.5-frameTolerance:
            RightStimuli.win = win0
            RightStimuli.setAutoDraw(True)
            RightStimuli.win = win1
            RightStimuli.setAutoDraw(True)
if continueRoutine: 
            win0.flip()
            win1.flip()

I also tried to set win = win0 and win1 when setting the stimulus parameters, and just say RightStimuli.setAutoDraw(True) later to initialize, but that also doesn’t work. I’ve read through Draw into two windows simultaneously, but wasn’t sure what to change about my code. Duplicating the stimuli as they doesn’t seem to me the best solution.

Thanks a lot again,
Jana

Hi Jana,

A stimulus can only be associated with one window at any instant in time. In your code above, you change the window the stimulus will be drawn into to win0, then set its autodraw property to True, and then set its window to win1, and once again set its autodraw property to True. The result is that it will only be drawn once, in win1. Autodraw is just a promise to do something in the future. It will only apply when the current window the stimulus belongs to is flipped. That window can only be win1 in the code above:

if continueRoutine: 
    win0.flip() # oops, no stimuli associated with me at this point in time
    win1.flip() # only I have something to autodraw

So I personally prefer to avoid autodraw. It is a useful shortcut, but it can be much more obvious in both reading and writing your code if you draw manually. e.g.:

if LeftStimuli.status == NOT_STARTED and tThisFlip >= 0.5-frameTolerance:
    LeftStimuli.win = win0
    LeftStimuli.draw() # drawing into the window buffer happens RIGHT NOW
    LeftStimuli.win = win1
    LeftStimuli.draw() # as does this

# now the drawing has been done into the back buffer, we need
# to actually display it, by flipping to the front buffer:
if continueRoutine: 
    win0.flip() # yay, I have stimuli drawn into me!
    win1.flip() # so do I!

This way, the drawing happens explicitly, at the time each line executes. So this code would do what you want: drawing each stimulus twice, into separate windows. Delaying it to some time in the future with autodraw can lead to the sorts of issues you see, because it is left implicit when the drawing will occur. NB the code can be even more concise, by specifying the window within the draw function:

if LeftStimuli.status == NOT_STARTED and tThisFlip >= 0.5-frameTolerance:
    LeftStimuli.draw(win0)
    LeftStimuli.draw(win1)

Hey Michael,
thank you so much for taking your time to explain these for you probably quite simple concepts. I can’t believe I didn’t just check for that option myself… Anyways, it works properly and also made me realize another mistake that I’ve done, which was also quite an easy fix in the end. So thanks again. :slight_smile:

Jana :smiley:

These things are only simple in hindsight… the value of forums like this is that both the question and the answer will be of use to many other people in the future who want to learn the same thing.

1 Like