Looking at the auto-generated code from an experiment designed in Builder, it is clear that certain chunks are repeated. For example, a single screen of static text (which waits for a button press before commencing the next block) occurs 10 times in my current experiment, with only the actual text stimulus changing, and so something very similar to the following code is repeated 10 times in the script:
# ------Prepare to start Routine "staticText"-------
t = 0
staticTextClock.reset() # clock
frameN = -1
continueRoutine = True
# update component parameters for each repeat
respStaticText = event.BuilderKeyResponse()
textStaticText.setText(u'I am the first occurrence of static text')
# keep track of which components have finished
staticTextComponents = [isiStaticText, textStaticText, respStaticText]
for thisComponent in staticTextComponents:
if hasattr(thisComponent, 'status'):
thisComponent.status = NOT_STARTED
# -------Start Routine "staticText"-------
while continueRoutine:
# get current time
t = staticTextClock.getTime()
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
# update/draw components on each frame
# *isiStaticText* updates
if t >= 0.0 and isiStaticText.status == NOT_STARTED:
# keep track of start time/frame for later
isiStaticText.tStart = t
isiStaticText.frameNStart = frameN # exact frame index
isiStaticText.setAutoDraw(True)
frameRemains = 0.0 + 0.8- win.monitorFramePeriod * 0.75 # most of one frame period left
if isiStaticText.status == STARTED and t >= frameRemains:
isiStaticText.setAutoDraw(False)
# *textStaticText* updates
if t >= 0.8 and textStaticText.status == NOT_STARTED:
# keep track of start time/frame for later
textStaticText.tStart = t
textStaticText.frameNStart = frameN # exact frame index
textStaticText.setAutoDraw(True)
# *respStaticText* updates
if t >= 1.8 and respStaticText.status == NOT_STARTED:
# keep track of start time/frame for later
respStaticText.tStart = t
respStaticText.frameNStart = frameN # exact frame index
respStaticText.status = STARTED
# keyboard checking is just starting
event.clearEvents(eventType='keyboard')
if respStaticText.status == STARTED:
theseKeys = event.getKeys()
# check for quit:
if "escape" in theseKeys:
endExpNow = True
if len(theseKeys) > 0: # at least one key was pressed
# a response ends the routine
continueRoutine = 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 staticTextComponents:
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 "staticText"-------
for thisComponent in staticTextComponents:
if hasattr(thisComponent, "setAutoDraw"):
thisComponent.setAutoDraw(False)
# the Routine "staticText" was not non-slip safe, so reset the non-slip timer
routineTimer.reset()
Obviously it would be nice to only repeat this once. My first thought was to put it all into a separate .py script, with the only changes being defining it as a function and passing the actual text to this function as an option, like so:
def staticTextFunc(theText):
# ------Prepare to start Routine "staticText"-------
t = 0
staticTextClock.reset() # clock
frameN = -1
continueRoutine = True
# update component parameters for each repeat
respStaticText = event.BuilderKeyResponse()
textStaticText.setText(theText)
# keep track of which components have finished
staticTextComponents = [isiStaticText, textStaticText, respStaticText]
for thisComponent in staticTextComponents:
if hasattr(thisComponent, 'status'):
thisComponent.status = NOT_STARTED # (rest of routine follows)
which I saved in the same folder as the main script, calling it staticTextFunc.py
. But then, when I call the following in place of the usual routine code in the main script:
import staticTextFunc
staticTextFunc.staticTextFunc(u'Hi')
It gives the error global name staticTextClock is not defined
. Note that staticTextClock = core.Clock()
occurs earlier in the main script, as usual in the Builder-generated component initialization part. Moving the component initialization to the staticTextFunc.py
script does not solve the problem, it just starts a cascade of errors (name 'core' is not defined
; name 'win' is not defined
) whose solutions would seem to involve duplicating the entire original script inside the helper script!
I am probably missing something basic; I read that global variables in a script are not available to functions called within separate scripts in Python, but I don’t really understand how this works (coming from an intermediate R background and no coding otherwise). I might add that there is no problem running the experiment from Spyder before these changes are made. I am new to the coder, so please forgive my ignorance!