Treat Code Like An Object

I’m trying to create a simple learning task with pigeons where a correct peck (touch screen mouse click) results in the delivery of food. The following code allows for this action using a motor we have attached to our computers :

if mouse.isPressedIn(TargetStimulus):
import maestro 
servo = maestro.Controller('COM4')
servo.setAccel(0,4)      #set servo 0 acceleration to 4
servo.setTarget(0,1200)  #move servo to feed
import time
time.sleep(3)
servo.setAccel(0,-4)      #set servo 0 acceleration to -4
servo.setTarget(0,6200)  #move servo to resting position
servo.close 

The problem is I need this action to occur consistently for 30 s, that is, for 30 s every time the pigeon pecks at the Target Stimulus, the food reward process begins. I’m not sure how to code for this as my understanding with python is that it will just read through the code from top to button, but clearly they’re must be a way to have this action continually running. Any thoughts on how to make this work would be greatly appreciated. I have had success using a textbook that says FOOD as my reward response but replacing the textbox with this bit of code is proving more difficult than I had anticipated.

Hi Benjamin, just to check, you’re wanting to do this within Builder, by using a code component, correct?

That is correct. I have been manipulating the code script quite a bit, but ultimately all of my solutions I come up with I would like to add as code components. To clarify as well, the code I have listed above occurs with each Frame. I get the sense that I will probably have to define something before the experiment begins, but I don’t know much about what that entails.

Definitely some things here should only happen once, so they need to be shifted to the Begin experiment tab. Note there is no problem with importing a library if you aren’t going to use it: it is far better that that happens at the beginning of the experiment rather than during a frame interval, where it might cause timing issues:

# BEGIN EXPERIMENT:
import maestro 
servo = maestro.Controller('COM4')

Note that we do not import time here, because we never want to call time.sleep() during a Builder script. Builder is fundamentally structured on a drawing loop that runs on every screen refresh, so if you asked Python to sleep for 3 seconds, that would break Builder’s experimental control and careful timing completely. i.e. everything that we do in the Each frame tab needs to be completed within one screen refresh period (typically 16.666 ms).

So your challenge then is how to do things that potentially span several seconds, when your Each frame code will typically get repeated at say, 60 times per second. This requires setting variables so that you can maintain state across frames. So you will probably need a variable that represents whether the feeder is in operation and what time it started. Let’s set that in the Begin routine tab:

# BEGIN ROUTINE:
feeder_open = False

Then you need to monitor mouse presses but take into account if the feeder is already running and for how long:

# EACH FRAME:

# open feeder if not already open:
if mouse.isPressedIn(TargetStimulus):
    if not feeder_open:
        feeder_open = True
        feeder_start = t # current time
        servo.setAccel(0,4)      #set servo 0 acceleration to 4
        servo.setTarget(0,1200)  #move servo to feed
    else:
        feeder_start = t # just guessing here that you would want to extend the 
        # open duration for new presses that arrive during the 3 s period.

# check if it needs to be closed:
if feeder_open and (t - feeder_start >= 3.0):
    servo.setAccel(0,-4)      #set servo 0 acceleration to -4
    servo.setTarget(0,6200)  #move servo to resting position
    servo.close 
    feeder_open = False

1 Like

Hi Michael,

This works beautifully, I cannot express how thankful I am.

1 Like

Thanks for posting the question. I’m hoping this thread might be useful to other people who might be considering using PsychoPy for animal behavioural studies.