psychopy.org | Reference | Downloads | Github

Creating a clock that starts with a loop

Hi,

So I have an experiment which records BOTH the onset of stimuli (currently relative to the trial) AND keyboard inputs (reaction time). The reaction time has been built in the builder with the presentation of stimuli a mix between inputted code and builder. The loop consists of 2 routines (fixation and trial), the first being a simple fixation target presented for 1 second using the routineTimer code. The second routine consists of a presented arithmetic sum with coinciding flashing squares.

The problem is this, the presentation of the stimuli is recorded relative to the start of the second routine Trial. The reaction time is recorded to what appears to be the start of the loop. Looking at reaction time code, it records name.clock.getTime(), however the name.clock is not defined anywhere previously. I can only assume it is encapsulated in the BuilderKeyResponse which the builder view uses to generate keyboard responses. Further I cannot get a clock to start from the start of a loop to match the reaction time clock. The difference between the two is 2 seconds, and the reaction time clock is accurate, there are definite 2 seconds between start of the loop and first stimuli presentation.

I have attempted to put any clock at the beginning of the 1st routine and it is about a second off, as it is when placed in the loop statement. The only way I have matched the reaction time clock is by using the global.Clock, for some reason this records the same start time, but I cannot reset it for each loop in the right place making it not possible to compare. When trying to alter the reaction time clocks, very strange things happen, if using the global.Clock for both stimuli and reaction time for some bizarre reason the original 2 second gaps returns. I can get them to match if I replace the default clock in the reaction time statements with ‘t’ and use a clock at the start of the loop for the stimuli.

This creates a situation where the reaction time reduces by 1 second and the stimuli increase by 1 second. I attach the code below, I’ve included a lot as the key areas are over several large sections.

# 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 "setup_and_instructions"-------
t = 0
setup_and_instructionsClock.reset()  # clock
frameN = -1
continueRoutine = True
# update component parameters for each repeat

key_resp_2 = event.BuilderKeyResponse()
# keep track of which components have finished
setup_and_instructionsComponents = [text_2, key_resp_2]
for thisComponent in setup_and_instructionsComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

# -------Start Routine "setup_and_instructions"-------
while continueRoutine:
    # get current time
    t = setup_and_instructionsClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    
    # *text_2* updates
    if t >= 0.0 and text_2.status == NOT_STARTED:
        # keep track of start time/frame for later
        text_2.tStart = t
        text_2.frameNStart = frameN  # exact frame index
        text_2.setAutoDraw(True)
    
    # *key_resp_2* updates
    if t >= 0.0 and key_resp_2.status == NOT_STARTED:
        # keep track of start time/frame for later
        key_resp_2.tStart = t
        key_resp_2.frameNStart = frameN  # exact frame index
        key_resp_2.status = STARTED
        # keyboard checking is just starting
        win.callOnFlip(key_resp_2.clock.reset)  # t=0 on next screen flip
        event.clearEvents(eventType='keyboard')
    if key_resp_2.status == STARTED:
        theseKeys = event.getKeys(keyList=['space'])
        
        # check for quit:
        if "escape" in theseKeys:
            endExpNow = True
        if len(theseKeys) > 0:  # at least one key was pressed
            key_resp_2.keys = theseKeys[-1]  # just the last key pressed
            key_resp_2.rt = key_resp_2.clock.getTime()
            # 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 setup_and_instructionsComponents:
        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 "setup_and_instructions"-------
for thisComponent in setup_and_instructionsComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)

# check responses
if key_resp_2.keys in ['', [], None]:  # No response was made
    key_resp_2.keys=None
thisExp.addData('key_resp_2.keys',key_resp_2.keys)
if key_resp_2.keys != None:  # we had a response
    thisExp.addData('key_resp_2.rt', key_resp_2.rt)
thisExp.nextEntry()
# the Routine "setup_and_instructions" was not non-slip safe, so reset the non-slip timer
routineTimer.reset()

# set up handler to look after randomisation of conditions etc
trials = data.TrialHandler(nReps=1, method='random', 
    extraInfo=expInfo, originPath=-1,
    trialList=data.importConditions('ConduciveA.xlsx'),
    seed=None, name='trials')
thisExp.addLoop(trials)  # add the loop to the experiment
thisTrial = trials.trialList[0]  # so we can initialise stimuli with some values
# abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
if thisTrial != None:
    for paramName in thisTrial.keys():
        exec(paramName + '= thisTrial.' + paramName)

