How to present all the squares at the same time using the custom code?

OS (e.g. Win10): Window 10
PsychoPy version (e.g. 1.84.x): 2020.2.10

What are you trying to achieve?:
I’m trying to create a visual pattern test (VPT) using the Builder view. In VPT, the participant will see a 3x3 or 4x4 grid, with some of the squares randomly flashed for 600ms. Following the presentation, the grid will be presented again and participant will be required to select all the squares that have flashed before they can proceed. The experiment will be terminated if they fail to select all the squares that have flashed. Details for the VPT task: VPT description

I found a Corsi experiment (using grid) on Pavlovia Corsi (4x4 grid) and I wanted to adapt it to the VPT. I wonder how to adapt the code to make the tiles presented at the same time, instead of in sequence.

If it is not possible to do with the code, I’m thinking of creating overlapping squares (18 squares for 3 x3 grid) and manipulate the squares presentation time using the Excel to create a pattern for each routine. That is one way of doing it but it seems to be easier to do it with code.

1 Like

Hi @mayling, take a look at the attached, it is adapted from the Drag and Drop demo.
grid.psyexp (10.5 KB)

Set the size of the grid and the number of rows using the variables in the code component, where the squares within are created proportionately i.e., square size / number of rows (in pixels). Set the number of black squares for each trial using the nBlack variable. These variables can be replaced by variables from a conditions file, so you could have a different number of black squares on each trial.

Thanks, @dvbridges. I will look at the demo.

Yes look at the demo, but also download and look at the exp file I attached. It has been adapted so it presents the grid with randomised black tiles.

@dvbridges What if the pattern is not in a grid? For example, I have pattern as shown in figures below. Wouldn’t creating squares be better and I can create condition file so that I can cross-check the answers with the participants’ response.

All the patterns have been pre-decided and I want to test the participants’ memory, so the patterns should be presented for a short duration of time (e.g., 1 second).

image image

How many predefined shapes are there?

The Corsi task is essentially the same, it creates a grid and presents a sequence. One solution would be to use the grid example. Make a grid the same size as your largest shape, and pass the predefined shape to the createGrid function, where the unwanted squares are deleted. Then in your test phase, you present a blank grid, where mouse clicks change the tiles to black. You can compare the test grid with the predefined grid on each mouse click, matching each tile based on color, where mismatches end the trial / task.

There are 14 predefined shapes.

If I were to use the grid example, then would it be better if the size of the grid is controlled across different screens? For example, each square would be 2x2cm. Then I can probably use the createGrid function to delete the unwanted squares.

When I get a moment I will change the grid function, so all tiles are one size regardless of the grid size, and accept a predefined shape. However if you manage to fix the function yourself before then, please share the solution :slight_smile:

1 Like

Hi @mayling, here is a working task with conditions file.

The new version draws a grid based on a shape defined in the conditions file. Define the shape based on the number of rows and columns for each shape. To determine which tiles are drawn, add a list of numbers, where 0 = no tile, 1 = white tile and 2 = black tile.

First the pattern is presented, followed by a blank response grid. The user clicks the tiles on the response grid to change their color. The code will then compare each tile color between the pattern and response grid. If tiles mismatch the trial ends with negative feedback, else the trial ends when all correct tiles have been selected, giving positive feedback.

If you want to actually quit the task on incorrect responses, use endExpNow = True in place of continueRoutine = False in the response code, when comparing tiles immediately after they are clicked.

grid.psyexp (22.3 KB)
cond.xlsx (8.2 KB)

@dvbridges, thank you so much. :slightly_smiling_face: I was working on the codes and then, I realized that you have posted the task file.

@dvbridges or anyone else who is familiar with how the timing for stimuli works.

I’m trying to present the grid (with patterns drawn) for 2ms, instead of giving unlimited time to the participant to remember the patterns.

Procedure:

  1. Present the patterns for 2s
  2. Blank screen for 1s
  3. Response by redrawing the pattern (i.e., clicking the right squares)

So, for the Each Frame, could I add myClock=core.Clock() as shown below:

