psychopy.org | Reference | Downloads | Github

Clicking on Image Causes Response

Hi all,

I’m trying to bring psychopy into our pigeon lab and am having some trouble with what I think should be a fairly easy task. Essentially, I need to make a small button in the middle of the screen that when clicked on (we use touch screens and pecks=clicks) a response is made (external food delivery). Pretending the response is just the image of smiley face appearing on the screen, how could I create a program that for 30 s every time the stimulus (I use a circle from the polygon stimuli) is clicked on a smiley face briefly flashes on screen?

so far this is what my code looks like. I know the answer lies somewhere in the if mouse.getspressedin function I’m just not sure where to insert it in my code.

trialClock = core.Clock()
polygon = visual.Rect(
    win=win, name='polygon',
    width=(1.5, 1.5)[0], height=(1.5, 1.5)[1],
    ori=0, pos=(0, 0),
    lineWidth=1, lineColor=1.0, lineColorSpace='rgb',
    fillColor=1.0, fillColorSpace='rgb',
    opacity=1, depth=0.0, interpolate=True)
targetstimulus = visual.Polygon(
    win=win, name='targetstimulus',units='deg', 
    edges=487, size=(0.4, 0.4),
    ori=0, pos=(0, -.70),
    lineWidth=1, lineColor=u'white', lineColorSpace='rgb',
    fillColor=u'white', fillColorSpace='rgb',
    opacity=1, depth=-1.0, interpolate=True)
FOODRESPONSE = visual.TextStim(win=win, name='FOODRESPONSE',
    text=u'YAY FOOD',
    font=u'Arial',
    pos=(0, 0), height=0.1, wrapWidth=None, ori=0, 
    color=u'white', colorSpace='rgb', opacity=1,
    depth=-2.0);
mouse1 = event.Mouse(win=win)
x, y = [None, None]

# Create some handy timers
globalClock = core.Clock()  # to track the time since experiment started
routineTimer = core.CountdownTimer()  # to track time remaining of each (non-slip) routine 

# ------Prepare to start Routine "trial"-------
t = 0
trialClock.reset()  # clock
frameN = -1
continueRoutine = True
routineTimer.add(10.000000)
# update component parameters for each repeat
polygon.setFillColor(u'red')
polygon.setLineColor(u'red')
# setup some python lists for storing info about the mouse1
# keep track of which components have finished
trialComponents = [polygon, targetstimulus, FOODRESPONSE, mouse1]
for thisComponent in trialComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

# -------Start Routine "trial"-------
while continueRoutine and routineTimer.getTime() > 0:
    # get current time
    t = trialClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *polygon* updates
    if t >= 0.0 and polygon.status == NOT_STARTED:
        # keep track of start time/frame for later
        polygon.tStart = t
        polygon.frameNStart = frameN  # exact frame index
        polygon.setAutoDraw(True)
    frameRemains = 0.0 + 9- win.monitorFramePeriod * 0.75  # most of one frame period left
    if polygon.status == STARTED and t >= frameRemains:
        polygon.setAutoDraw(False)
    
    # *targetstimulus* updates
    if t >= 0.0 and targetstimulus.status == NOT_STARTED:
        # keep track of start time/frame for later
        targetstimulus.tStart = t
        targetstimulus.frameNStart = frameN  # exact frame index
        targetstimulus.setAutoDraw(True)
    frameRemains = 0.0 + 9- win.monitorFramePeriod * 0.75  # most of one frame period left
    if targetstimulus.status == STARTED and t >= frameRemains:
        targetstimulus.setAutoDraw(False)
    
    # *FOODRESPONSE* updates
    if t >= 0.0 and FOODRESPONSE == NOT_STARTED:
        # keep track of start time/frame for later
        FOODRESPONSE.tStart = if mouse1.isPresssedIn(targetstimulus)
        FOODRESPONSE.frameNStart = frameN  # exact frame index
        FOODRESPONSE.setAutoDraw(True)
    frameRemains = 0.0 + 2- win.monitorFramePeriod * 0.75  # most of one frame period left
    if FOODRESPONSE.status == STARTED and t >= frameRemains:
        FOODRESPONSE.setAutoDraw(False)
    
    # check if all components have finished
    if not continueRoutine:  # a component has requested a forced-end of Routine
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in trialComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished
    
    # check for quit (the Esc key)
    if endExpNow or event.getKeys(keyList=["escape"]):
        core.quit()
    
    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()

