Organised Randomization of 4 photos

Hello,
I’m new to Psychopy. I know how to randomize a list of images. But at the moment I’m trying to build a task in which 4 images appear in each corner of the screen: 2 neutral, one positive and one negative.
I want the order and placement of image valence (positive, neutral & negative) to be randomized without repetition. For instance I don’t want negative images to appear in the same place every time.
I’m not sure how to do that. Any help would be appreciated.
I’m using the latest version of psychopy (v1.85.0) on windows.
Thanks in advance.

There are two options:
(1) control this behaviour using small code snippets
(2) handle it yourself in your conditions file.

e.g. The second approach is likely simplest, just have four columns for the image names:

top_left    top_right    bot_left    bot_right
neut1.jpg   neut2.jpg    pos1.jpg    neg1.jpg
pos2.jpg    neut3.jpg    neut4.jpg   neg2.jpg  
etc
etc

and use those variable names in the corresponding image stimuli.

i.e. in this approach, PsychoPy will handle randomising the order of trials, while you are handling the allocation of stimuli to positions. If this pseudo randomisation within trials (which would be constant across subjects) is not sufficient, then some code would be required (and a more detailed description of the variables).

Hi Michael
Thanks for your help. I did indeed consider the four columns approach, however the participants should select one of the pictures at each trial depending on the instructions given beforehand.
For instance:
Trial 1: top left: neutral, top right: positive, bottom left: negative, bottom right: neutral.
The instructions for this trial are let’s say select the negative image.
Trial 1: top left: neutral, top right: negative, bottom left: neutral, bottom right: positive.
The instructions for this trial are let’s say select the positive image.
And so on.
Hence, how do I specify the correct answer to Psychopy for each trial? In my previous projects I specified it in my conditions file. But I only had one colomn back then. In this case would it work if I specify:
Correct:
bot_left
bot_right

OR correct:
neg1.jpg
pos3.jpg
?

Thanks again.
And have a nice day.

Hello. It sounds like you want something like in this post:

i.e. are you wanting the participant to just click on one of the four images to indicate the answer? In this case, yes, you could use the arrangement as discussed, with four columns to specify the image names for the four quadrants, a fifth column which repeats one of the image names (the correct choice), and another column containing the question text. The you want to insert a code component in Builder and put something like this in the Each frame tab:

# cycle through the stimulus components by name:
# (i.e. replace with the actual names of your 4 image stimulus components 
# in the Builder timeline view):
for stimulus in [ image_TL, image_TR, image_BL, image_BR]:

    # check if the mouse is pressed within the current one:
    if mouse.isPressedIn(stimulus):

        # Yes, so store the reaction time in the data:
        thisExp.addData('RT', t)

        # get the filename for this stimulus (originally came from one of 
        # your four columns):
        image_chosen = stimulus.image 
        thisExp.addData('choice', image_chosen)

        # check if the stimulus' image filename matches the correct answer:
        if image_chosen == correct_choice: # use your actual fifth column name
             thisExp.addData('correct', 'True')
        else:
             thisExp.addData('correct', 'False')

        # end the trial once done here:
        continueRoutine = False

        # stop any further checking:
        break

You could also do other things here, like play different tones to give correct/incorrect feedback, highlight the clicked image, etc.

Hello again,

Thanks for you answer. I tried the code and it’s not working. I believe I’ve made a mistake somewhere that I can’t seem to be able to pinpoint.

# initialise a counter variable:

correct_responses = 0

#cycle through the stimulus components by name:
for stimulus in [TL, TR, BL]: #that’s how the images are entitled in my builder (the images not the $Top_Left etc. refering to the document)

# check if the mouse is pressed within the current one:
if mouse.isPressedIn(stimulus):

    # Yes, so store the reaction time in the data:
    thisExp.addData('RT', t)

    # get the filename for this stimulus (originally came from one of 
    # your four columns):
    image_chosen = CorrectAns  #the correct answer column in the Excel file
    thisExp.addData('choice', image_chosen)

    # check if the stimulus' image filename matches the correct answer:
    if image_chosen == CorrectAns:
         thisExp.addData('correct', 'True')
    else:
         thisExp.addData('correct', 'False')

    # end the trial once done here:
    continueRoutine = False

    # stop any further checking:
    break

Here is the error it shows when I try to run it from the script:

Running: C:\Users…

Traceback (most recent call last):
File “C:\Users…”, line 5, in
for stimulus in [TL, TR, BL]:
NameError: name ‘TL’ is not defined