myClock = core.Clock()
while myClock.getTime() < 2:
    drawGrid.draw()
    win.flip()

    # clear the window
    win.flip()

@mayling, as you have a mouse component in the pattern exposure routine, you do not need any code to control the presentation time. Assuming you deleted the text component in the routine, set the mouse duration to 2 seconds, and set the mouse to “never” end routine. This will stop the pattern grid routine after 2 seconds, which then moves on to the blank routine, which has a duration set by the blank text stimulus. You could also use an Static component for the same thing, i.e., to control presentation time instead of either the mouse or text component.

1 Like

Hi,

I have changed the codes to endExpNow = True as suggested. It works well! :slight_smile: The routine ends following ONE incorrect response.

Then, I managed to make the routine to end after TWO incorrect responses by adding new variable into the Begin Experiment tab.

Begin Experiment

total_wrong=0

Each Frame

if respMouse.isPressedIn(currentTile) and currentTile.fillColor == "white":
        currentTile.fillColor = "black"
        # Compare clicked tile
        if currentTile.fillColor != patternTile.fillColor:
            total_wrong=total_wrong+1
            continueRoutine = False

End Routine

if total_wrong >=2:
    endExpNow = True
else:
    total_wrong<2
    endExpNow = False

In the visual patterns test, there are 3 patterns for each pre-determined grid/matrix. Starting with a grid size of 4 (2 × 2), participants complete three trials at each grid size, with the grid size increasing as long as they get at least two out of the three trials correct. There are 14 grid size in total.

So, I created 14 blocks of trials with 14 xlsx. file as shown in Blocks of trials and counterbalancing — PsychoPy v2023.2.3

I tried to move the total_wrong=0 to Begin Routine hoping that it would have recorded the number of wrongs in each grid/matrix. But it didn’t work.

So, my question is how do I:

  1. record the number of wrong responses for each grid/matrix set
  2. depending on the number of wrong responses in each grid/matrix set, continueRoutine =False or endExpNow=True

Additionally, when I tried to upload the task to Pavlovia, the following error popped up.
ReferenceError: createGrid is not defined VPTblocks.psyexp (47.5 KB)

cond.xlsx (8.3 KB)
blockEight.xlsx (8.5 KB)
blockSeven.xlsx (8.5 KB)
chooseBlocks.xlsx (8.5 KB)

Hi @mayling, ah as the category for this post was set to Builder, the code was written without JS code in mind. There was a few things to debug to get this working online, so I have created a working version attached. This new version should not need any code changes, although the tile sizes differ between Python and JS versions. This works with the latest PsychoPy.

In response to your questions:

  1. For each matrix set, you can count the number of incorrect responses and end the task based on this, however you need to reset the counter outside of the trial loop (see example)
  2. I have changed how the task ends, to end more gracefully, and to also be compatible with online tasks. Ending the task now involves ending the loops, and moving to a final end routine to exit the experiment, with a thank you message.

The example attached uses an outer loop to control which trial list / condition file is presented in the inner loop. There is some code at the beginning of the task which sets some variables, which you should not use auto-translate on, as it will fail to translate in a useful way. Everything else is self-explanatory, but let me know if you have questions.

grid.psyexp (29.9 KB)
trialsLoop.xlsx (8.2 KB)
condLoop.xlsx (8.1 KB)

URL of working task on Pavlovia

@dvbridges Thank you for the file and sorry for the confusion. I assumed this post should be under the Builder category as I designed it in the Builder. Noteworthy, my assumption that if the experiment had worked in the Builder, then it probably would be in the Pavlovia was wrong. Now, that I have read some of the threads in the forum, I too realised that both platforms are running on two different languages. :sweat_smile:

I will experiment with the file tomorrow and see if I can do the tile resizing works with the screen calibration using the credit card.

Now, I understand. Earlier, I was thinking of using the adaptive staircase method but it didn’t work out as I have a pre-determined matrix sets.

Noted with thanks.

Happy New Year and enjoy the long weekend. Hope that 2021 will be a better year. :hugs:

