You could create them in code and that way, you only need to alter a few parameters rather than the values for every stimulus individually. e.g. insert a code component (from the “custom” component panel) and in either its “begin experiment” or “begin routine” tab, put something like this:
# specify top-left corners of each row of blocks:
corners = [[-0.1, 0.05], [-0.1, 0.025], [0.05, 0.05], [0.05, 0.025],
[-0.1, -0.025], [-0.1, -0.05], [0.05, -0.025], [0.05, -0.05]]
# set the colours in the same order:
colours = ['blue', 'blue', 'red', 'red', 'green', 'green', 'white', 'white']
text_colours = ['white'] * 6 + ['black'] * 2
# number labels:
number_lists = [[1, 2, 3, 4], [5, 6, 7, 8]]
# populate the list of stimuli that will get drawn on each frame:
stimuli = []
for corner_number, corner in enumerate(corners):
if corner_number % 2 == 0: # upper row (1-4)
number_list = number_lists[0]
else:
number_list = number_lists[1] # lower row (5-8)
# create the boxes and matching labels:
for i, number in enumerate(number_list):
stimuli.append(visual.Rect(win,
size = [0.02, 0.02],
pos = [corner[0] + i * 0.03, corner[1]],
lineColor = None,
fillColor = colours[corner_number],
name = f'{colours[corner_number]}_{number}'))
stimuli.append(visual.TextStim(win,
text = str(number),
height = 0.022,
color = text_colours[corner_number],
pos = [corner[0] + i * 0.03, corner[1]]))
and then to make the stimuli appear, simply put this in the “each frame” tab:
for stimulus in stimuli:
stimulus.draw()
Adjust the numbers as required to get the layout and shape you want (these numbers are set assuming that “height” units have been applied). You might prefer to work with pixels, degrees, etc.
The code above is not very elegant and no doubt there would be more parsimonious ways to achieve it if some more thought was applied.
I just edited to add a name for each rect stimulus so you can tell which one was clicked, but have not tested that.