Resizing stimuli using keyboard input

Hi,
I am struggling to get to grips with using a code component in my experiment and could really use some more experienced input. I have become pretty familiar with the builder and defining stimuli this way. I am very much a novice in using python though and have got this far mostly by trial and error.

I have described the problem below and what I have tried so far:

OS: Windows 10.

What are you trying to achieve?:
I am creating an experiment where participants view an image for 20s and are then asked to resize a circle to complete a size judgment task.
The passive stimuli and circle are all presented as image components in the builder and I have had no problems so far getting them to display. There are two routines, one to display the passive stimuli and the other to complete the resizing task.

The problem I am having is in trying to get the circle to be resized based on keyboard input using a code component.

What did you try to make it work?:
I have defined the circle as an image component called ‘ResizeCircle’. The image is linked to the filepath for the circle picture (constant) and I have defined the size as the original size of this image in pixels (194,194) and set this to change every frame. I have not placed an end duration on this component.

To create the change in size based on input I have defined a keyboard component ‘keyR’ and unticked the option to ‘force end of routine’. Allowed keys are: ‘1’, ‘9’, ‘space’, ‘escape’.

I then created a code component (placed at the top of the component list) and entered the following code in the ‘Each Frame’ section (the notes indicate what I am hoping each line should do):

if keyR == '1':
    # press 1 to decrease circle size by 10 pix
    ResizeCircle.size = ResizeCircle.size - 10.0

elif keyR == '9':
    # inc circle size by 10 pix
    ResizeCircle.size = ResizeCircle.size + 10.0

elif keyR == 'space':
    # store the selected size in the data file and end the routine
    thisExp.addData('size_judgement', ResizeCircle.size[0])
    continueRoutine = False
    
elif keyR == 'escape':
    core.quit()
#manually allow the experiment to be ended during the routine

What specifically went wrong when you tried that?:
No error messages appear and the experiment runs until it hits the routine. The circle appears in the centre of the screen as planned, however, pressing the keys does not alter the size of the circle or progress the experiment. ESC quits the experiment as ususal.

I suspect that the issue is either in the actual code I have written or in the way, I am defining/using the keyboard component but I am not knowledgeable to know where the issue lies, Changing either of these seems to either result in no change or a syntax error. I have looked a little into adding a keyboard from scratch using code, though to be honest, I did not understand much of what I found.

A more minor consideration:
Currently, the circle always starts the routine at the same size. I would like to modify this so that the circle can start at a range of sizes and be resized in the same way. I would then record the start and end size during the routine. I am thinking that I might achieve this using a variable component or the conditions file for the loop? I am not sure if this would influence the approach I need to take for the resizing problem though.

Thank you in advance and apologies for the wall of text! :slight_smile:

Hi!
I may be wrong, but I think the cleanest way to handle input iteratively is to remove the keyboard component and use event.getKeys() in the Every Frame section of the code component:

resp = event.getKeys(['1','9','space','escape']) # get current response

if len(resp): #if there is a response, resp has a length >0 and this evaluates to true
    if resp[0] =='1' :
        # press 1 to decrease circle size by 10 pix
        ResizeCircle.size = ResizeCircle.size - 10.0

    elif resp[0] =='9' :
        # press 1 to decrease circle size by 10 pix
        ResizeCircle.size = ResizeCircle.size - 10.0
    
    elif resp[0] =='space': 
    # store the selected size in the data file and end the routine
    thisExp.addData('size_judgement', ResizeCircle.size[0])
    continueRoutine = False
    
    elif resp[0] =='escape':
        core.quit()
#manually allow the experiment to be ended during the routine

About varying the starting size each trial, you can use a variable from the conditions file or create it via code component. Then in the circle component, select “set every repeat” in the size field, and just write the name of the variable. This will change the circle size to the value of that variable at the start of each trial.

Hope that helps :slight_smile:

Hi Nahuel,

Thank you so much for taking a look and giving me some advice! I can see that it would make sense to include the keyboard input in the code component.

I have just had a go running the experiment with this code instead and now the routine seems to skip entirely? Before, the circle that I want to resize would appear but not change based on the keys pressed. Now the circle does not appear at all and the experiment goes back to the 1st routine in the loop. Again there are no syntax errors or anything like that.

This doesn’t seem like it would be a result of the code component directly, but is it perhaps a mistake in the way I have set up the routine? I have taken a couple of screenshots in case anyone can see an obvious error in the way I have set things up.

Structure of the routine:

The code component with the modified code:

Settings for the image component (i.e. the circle):

Thank you again for taking the time to give me your ideas :slight_smile:

You probably use space to finish afterimage, right? The key press may be “carrying over” to the resize routine.

Try adding the following in the Begin Routine section of the code (maybe good to also add it at the end of the Every Frame section):

event.clearEvents()

Even if you are not using any of the same keys in the previous routine I think it’s good practice to do that when using event.getKeys() in a routine. event accesses directly a “queue” of inputs that needs to be cleared to avoid things like this. Response components do this automatically (they call both event.getKeys() and event.clearKeys() when appropriate), but when manipulating events directly as we are doing here, we need to clear the queue ourselves.

Also, in “size” of ResizeCircle select “set every repeat” rather than “set every frame” (otherwise it will be resetting the size and your resizing based on user input may not work). Fields that don’t change over the experiment can be set to “constant”.

Finally, I made a mistake in my previous code where both 1 and 9 were doing the same thing (I also forgot to indent a couple of lines which you seem to have corrected already). So the good code for Every Frame would be:

resp = event.getKeys(['1','9','space','escape']) # get current response

if len(resp): #if there is a response, resp has a length >0 and this evaluates to true
    if resp[0] =='1' :
        # press 1 to decrease circle size by 10 pix
        ResizeCircle.size = ResizeCircle.size - 10.0

    elif resp[0] =='9' :
        # press 9 to increase circle size by 10 pix
        ResizeCircle.size = ResizeCircle.size + 10.0
    
    elif resp[0] =='space': 
        # store the selected size in the data file and end the routine
        thisExp.addData('size_judgement', ResizeCircle.size[0])
        continueRoutine = False
    
    elif resp[0] =='escape':
        core.quit()
        #manually allow the experiment to be ended during the routine

event.clearEvents()

Hi Nahuel,

Thank you so much for taking the time to help with this. I have used your advice and I can now use letter keys to resize the image (for some reason number keys still do not work but this is not a problem for me).

Hopefully, I can now get the last parts of the experiment sorted, setting the size of the circle each trial using the conditions file.

Thank you again you’re advice has saved me so much time and stress! :slight_smile:

Best Wishes,
Charlotte

The number pad keys have a different key code to the regular number keys. You can respond to them like this:

if resp[0] == '1'  or resp[0] == 'num_1' :

Also you are telling the image stimulus to update its image file on every screen refresh. Doing this at typically 60 times per second will cause performance issues. Since the image file path is constant, set that field to “constant” rather than “every frame”. That way it just gets set once, at the start of the experiment.

Lastly, avoid using full absolute paths to files (i.e. ones that start with c:). That means the experiment will break if you move it to another folder or computer (or run it online). Instead use relative paths, just specifying the file location relative to your .psyexp file. i.e. if the image is in the same folder, just give the file name (e.g. cat.jpg). If it is in a sub folder, it would be something like images/cat.jpg. Lastly lastly, use forward slashes rather than Windows-style backslashes, for arcane but important reasons.