psychopy.org | Reference | Downloads | Github

Randomizing loops to two monitors

OS - Win10; PsychoPy version - 3.1

I built a visual stimulus using the Builder; it consists of loops of sinusoidal gratings; these loops are being repeated certain number of time. I can send the stimulus with no problem to one monitor. I can also simultaneously run the stimulus on two different monitors. However what I would like to achieve is to run my stimulus in a pseudorandom way on two different monitors, such that if the stimulus consists of loops (of sinusoidal gratings) that are repeated ten times these repeats will be pseudorandomized on two monitors and the subjects will see some of the gratings on one and some other on the second monitor.

Sorry in advance if I could not make myself clear, this is my first post in the forum. I am happy to provide further details to make my post clearer.

Best wishes
Cigdem

Can you explain how you are currently doing that (I thought Builder was currently set up for display of stimuli on one monitor only). Or are you using some sort of hardware solution, like a cable splitter to display the same window on two physically separate monitors?

PsychoPy can work with multiple monitors in software, but it will likely mean needing to insert some custom code to overcome Builder’s default of only referring to one.

I think the strategy here would be to define a second window in code, which is set to appear on your second monitor. Then we can override the window attribute of a given stimulus so that it will get drawn in that second window, rather than the default one that Builder creates. We would need to know more about your procedure and design to advise upon how to implement that, but the basic idea is shown here:

https://www.psychopy.org/api/visual/gratingstim.html#psychopy.visual.GratingStim.win

Dear Michael
Thanks for your reply. My visual stimuli consist of sinusoidal gratings; I have two types of gratings which differ only by their orientation. The way I run simultaneously these two types of stimuli on two different monitors is:

  1. I inserted a custom code under the tab “Begin Experiment” to set up the second window and link the second window to the additional (second) monitor. So, this code goes on like this:
# Set up the window
win1 = visual.Window(
    size=[800, 600], screen=1, 
    monitor='3', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', units='height')

In this case my first window is:

# Set up the window
win = visual.Window(
    size=[800, 600], fullscr=False, screen=0, 
    winType='pyglet', allowGUI=True, allowStencil=False,
    monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', useFBO=True,  units='height')
  1. Then under the tab “Begin Routine” I insert few commands like below:
# Initialize components for Routine "novel" (here “novel “is the gratings with 135 deg orientation)
novelClock = core.Clock()
novel_grating = visual.GratingStim(
    win=win1, name='novel_grating',
    tex='sin', mask=None,
    ori=135, pos=(0, 0), size=2, sf=5, phase=2,
    color=[-1,-1,-1], colorSpace='rgb', opacity=1,blendmode='avg',
    texRes=256, interpolate=True, depth=0.0)

In this way I set up a Builder experiment for familiar (i.e. gratings with 45 deg orientation) and send these gratings to screen “0” that is linked to first monitor and with an addition of few commands I can generate novel (i.e gratings with 135 deg orientation) and send these gratings to screen “1” that is linked to the second monitor. I have a PC with four mini display ports which I can connect to four monitors using a mini disiplay to display cables.
So, I believe I can define the second window (using a code component), that is set to appear on my second monitor - I am happy to attach the whole experiment as I have been repeatedly running the experiment. But my main issue is to randomly drift these two types gratings to two different monitors. Each gratings are repeated 100 times and these 100 repetitions make one block; at the end of each block there is a static grey screen and then second block comes in. I have in total five familiar and five novel blocks of gratings and I want to randomly send these 10 blocks to two different monitors.

I am not sure if the lines below would work for drifting the stimuli to two screens (monitors):
** Create a randomized vector from 1 to 10, (like “randperm” function in MATLAB) which will create a random sequence of values between 1 and 20 (Vector a)
** Create a second vector which consists of 10X “0” and 10X “1” (Vector b)
** Then randomize the binary elements of vector “b” by using the previously randomized floating points of vector a. So I will create a vector “c” which will have elements of vector b in the random order of vector a; c=b(a). Here the binary elements of vector a represent the familiar and novel gratings.

** Then I will create a “for” loop; these would be the lines in MATLAB

 i=1:length(c)
if c(i)= 0
screen(drifting, screenA)
screen(gray,screenB)
else if c(i)=1
screen(drifting, screenB)
screen(gray, screenA)
end

I am not sure if this will help as I am currently in the process of finding out Python version of these MATLAB lines as I am not very familiar with the Python language (I am learning though!).
I would be very happy to hear your advice on my thoughts. Thanks again for your help.

Cigdem

Ahh, OK, you clearly know what you’re doing and it seems that you have solved all the difficult stuff already (of creating the secondary window and knowing how to draw to the correct one).

The randomisation is a little bit easier in Python than in MATLAB. You can construct your list of conditions like this:

c = [0, 1] * 10

Builder imports the shuffle function from the numpy.random module, so you can randomise the order of your list just like this:

shuffle(c)

On each trial, you can just pop off the next element in that list to do whatever you need to do. So I’d suggest doing something like this in the “begin routine” tab of your custom code component:

if your_loop_name.thisN == 0: # only do this on the first trial
    conditions = [0, 1] * 10
    shuffle(conditions)

current_condition = conditions.pop() # NB this shrinks the list by 1
thisExp.addData('condition', current_condition) # record this trial's value in the data file.

Then your conditional stuff either continues in the “begin routine” tab (e.g. if it is a one-off thing like setting the window for a stimulus to apply for the rest of the trial), or in the “every frame” tab (if it does things that require to be dynamically updated on every frame):

if current_condition == 0:
    # do some things, e.g.
    some_stimulus.win = win
    other_stimulus.win = win1
else:
    # do the opposite things, e.g.
    some_stimulus.win = win1
    other_stimulus.win = win

Notice that by popping the latest element from the conditions list at the beginning of each trial, we don’t need to keep track of a list index or impose some sort of loop structure to cycle through the list. You’ll realise you’ve successfully migrated away from MATLAB to a more Pythonic style of programming when you find yourself using indices a lot less.

PS: Good Python style is also to use descriptive variable names rather than concise but non-semantic labels like c. :wink:

PPS:

Thank you Michael I appreciate your help!

Best wishes, have a nice day!
Cigdem

Ok

I make sure I insert Python commands in the right way next time I post in the forum

Cigdem