Moving cursor and selecting from a grid of images (3*3) using keyboard only

Hi,
I’m struggling with the response portion of my task. I present 9 images in a 3x3 grid and subjects must select a correct image out of the 9. I need to code this so that they can move around the grid using 2 buttons and have an additional button act as the response selection. The problem is I need to collect the position of the cursor when the response is selected. This objective works fine with event.mouse but a mouse is not, to my knowledge, possible to translate to a scanner response box (if I’m wrong, please correct me).
Any advice? I thought of using the rating scale function but I don’t think I can set it up so that one can select an option from a 3*3 grid.

https://www.imaging.psu.edu/facilities/3t-mri/fmri-joystick

if you have money… otherwise

There are several ways to do this but I have had good luck with the following logic

If you want to do this with a keyboard, you can make your 9 squares (or circles) with Visual.ShapeStim (I like to put these in a list) Imagine rows by 3 columns so any square is in position r,c
where r = 0 to 2 and c= 0 to 2 and you can drop the image in there.

``````0,0    0,1   0,2
1,0    1,1   1,2
2,0    2,1   2,2
``````

Experience: Whenever I have done this, I find it easier just to program it with no graphics first. What I mean is program it in text console that reads single strokes from keybd until spacebar. For each keystroke, you print the updated r,c to screen to make sure it does what you think; test it to make sure r and c stay in 0,1,2. It is very easy to boggle yourself and get r,c mixed up and think x,y when you are actually doing y,x because r goes up and down and c goes left and right.

Pick a random square as the starting and indicate current square with highlight (outlining or fillcolor). You know the r,c for the current square because you set it.

Now every press on the right or left arrow, increments or decrements c by one. Make sure the c is always modulo 3 to keep it in 0,1,2 If c=0 is current and the left arrow is pressed, then c= -1. Use an if c<0: to bump c back to 2. Repeat for rows. If you have your squares in a list of 9, or a list of lists, then you just use r,c from current square to un-highlight it, then as left or right arrow keystrokes come, (and therefore r,c is updated), just highlight new square which is updated r,c When the observer lands in the final desired square, another keystroke (maybe spacebar) logs the final r,c. So your ‘allowedkeys’ will be left, right, space, up, down

You can also put the image names in a list corresponding to the r,c system. One worry is if you are capturing RT because near and far squares will take different numbers of keystrokes to get to. If it is only #correct you care about, you are mostly OK

1 Like

Dear Ben,
Thank you so much for your suggestion (also for confirming that there isn’t some simple function I missed). I’m working on this now and I’m getting there Although it would be nice for the developers to maybe make this easier? I’m thinking for things like spatial navigation tasks.
Thanks so much again.
Best,
Leyla

My Affect Grid online demo might be of interest. I think it includes keyboard navigation. It certainly could.

1 Like

Dear all,
It took some time, and it can probably be prettier, but here is the code I’ve written to get this to work, if anyone needs it in the future:

``````i = 0
for stimIm in expIms:
i +=1
stim = visual.ImageStim(win, image=stimIm, pos = [0,0], size = [10, 10])
stim.draw()
win.flip()
t0.append(clock.getTime())
core.wait(0.1)

FixationText.draw()
win.flip()
t1.append(clock.getTime())
core.wait(2)
random.shuffle(im_positions)
confScale.reset()
thisTrialResp = []
for imShow, imR, sIm, pos in zip(imShowList, imRList, sImList, im_positions):
imShow.pos = pos
imR.pos = pos
imShow.draw()
imR.draw()

cursor.setPos((cursor_x, cursor_y))
cursor.draw()
win.flip()
t2.append(clock.getTime())

keyState = {'right': False, 'left': False, '2': False, '3': False}

thisResp =[]
while '2' not in thisResp and '3' not in thisResp:
move = event.waitKeys(keyList = ['right', 'left', '2', '3'],clearEvents=True, timeStamped=True)
if move:
press = move[0]
thisResp = press
thisRT = move[1]
response.append(press)
rt.append(thisRT)
for key in keyState:
keyState[key] = any(press == key for press in move)
if keyState['right']:
if cursor_x == -6 and cursor_y == 6:
cursor_x = 0
cursor_y = 6
elif cursor_x == 0 and cursor_y == 6:
cursor_x = 6
cursor_y = 6

elif cursor_x == 6 and cursor_y == 6:
cursor_x = -6
cursor_y = 0
elif cursor_x == -6 and cursor_y == 0:
cursor_x = 0
cursor_y = 0
elif cursor_x == 0 and cursor_y == 0:
cursor_x = 6
cursor_y = 0
elif cursor_x == 6 and cursor_y == 0:
cursor_x = -6
cursor_y = -6
elif cursor_x == -6 and cursor_y == -6:
cursor_x = 0
cursor_y = -6
elif cursor_x == 0 and cursor_y == -6:
cursor_x = 6
cursor_y = -6
elif cursor_x == 6 and cursor_y == -6:
cursor_x = -6
cursor_y = 6
elif keyState['left']:
if cursor_x == 6 and cursor_y == 6:
cursor_x = 0
cursor_y = cursor_y
elif cursor_x == 0 and cursor_y == 6:
cursor_x = - 6
cursor_y = cursor_y
elif cursor_x == -6 and cursor_y == 6:
cursor_x = 6
cursor_y = 0
elif cursor_x == 6 and cursor_y == 0:
cursor_x = 0
cursor_y = cursor_y
elif cursor_x == 0 and cursor_y == 0:
cursor_x = -6
cursor_y = 0
elif cursor_x == -6 and cursor_y == 0:
cursor_x = 6
cursor_y = -6
elif cursor_x == 6 and cursor_y == -6:
cursor_x = 0
cursor_y = -6
elif cursor_x == 0 and cursor_y == -6:
cursor_x = -6
cursor_y = -6
elif cursor_x == -6 and cursor_y == -6:
cursor_x = 6
cursor_y = 6

elif '2' in thisResp or '3' in thisResp:
cursor_x = cursor_x
cursor_y = cursor_y
thisTrialResp= True
t3.append(clock.getTime())
imSelectedPos = cursor_x, cursor_y
imSelectedInd = im_positions.index((imSelectedPos))
imSelectedIm = expAll.loc[imSelectedInd]
selectedS1 = imSelectedIm.split('_')[3]
if 'AFC' in selectedS1:
selectedObj = selectedS1[:-7]
selected.append(selectedObj)
correct.append(1 if selectedObj == objectID[i-1] else 0)
elif 'Decoy' in selectedS1:
selectedObj = selectedS1[:-9]
selected.append(selectedObj)
correct.append(1 if selectedObj == objectID[i-1] else 0)

for imShow, imR, sIm, pos in zip(imShowList, imRList, sImList, im_positions):
imShow.pos = pos
imR.pos = pos
imShow.draw()
imR.draw()

cursor.setPos((cursor_x, cursor_y))
cursor.draw()
win.flip()
``````

Thank you for all your help.
Leyla