1 Like

This, we need to change the configuration and fix the height and weight. According to this link, the default size is 0.5.

Hi,

Adding on the VPT task, I’m trying to allow the participant to ‘unclick’ the squares if they make a mistake using the if statement. In the current version, if all the response pattern is similar/not similar to the pattern presented, a feedback is given.

I’m thinking that in the responseGrid, participants should be able to click once (change to black) and ‘undo’ if they click wrongly. Once they are satisfied with their responses, then they can click on an image or text to continue to the next trial. However, it seems that the isPressedIn may or may not work with an imageStim.

Additionally, I want to check if the response == pattern presented and add ‘1’ for correct and ‘0’ for wrong answers. I also added the RT for time taken from the response grid is presented to continueButton/image is clicked.

Below is how I changed the code but it seems that the squares are not selected anymore. Can I know what is wrong with my code?

currentTile = 0
patternTile = 0

for tileN in range(len(respGrid)):
    # Get clicked tile and corresponding pattern tile
    currentTile = respGrid[tileN]
    patternTile = patternGrid[tileN]
    
    if respMouse.isPressedIn(currentTile) and currentTile.fillColor == white:
        respGrid[tileN].fillColor = black
    else: 
        respGrid[tileN].fillColor = white

# Compare clicked tile
    if respMouse.isPressedIn(pressContinueTxt):
        currentTile.fillColor != patternTile.fillColor
        totalWrong += 1
        thisExp.addData('totalWrong', totalWrong)
        if respMouse.isPressedIn(pressContinueTxt, buttons=[0]):
            continueRoutine = False
            thisExp.addData('RT', t)
    
if totalWrong >= 2:
    continueRoutine = False
    trials.finished = True
    conditionLoop.finished = True

correct = compareGrid(respGrid, patternGrid)
if correct:
    continueRoutine = False

Hi @mayling,

In response to the post about size, that looks fine but you will need to change the units used in the Rect being created, currently it is using pixels and so your tiles will only be 1 * 1 pixel.

To untick the squares you will have to keep track of the mouse button status. One way to do this is by using a mouseDown flag. In the “Begin Routine” tab of the response routine add

mouseDown = False

Next, change the color of the clicked tile only if mouseDown is False. Change the mouseDown flag to True when the mouse is clicked in a tile to change the color, and change it back to false when the mouse button has been released:

for tileN in range(len(respGrid)):
    # Get clicked tile and corresponding pattern tile
    currentTile = respGrid[tileN]
    patternTile = patternGrid[tileN]
    
    if respMouse.isPressedIn(currentTile) and currentTile.fillColor == white and not mouseDown:
        respGrid[tileN].fillColor = black
        mouseDown = True

    elif respMouse.isPressedIn(currentTile) and currentTile.fillColor == black and not mouseDown:
        respGrid[tileN].fillColor = white
        mouseDown = True
    
    # has mouse button been released?
    if not respMouse.getPressed()[0] == 1:
        mouseDown = False

Now, you want to end the trial on the press of a button you created. First check the button is clicked, and if so, use the compareGrid function to check whether the grids are equal, and make the increments to totalWrong if the grid is incorrect, then end the trial or loop depending on the value of totalWrong.

if respMouse.isPressedIn(button):
    # compare grids
    correct = compareGrid(respGrid, patternGrid)
    # save result
    thisExp.addData("correct", correct)
    
    if not correct:
        totalWrong += 1
        
    if totalWrong >= 2:
        continueRoutine = False
        trials.finished = True
        conditionLoop.finished = True
    
    # End the trial
    continueRoutine = False

I can post the example if you need it.

1 Like

Hi @dvbridges,

Thank you for the mouse solution. I have been trying to figure out the if-statement for a few days. Now, it works brilliantly.

When I changed it to width=1 and height=1, the grid was presented perfectly.

Before,

After,
image

Thanks for your help. :slight_smile: It was stressful to make the experiment works but on the bright side, I learnt a lot about Python.

1 Like