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.
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.
@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).
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.
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
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.
@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.
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.
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:
record the number of wrong responses for each grid/matrix set
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 definedVPTblocks.psyexp (47.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:
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)
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.
@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.
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.
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
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