# -------Ending Routine "trial"-------
for thisComponent in trialComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
# store data for thisExp (ExperimentHandler)
x, y = mouse1.getPos()
buttons = mouse1.getPressed()
thisExp.addData('mouse1.x', x)
thisExp.addData('mouse1.y', y)
thisExp.addData('mouse1.leftButton', buttons[0])
thisExp.addData('mouse1.midButton', buttons[1])
thisExp.addData('mouse1.rightButton', buttons[2])
thisExp.nextEntry()```

Thank you Michael I have adjusted the post as suggested.

Hi Benjamin,

For something like this, you won’t need to edit the Python code that Builder generates. Instead, you’ll insert a Code Component in the Builder interface, and put code within that. Those snippets of code will then automatically get inserted in the correct places for you when the overall experiment code is generated.

What you want should should be straightforward to achieve but you need to give a more precise description. How does the trial run? i.e. is it of a long duration, so that this 30 s appearance of the smiley face could occur multiple times within a single trial? Or are the trials broken up into shorter, discrete boundaries? What determines the end of a trial?

Is this smiley face appearance for 30 s a real thing, or just an analogy for what you actually want to happen (the food delivery)? If so, why don’t we just go straight to that?

Hi Michael,

Using the coder component I have actually been able to make some progress, in that I now have a target stimulus that when clicked on elicits a 2 second food response (smiley face) though I haven’t quite figured out how to allow for unlimited presentations of that response.

Regarding your suggestion how does one insert snippets of code into the Builder interface, I have not seen that option available.

Each trial will last for 30 seconds. A large rectangular cue will appear on screen and in the middle of that rectangular cue will be a small white circle (target stimulus) that when clicked on elicits a food response. In each trial, the subject will have the ability to make multiple clicks on the target stimulus and thus receive multiple food rewards. I am using a smiley face in lieu of the external food hopper because I do not have the port information at the moment and want the program to work fully before I tackle that aspect.

Thank you very much for your help

In the component panel on the right hand side of the Builder interface, click on the disclosure triangle below the label “Custom” and then click the icon with the code on it. This opens a dialog box, with different tabs for code to be run at different times in the session (e.g. at the beginning of the experiment, at the start of the routine, on every screen refresh, and so on).

This makes it much easier for you to know that code is inserted into the correct place. It also means that you can keep tweaking things in the Builder GUI: if you start directly editing the code that Builder generates, then you need to save that file separately and need to keep working in that code-only view: you can’t go back to the graphical interface to make changes.

So go back to the Builder view. Insert a smiley face stimulus that is set to last the entire duration of your 30s trial. But set its opacity value to 0 so that it will initially be invisible.

Then insert a code component. In the begin routine tab, put this:

smiley_face_off_time = 0 # an initial value

In its Each frame tab (so that the code will run once on every screen refresh, i.e. typically at 60 times per second), put something like this:

if mouse.isPressedIn(your_target_stimulus) and t > smiley_face_off_time:
    smiley_face_off_time = t + 2.0 # update when it needs to disappear
    # make the stimulus appear:
    your_smiley_face_stimulus.opacity = 1.0 # this line is what you would 
    # substitute with your port trigger command

if t > smiley_face_off_time: # switch it off again
    your_smiley_face_stimulus.opacity = 0.0

This means that the stimulus becomes visible for 2 s each time a press is registered. Note that multiple presses within that 2 second period have no effect: this may not be what you want.

Note that a press might last longer than one screen refresh and hence a single peck could be registered multiple times. This is taken care of above, as the code won’t respond to a new press for 2 seconds. But again, this might not fit with what you need.

Make sure the code component is above your smiley face stimulus component, so that any changes it makes to the smiley face visibility can be immediately responded to. The order of components can be changed by right clicking on their icons.

Thank you for teaching me how to add code to the builder mode. I have added the above code but my FoodReward (smileyface) does not appear anymore. Is this because the opacity is set to constant? I have tried changing it to set every repeat and every frame but then the reward appears for the entire time. What exactly is the purpose of smiley_face_off_time = 0 I don’t fully understand what that tells the program?

Sorry, I think I let an & slip in that code above rather than an and. Try changing that (have edited the post above accordingly).

The opacity should be set to 0 and constant so that it doesn’t fight against the code above.

But we should then probably insert this in the End routine tab, so that the stimulus will always be set to invisible again for the start of the next trial:

your_smiley_face_stimulus.opacity = 0.0

just in case it was set to be visible within 2 s of the end of the trial.