Using a single component to present in multiple locations

I’m using Win10, PsychoPy Standalone version: 2020.1

I’m trying to plot a number of circles (16) with an angle of 22.5 degrees and a radius of 8cm around the 0,0 location on the screen.
So far, I calculated the x and y coordinates for each point/circle I want to plot. Now I’d prefer not to create 16 single components in the builder for each of my routine (the circles are supposed to be in 80% of the routines) but to work with an excel sheet that I just read in. I tried to load the excel sheet in one of my loops but if I set the component’s coordinates on “constant”, I get an error. If I put it on “repeat” it always shows just a single circle (which makes sense of course).
Is there some other, efficient way to do this? I looked for other places where I could load the excel sheet with the coordinates but I couldn’t find anything. I also couldn’t find any info in the forum or in the general description as to whether this is possible or not.

Any help/suggestions/advice would be greatly appreciated :hugs:

You need one loop to append the circles to a list.

Then in your trials loop you can select which of your circles to autodraw.

However, this will need code components.

Thanks for your input!

Wouldn’t this also always draw one circle at a time? I want all the circles on the screen during most of the routines (there are two routines where they shouldn’t be visible).

In case I misunderstood, do you mean that I’d need an additional loop just for the circles (I already have loops that define the target location in the different trials)? So something like an overarching loop that goes over all my routines?

Here’s some code snippets from one of my experiments.

Defining some shapes:

shapes = []
#square
shapes.append([[.707,-.707],[-.707,-.707],[-.707,.707],[.707,.707]])
#triangle
shapes.append([[.866,-.5],[-.866,-.5],[0,1]])
#triangle2
shapes.append([[.866,.5],[-.866,.5],[0,-1]])
#diamond
shapes.append([[1,0],[0,-1],[-1,0],[0,1]])
#pentagon
shapes.append([[0,1],[.951,.309],[.588,-.809],[-.588,-.809],[-.951,.309]])
#hexagon
shapes.append([[1,0],[.5,-.866],[-.5,-.866],[-1,0],[-.5,.866],[.5,.866]])
#octagon
shapes.append([[1,0],[.707,-.707],[0,-1],[-.707,-.707],[-1,0],[-.707,.707],[0,1],[.707,.707]])
#heptagon
shapes.append([[0,-1],[-.782,-.623],[-.975,.223],[-.434,.901],[.434,.901],[.975,.223],[.782,-.623]])
#cross
shapes.append([[1,.5],[1,-.5],[.5,-.5],[.5,-1],[-.5,-1],[-.5,-.5],[-1,-.5],[-1,.5],[-.5,.5],[-.5,1],[.5,1],[.5,.5]])
#parallelogram
shapes.append([[1,.707],[.5,-.707],[-1,-.707],[-.5,.707]])
#four pointed star
shapes.append([[1,0],[.5,-.5],[0,-1],[-.5,-.5],[-1,0],[-.5,.5],[0,1],[.5,.5]])
#trapezium
shapes.append([[.5,.707],[1,-.707],[-1,-.707],[-.5,.707]])

Make an array of polygons

pic=[]
    shuffle(shapes)
    for Jdx in range(conditions[Idx][0]):
        pic.append(visual.ShapeStim(
        win=win,
        fillColor=colours[0][1],
        lineColor=colours[0][1],
        vertices=shapes[Jdx],
        pos = locations[Jdx],
        size=scale
        ))

Each Frame

for Jdx in range(0, conditions[Idx][1]+conditions[Idx][0]):
    if t > .8:
        pic[Jdx].setAutoDraw(False)
    elif t > .6:
        if conditions[Idx][2] == 1:
                pic[0].vertices=shapes[-1]
        pic[Jdx].setAutoDraw(True)
    elif t > .2:
        pic[Jdx].setAutoDraw(False)
    else:
        pic[Jdx].setAutoDraw(True)

Colours have been defined in a both code component as per my crib sheet.

red = new util.Color([0, (- 1), (- 1)]);
blue = new util.Color([(- 1), (- 1), 0]);
white = new util.Color([1, 1, 1]);

then

colours = [['red',red], ['blue',blue]]