for thisTrial in trials:
    currentLoop = trials
    # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
    if thisTrial != None:
        for paramName in thisTrial.keys():
            exec(paramName + '= thisTrial.' + paramName)
    
    # ------Prepare to start Routine "Fixation_2"-------
    t = 0
    Fixation_2Clock.reset()  # clock
    frameN = -1
    continueRoutine = True
    routineTimer.add(1.000000)
    # update component parameters for each repeat
    trialsrun += 1
    # keep track of which components have finished
    Fixation_2Components = [Fixation]
    for thisComponent in Fixation_2Components:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    
    # -------Start Routine "Fixation_2"-------
    while continueRoutine and routineTimer.getTime() > 0:
        # get current time
        t = Fixation_2Clock.getTime()
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *Fixation* updates
        if t >= 0.0 and Fixation.status == NOT_STARTED:
            # keep track of start time/frame for later
            Fixation.tStart = t
            Fixation.frameNStart = frameN  # exact frame index
            Fixation.setAutoDraw(True)
        frameRemains = 0.0 + 1- win.monitorFramePeriod * 0.75  # most of one frame period left
        if Fixation.status == STARTED and t >= frameRemains:
            Fixation.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 Fixation_2Components:
            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 "Fixation_2"-------
    for thisComponent in Fixation_2Components:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    
    
    # ------Prepare to start Routine "trial"-------
    t = 0
    trialClock.reset()  # clock
    frameN = -1
    continueRoutine = True
    # update component parameters for each repeat
    stimulus = [UL, UM, UR, BL, BM, BR]
    continuerout = 1
    framenum = 0
    
    
    
    
    Arithmetic.setText(Conducive)
    solved_arithmetic = event.BuilderKeyResponse()
    N_back_detect = event.BuilderKeyResponse()
    arith_ans = event.BuilderKeyResponse()
    experimenter_ends = event.BuilderKeyResponse()
    # keep track of which components have finished
    trialComponents = [UL, UM, UR, BL, BM, BR, Arithmetic, solved_arithmetic, N_back_detect, arith_ans, experimenter_ends]
    for thisComponent in trialComponents:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    
    # -------Start Routine "trial"-------
    while continueRoutine:
        # 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
        
        # *UL* updates
        if t >= 0.0 and UL.status == NOT_STARTED:
            # keep track of start time/frame for later
            UL.tStart = t
            UL.frameNStart = frameN  # exact frame index
            UL.setAutoDraw(True)
        
        # *UM* updates
        if t >= 0.0 and UM.status == NOT_STARTED:
            # keep track of start time/frame for later
            UM.tStart = t
            UM.frameNStart = frameN  # exact frame index
            UM.setAutoDraw(True)
        
        # *UR* updates
        if t >= 0.0 and UR.status == NOT_STARTED:
            # keep track of start time/frame for later
            UR.tStart = t
            UR.frameNStart = frameN  # exact frame index
            UR.setAutoDraw(True)
        
        # *BL* updates
        if t >= 0.0 and BL.status == NOT_STARTED:
            # keep track of start time/frame for later
            BL.tStart = t
            BL.frameNStart = frameN  # exact frame index
            BL.setAutoDraw(True)
        
        # *BM* updates
        if t >= 0.0 and BM.status == NOT_STARTED:
            # keep track of start time/frame for later
            BM.tStart = t
            BM.frameNStart = frameN  # exact frame index
            BM.setAutoDraw(True)
        
        # *BR* updates
        if t >= 0.0 and BR.status == NOT_STARTED:
            # keep track of start time/frame for later
            BR.tStart = t
            BR.frameNStart = frameN  # exact frame index
            BR.setAutoDraw(True)
        if frameN == 0:
            flasher = 0
            ExLog = []     #create blank list to record ordered stimulus / time pairs for presentation changes 
                            #when forcing 1-back or 2-back contingencies (e.g. after 6 stimuli). Have to record time or else contingency statement won't work.
            StimLog = [] # create a blank list of flasher-name/time pairs for sending to excel file
        
        if frameN > 0 and frameN < 10 and frameN %2 > 0: #getting all of the odd frames between 1 and 9
            flasher = random.choice(stimulus)
            flasher.setColor([1.0, -1.0, -1.0], colorSpace='rgb')
            #when flasher gets assigned a random stimulus, append list (with the chosen stimulus as the first element and corresponding time as the second element) to the Experiment log list. 
            ExLog.append([flasher,t])
            StimLog.append([flasher.name,t]) # flahser.name = 'UL, BR etc'. Flasher.name can be sent to excel file, flasher cannot.
            time.sleep(1)
        
        if frameN > 0 and frameN < 11 and frameN %2 == 0: #get all even framesbetween 1 and 10. Doesn't record anything about forcing flasher to go white
            flasher.setColor([1.0, 1.0, 1.0], colorSpace='rgb')
            time.sleep(1)
        
        #Code to identify whether 6th trial needs to be forced or not
        if frameN == 10:
            fcheck = False # variable to say whether 1 or 2 back event has happened
            for i in range(len(ExLog)-1): #length of Exlog is 4 (python starts at zero). There are 5 items in ExLog, the 5th item 
                                            #cannot be evaluated because 6th hasn't been defined. So for the 4 items do the check.
                if ExLog[i][0] == ExLog[i+1][0]:#for each flasher.name, ignoring time, is the same as the one that follows, fcheck is true
                    fcheck = True
        
        if frameN == 11: #contingency for the 6th stimulus
            if fcheck == True:# if detection event has happened, continue at random as normal
                flasher = random.choice(stimulus)
                flasher.setColor([1.0, -1.0, -1.0], colorSpace='rgb')
                #when flasher gets assigned a random stimulus, append list (with the chosen stimulus as the first element and corresponding time as the second element) to the Experiment log list. 
                ExLog.append([flasher,t])
                StimLog.append([flasher.name,t]) # flahser.name = 'UL, BR etc'. Flasher.name can be sent to excel file, flasher cannot.
                time.sleep(1)        
            else:
                flasher = ExLog[4][0] #if detection event hasn't happened, take the 5th stimulus presented and flash it again
                flasher.setColor([1.0, -1.0, -1.0], colorSpace='rgb')
                #when flasher gets assigned a random stimulus, append list (with the chosen stimulus as the first element and corresponding time as the second element) to the Experiment log list. 
                ExLog.append([flasher,t])
                StimLog.append([flasher.name,t]) # flahser.name = 'UL, BR etc'. Flasher.name can be sent to excel file, flasher cannot.
                time.sleep(1)        
        
        # After the fcheck period, continue the routine as normal
        
        if frameN > 11 and frameN %2 == 0: #get all even framesbetween 1 and 10. Doesn't record anything about forcing flasher to go white
            flasher.setColor([1.0, 1.0, 1.0], colorSpace='rgb')
            time.sleep(1)
        
        if frameN > 11 and frameN %2 > 0: #getting all of the odd frames between 1 and 9
            flasher = random.choice(stimulus)
            flasher.setColor([1.0, -1.0, -1.0], colorSpace='rgb')
            #when flasher gets assigned a random stimulus, append list (with the chosen stimulus as the first element and corresponding time as the second element) to the Experiment log list. 
            ExLog.append([flasher,t])
            StimLog.append([flasher.name,t]) # flahser.name = 'UL, BR etc'. Flasher.name can be sent to excel file, flasher cannot.
            time.sleep(1)
            
        
        # *Arithmetic* updates
        if frameN >= 3 and Arithmetic.status == NOT_STARTED:
            # keep track of start time/frame for later
            Arithmetic.tStart = t
            Arithmetic.frameNStart = frameN  # exact frame index
            Arithmetic.setAutoDraw(True)
        
        # *solved_arithmetic* updates
        if t >= 0.0 and solved_arithmetic.status == NOT_STARTED:
            # keep track of start time/frame for later
            solved_arithmetic.tStart = t
            solved_arithmetic.frameNStart = frameN  # exact frame index
            solved_arithmetic.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(solved_arithmetic.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if solved_arithmetic.status == STARTED:
            theseKeys = event.getKeys(keyList=['space'])
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                solved_arithmetic.keys = theseKeys[-1]  # just the last key pressed
                solved_arithmetic.rt = solved_arithmetic.clock.getTime()
        
        # *N_back_detect* updates
        if t >= 0.0 and N_back_detect.status == NOT_STARTED:
            # keep track of start time/frame for later
            N_back_detect.tStart = t
            N_back_detect.frameNStart = frameN  # exact frame index
            N_back_detect.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(N_back_detect.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if N_back_detect.status == STARTED:
            theseKeys = event.getKeys(keyList=['backspace'])
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                N_back_detect.keys.extend(theseKeys)  # storing all keys
                N_back_detect.rt.append(N_back_detect.clock.getTime())
        
        # *arith_ans* updates
        if t >= 0.0 and arith_ans.status == NOT_STARTED:
            # keep track of start time/frame for later
            arith_ans.tStart = t
            arith_ans.frameNStart = frameN  # exact frame index
            arith_ans.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(arith_ans.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if arith_ans.status == STARTED:
            theseKeys = event.getKeys(keyList=['num_1', 'num_2', 'num_3', 'num_4', 'num_5', 'num_6', 'num_7', 'num_8', 'num_9', 'num_0'])
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                arith_ans.keys.extend(theseKeys)  # storing all keys
                arith_ans.rt.append(arith_ans.clock.getTime())
        
        # *experimenter_ends* updates
        if t >= 0.0 and experimenter_ends.status == NOT_STARTED:
            # keep track of start time/frame for later
            experimenter_ends.tStart = t
            experimenter_ends.frameNStart = frameN  # exact frame index
            experimenter_ends.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(experimenter_ends.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if experimenter_ends.status == STARTED:
            theseKeys = event.getKeys(keyList=['enter', 'return'])
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                experimenter_ends.keys = theseKeys[-1]  # just the last key pressed
                experimenter_ends.rt = experimenter_ends.clock.getTime()
                # 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 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()

Hi Richard,

I suspect this post won’t get a useful response any time soon. Can you narrow things down a bit for us by giving a more detailed explanation of your problem? You clearly know what the problem is but it isn’t so easy for us to get to grips with.

e.g.

  • there is a lot of code to wade through here, so explain exactly what variable(s) correspond to “the reaction time”.
  • what do you mean by “I cannot get a clock to start from the start of a loop to match the reaction time clock”

etc.

Hi Micheal,

Thanks for the reply. Lets be more specific.

The experiment is a loop containing 2 routines, a fixation, a one second cross is shown first this is to focus attention. Followed by the ‘Trial’. This shows an arithmetic problem 23+34-23 etc, and at the same time flashes one of 6 squares randomly in red. The aim is to answer the arithmetic question whilst also recognising a certain pattern within the squares. That is recorded by pressing the space button to answer the sum and the backspace button when the pattern is noticed.

The presentation of the flashing squares is also recorded so it can be seen whether the participant is correctly recording the flashing as is the presentation of the arithmetic problem to calculate reaction time till answering.

The problem is the clocks are not recording the same time. The keyboard responses default to this clock


solved_arithmetic.rt = solved_arithmetic.clock.getTime()


Where solved_arithmetic.clock is NOT defined anywhere, I can only assume that it is contained within the code,

solved_arithmetic = event.BuilderKeyResponse()

Now it records the correct amount of time from the start of the loop till the start of key presses. However the flashing squares and the arithmetic by default record time to the start of the ‘Trial’

StimLog.append([flasher.name,t])

Where t is defined earlier by

t = 0
trialClock.reset()  # clock
t = trialClock.getTime()

This makes sense however when I try and change how this time is recorded to be based on the start of the loop, using the fixation clock.

# ------Prepare to start Routine "Fixation_2"-------
    t = 0
    Fixation_2Clock.reset()  # clock
    frameN = -1
    continueRoutine = True
    routineTimer.add(1.000000)
    # update component parameters for each repeat
    trialsrun += 1
    # keep track of which components have finished
    Fixation_2Components = [Fixation]
    for thisComponent in Fixation_2Components:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED

So changing the t in the previous code with;

StimLog.append([flasher.name,Fixation_2Clock.getTime()])

Instead of recording the same time as the stimuli it records exactly 1 second less, so instead of 3 it records 4. Now if i add the same code line into the arithmetic presentation code going from;

if frameN >= 3 and Arithmetic.status == NOT_STARTED:
            # keep track of start time/frame for later
            Arithmetic.tStart = t

To

if frameN >= 3 and Arithmetic.status == NOT_STARTED:
            # keep track of start time/frame for later
            Arithmetic.tStart = Fixation_2Clock.getTime()

It now equals the right time as recorded by the keyboard presses.
To add the preamble to the flashing square stimuli is as follows;

if t >= 0.0 and BM.status == NOT_STARTED:
            # keep track of start time/frame for later
            BM.tStart = t
            BM.frameNStart = frameN  # exact frame index
            BM.setAutoDraw(True)

BM being bottom middle.

So the question is why is the stimuli recording this one second gap and why does one stimuli record accurately when changing to the fixation clock and the other one second off?

Anyone got any ideas on this?

No, as Mike says, I’m afraid it’s too much for me to wade through a post this long to try and understand the issue.

Hi Jon,

Thanks for the reply. It basically boils down to this.

How can you get response type variables (keyboard inputs etc), to have the same clock to other trial components when replacing the code to an identical clock of other components does NOT work.

The code just illustrates what those components are.