When I run it from the builder the mouse has no effect. Wherever I click there’s no response whatsoever.

  1. Why are you running it from the scrip[t rather than from the Builder window?
  2. You haven’t posted the full error message which makes it harder to analyse things, but that line somehow appears to be in line 5 of your script. That early in the experiment script, it is highly unlikely that TL, TR, etc can have been defined yet. That code is supposed to be running on every frame refresh during your experiment, not at the very beginning of your experiment, before the stimuli have bene been created.

I suggest you read through the instructions above more carefully about where exactly to put this code.

Hello Micheal.
Indeed I was placing the code in the wrong place in my coder (I created a new code file for it at first). However now I face a new issue with the code. I’ve tried running it form the builder then from the coder and nothing worked, I get different error messages depending on where I try to run it from.
Whenever I try to run it from the coder, the following two errors appear:

Traceback (most recent call last):
File “C:\Program Files (x86)\PsychoPy2\lib\site-packages\psychopy-1.84.2-py2.7.egg\psychopy\app\coder\coder.py”, line 2530, in runFile
fullPath = self.currentDoc.filename
AttributeError: ‘NoneType’ object has no attribute 'filename’
Note: Dynamic code seems intended but updating is “constant”:
"$CorrAns2", in Routine RateV2 (Image_To_RateV2: Image)
"$CorrAns2", in Routine SavourerV2 (a_savourer_V2: Image)
"$CorrAns", in Routine RateV (image_to_rate: Image)
"$CorrAns2", in Routine attenteV2 (ImagePosASavourer: Image)

The lines 2527 to 2545 are the following:

def runFile(self, event):
    """Runs files by one of various methods
    """
    fullPath = self.currentDoc.filename
    filename = os.path.split(fullPath)[1]
    # does the file need saving before running?
    if self.currentDoc.UNSAVED:
        sys.stdout.flush()
        msg = _translate('Save changes to %s before running?') % filename
        dlg = dialogs.MessageDialog(self, message=msg, type='Warning')
        resp = dlg.ShowModal()
        sys.stdout.flush()
        dlg.Destroy()
        if resp == wx.ID_CANCEL:
            return -1  # return, don't run
        elif resp == wx.ID_YES:
            self.fileSave(None)  # save then run
        elif resp == wx.ID_NO:
            pass  # just run

Line 2530 is the following:
fullPath = self.currentDoc.filename

Whenever I try to run it from the builder, it loads as if it’s going to run and then an output window message appears claiming that “CorrAns” is not defined & that there’s an issue in line 241 in < module>. image=CorrAns, mask=None. NameError: name ‘CorrAns’ is not defined.

Here’s the code I found (237 to 254):

            if thisLine.find('Saved copy of actual frame') > -1:
                # show the new images, double size for easier viewing:
                newImg = [f for f in thisLine.split()
                          if f.find('_local.png') > -1]
                newFile = newImg[0]
                origFile = newFile.replace('_local.png', '.png')
                img = os.path.join(self.parent.paths['tests'], origFile)
                self.png.append(wx.Image(img, wx.BITMAP_TYPE_ANY))
                self.MoveEnd()
                self.WriteImage(self.png[-1])
                self.MoveEnd()
                self.WriteText('= ' + origFile + ';   ')
                img = os.path.join(self.parent.paths['tests'], newFile)
                self.png.append(wx.Image(img, wx.BITMAP_TYPE_ANY))
                self.MoveEnd()
                self.WriteImage(self.png[-1])
                self.MoveEnd()
                self.WriteText('= ' + newFile + '; ')

The line 241 is the following:
newFile = newImg[0]

Finally, here’s the code I used for the two parts of the task when the participant has to click on one of the pictures (each part is attributed to a different Excel file):

for stimulus in [ TL, TR, BL, BR]:

if mouse. is PressedIn(stimulus):
    thisExp.addData('RT', t)
    image_chosen = CorrAns
    thisExp.addData('choice', image_chosen)
    if image_chosen == CorrAns:
        thisExp.addData('correct', 'True')
    else:
        thisExp.addData('correct', 'False')
    continueRoutine = False
    break

for stimulus in [ TLV2, TRV2, BLV2, BRV2]:

if mouse.is PressedIn(stimulus):
    thisExp.addData('RT', t)
    image_chosen = CorrAns2
    thisExp.addData('choice', image_chosen)
    if image_chosen == CorrAns2:
        thisExp.addData('correct', 'True')
    else:
        thisExp.addData('correct', 'False')
    continueRoutine = False
    break

On a side note & maybe it could help:
In the builder I set the images to change every frame, the mouse is relative to routine & the mouse state is to be saved every frame.
Otherwise, I’m using windows 7 pro, and I’m just a user of the laptop not its admin.

Hi, by “read through the instructions above more carefully” I’m specifically referring to " insert a code component in Builder and put something like this in the Each frame tab".

i.e. you shouldn’t be directly editing the Python .py file. Instead, from the Builder interface, insert a Code Component. You get this from the Component panel on the right hand side: click on the Custom arrow and then click the code icon. This component has specific tabs to type code in. The component will automatically handle putting code in the right place so that it runs at the start of the experiment, at the start of a routine, on each frame, and so on.

We can’t really sort out any errors until we know that this is how you are inserting your custom code.

Hi!
Once I put it in the builder it worked! Hallelujah!
Thank you so much.
One last thing. Even when I select the wrong photo, the correct one is automatically shown in the next frame. Is there no way to “reject” incorrect answers? So unless the person chooses the right photo there is no moving forward in the task.

I’ve seen this post: https://discourse.psychopy.org/t/in-coder-properties-of-builder-can-i-designate-trigger-loop-for-only-correcrt-answers/1470/2
But I’m not sure we’re looking for the same thing.

With the appropriate code, you can probably do whatever you want. But what you are describing is entirely different to the situation above (i.e. there is no point in recording whether a response was correct or not if the final response will always be correct, and the reaction time is also meaningless if multiple responses can be made. You are only going to get useful responses here if you describe exactly what you want to do.

The participants have to choose between multiple photos on the screen.
They need to pick one and only one depending on the instructions given to them at the beginning of each bloc.
For instance: in bloc 1 there are 3 negative images & 1 positive & they have to select the positive image
In bloc 2: there are 2 positive images, 1 neutral & 1 negative and they have to pick the negative image

In every bloc, the participant should select only one type of image depending on its valence (positive, negative, neutral). Once the image is selected, the participant will look at it for 10 seconds then rate on a scale from 1 to 10 how much emotion it stirred in him.

Now, in the way I constructed the programm, each bloc is divided into 3 routines:
choice, observation, rating.

In the observation & rating routines I used the $CorrAns so that the correct photo shows in the middle of the screen.
What I’m trying to do is not allow the observation & rating routines if the person clicks on the wrong photo. For instance, if in Bloc1, if the person chooses the negative photo instead of a positive one, I want an error feedback to appear and ask the participant to select the correct image.

In my current protocol, where the participant clicks doesn’t matter because the protocol continues anyway and goes into the observation & rating routines. Given the fact that I need the participants focused, I want the protocol to continue when and only when they choose the right photo.
TR and respect of the instructions regarding image choice are important because I’m measuring negativity biases (among other things).

OK. So stick with the experimental routine as described but then create two additional routines to follow it: one for error feedback and the other for your ratings and so on. You can select which one will be executed on each trial by inserting a code component and in the Begin Routine tab, putting some code to not continue with the routine, depending on the decision made. You then also need to wrap all three routines in another loop (i.e. nested inside your main loop). This loop isn’t linked to a conditions file. Just give it a large number of repetitions. i.e. this just makes the trial repeat until the correct selection is made. In the begin routine tabs, put something like this:

# for the error feedback routine:
if image_chosen == CorrectAns:
    continueRoutine = False # don't give error feedback for a correct trial
# for the rating routine:
if image_chosen != CorrectAns:
    continueRoutine = False # don't do the rating
else: # will do the rating,
    your_inner_loop_name.finished = True # and end the cycle of repeating this trial

Hi,

I tried the command in several combinations but it didn’t work. Whatever image I click on, the correct one still shows, the program is just slower.
However I do believe have some difficulty understanding the instructions:
“add two additional routines to follow it” → follow all 3 routines or follow each routine?
“and so on” → so I need to use both codes for each routine?
“wrap all three routines in another loop” → my 3 routines? or each routine with the two new routines & their codes?

I inserted the code for each condition in my Block.
At first, I only used it for choice, then rating, then choice and rating but it didn’t work either…
I also tried wrapping my routines and putting the codes outside the inner loop.
I tried, wrapping all 3 routines with the additional routines I created for the codes.
I tried using the codes in one coder in the builder.

In the error routine I put the following code in begin routine tab:
if image_chosen == CorrAns:
continueRoutine = False
Then in the rating routine I used the following code:

if image_chosen != CorrAns:
continueRoutine = False

else:
avancement_choix_essai.finished = True #I changed the name for each nested loop I created

Here’s an example of 1 of the flows I modified:

I think you would benefit a great deal from the workshop!

http://www.psychopy.org/resources/workshops.html#bep

Ah I didn’t know you organized workshops. That’s a great idea and indeed I’d love to. Thank you.