I’m not sure I completely understand your code and what it is doing (I’m sorry my knowledge of python is still quite limited). But if I get it right, I will need to create the 16 circles in a code component in my builder, right? Meaning I can’t use a single component to make one circle and assign multiple locations to it…
So it also doesn’t matter where I put the locations (they don’t have to be in an excel sheet), I can just assign them at the beginning of a routine or at the beginning of the experiment and use them when I create the circles.

Sorry for the long rant, I’m just trying to see if I understand the logic behind what I need to do…

You need as many visual objects as the maximum number you need on the screen at one time. Once created (either in code or as components) you can edit their properties (such as size, colour and location) as often as you like. I prefer to create the objects in code because I can then add them to an array which makes it easier to make edits to them all in one go, or particular ones.

The initial creation of the list/array of objects could read information from an Excel sheet if you used a loop prior to your trials loop, like I do for preloading slimuli in Pavlovia

Okay, got it. So far I didn’t need as many objects at a time so I just used the builder components but it doesn’t make sense to do this with all these circles.

So I wrote this piece of code now (where the x and y position come from an excel sheet that is loaded during the loop that goes around the routines). Something is wrong in the code (I guess it’s my coding), but is this the right way to go?

for Idx in range(0, 17):
    landmarks[Idx] = visual.Polygon(
        win = win, name = 'landmarks' + str(Idx),
        edges = 50, size = (0.5,0.5),
        ori = 0, pos(landmark_x(Idx),landmark_y(Idx)),
        lineWidth=1, lineColor = [0.5,0.5,0.5], lineColorSpace = 'rgb',
        fillColor = [0.5,0.5,0.5], fillColorSpace = 'rgb',
        opacity = 1, depth = -5.0, interpolate = True)
    landmarks[Idx].setAutoDraw(True)

Hello Jana,

I am not quite sure but I suspect that the above mentioned line is incorrect. Shouldn’t this read

pos = [landmark_x(Idx),landmark_y(Idx)],

Best wishes Jens

Hi Jens,
I think you’re right and it needs to be brackets instead of parens. But there seems to be another mistake higher up… I get an error once I start defining the landmarks variable. So

landmarks[Idx]

is fine, but once I write

landmarks[Idx] = visual.Polygon( ...

it shows an error. This is how shapes are defined though so I’m not sure why it’s not working…

Hello Jana,

what shows an error and what is the error?

Best wishes Jens

landmarks[Idx] = visual.Polygon(

This won’t work unless landmarks already has a length of 18, which is why I use append.

    ori = 0, pos(landmark_x(Idx),landmark_y(Idx)),

As I think you’ve noticed, this needs to be landmark_x[Idx] because it’s a list not a function.

fillColor = [0.5,0.5,0.5]

This probably won’t autotranslate correctly, which is why I define colours in a both code component.

I use the Auto->JS code type in the code component, so I get an error in the Java script code letting me know that something’s wrong with my python code…

Ahh okay, thanks! I saw that you use append, I just wasn’t sure if that’s necessary or just a nicer way of doing it… So could I use append in a loop? Like

landmarks.append[Idx] = visual.Polygon( ...

Is that a way to do it? It should be possible in a loop, right? Since all my circles are the same, not like in your case where each component has a different shape…

And I missed your last remark, so you think it’s better if I define the colours before I enter this loop (or after as you do)? Maybe in a list as well? I was just thinking that all the circles need to have the same colour so it shouldn’t matter if they’re defined as a constant…

I’m really sorry if I’m asking obvious questions but I really feel a bit lost with this and I can’t figure out how to make it work…

Please do look at my crib sheet since various bits of the code won’t work unless you have the right elements in code_JS.

Please look at my code in this thread for how to use .append. It just adds the object to the end of the list.

The colours needs to be defined first. I sometimes have a colours/both code component next to my code_JS component.

Okay, I will give it a go with the append and get rid of the loop. The crib sheet looks super handy, it’ll take me a while to get through that though :smile:

Thanks a lot for all the help, I hope I’ll manage to make it work!

I’m not sure I suggested that. If you want to append 18 items to a list it would make sense to use a loop.

:sweat_smile: Sorry, I guess that was my interpretation of your previous post. It does make much more sense to you a loop, I just need to see how I can make it work…