How to prevent a later-than-expected keypress from messing up future trials?

I have an experiment where the basic flow is that the participants are shown a word for 2 seconds, then they see a picture and have to make a sentence using that word that also describes the picture (with the option to press the space bar to end the trial early, otherwise it is displayed for 12 seconds and that’s how long they have to say their sentence), and then they see a fixation cross at the end before the trial loop repeats. These 3 events are all separate routines (this was originally made in builder and then converted to code to add some other things that aren’t part of this loop).

This works fine and does exactly what I want it to do, although if the participant ever finishes right on the 12 second mark and actually presses the space on the fixation cross (or if they were to ever press it on the verb) the next picture skips, and then the entire experiment starts to slow down and the space key press to move to the next picture stops working from that point forward in the experiment, and the recording audio files get messed up too.

I’ve tried a few things to prevent participants from being able to press the space at any point other than right after the picture, such as turning the keyboard off for any point other than when the picture is displayed, and also clearing the keyboard events / clearing all events right before the picture routine starts, or after the fixation cross routine ends, and a few other points, but nothing seems to work and it just always skips the next picture.

I’ve been able to modify how I give the instructions to the participant when I’m with them in the lab, but it still happens every now and again, and I’d love to find a solution that still allows them to have the ability to end the microphone recording when they’ve finished their sentence, even though that will be at varying timepoints for each picture/sentence.

Since I would have thought either clearing the events or turning off the keyboard would have worked, I’m also wondering if I’m either doing those wrong in the code (the lines are included but commented out) or if the keypress isn’t the actual problem and it’s something else I’m not considering - especially since the verb and fixation cross trials aren’t set up to expect a keypress of any sort, so maybe something is happening at a point I’m not considering.

I’ve included the code for the image loop with the parts I’ve added in bold, non-bold is what the builder experiment was written to when I had it converted to python code, and also I’ve included the entire code file in case that is helpful as well.

# set up handler to look after randomisation of conditions etc
trials = data.TrialHandler(nReps=1.0, method='sequential', 
    extraInfo=expInfo, originPath=-1,
    trialList=data.importConditions(LISTNAME, selection=pseudo_2),
    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:
        exec('{} = thisTrial[paramName]'.format(paramName))

for thisTrial in trials:
    currentLoop = trials
    # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
    if thisTrial != None:
        for paramName in thisTrial:
            exec('{} = thisTrial[paramName]'.format(paramName))
    #defaultKeyboard.clearEvents()
    # --- Prepare to start Routine "give_verb" ---
    continueRoutine = True
    routineForceEnded = False
    **#defaultKeyboard.clearEvents()**
    # update component parameters for each repeat
    verb_text.setText(Verb)
    # keep track of which components have finished
    give_verbComponents = [verb_text]
    for thisComponent in give_verbComponents:
        thisComponent.tStart = None
        thisComponent.tStop = None
        thisComponent.tStartRefresh = None
        thisComponent.tStopRefresh = None
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    # reset timers
    t = 0
    _timeToFirstFrame = win.getFutureFlipTime(clock="now")
    frameN = -1
    
    # --- Run Routine "give_verb" ---
    while continueRoutine and routineTimer.getTime() < 2.0:
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *verb_text* updates
        if verb_text.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            verb_text.frameNStart = frameN  # exact frame index
            verb_text.tStart = t  # local t and not account for scr refresh
            verb_text.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(verb_text, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'verb_text.started')
            verb_text.setAutoDraw(True)
        if verb_text.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > verb_text.tStartRefresh + 2.0-frameTolerance:
                # keep track of stop time/frame for later
                verb_text.tStop = t  # not accounting for scr refresh
                verb_text.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'verb_text.stopped')
                verb_text.setAutoDraw(False)
        
        # check for quit (typically the Esc key)
        if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
            core.quit()
        
        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            routineForceEnded = True
            break
        continueRoutine = False  # will revert to True if at least one component still running
        for thisComponent in give_verbComponents:
            if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished
        
        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()
    #defaultKeyboard.clearEvents()
    # --- Ending Routine "give_verb" ---
    for thisComponent in give_verbComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
    if routineForceEnded:
        routineTimer.reset()
    else:
        routineTimer.addTime(-2.000000)
    **#defaultKeyboard.clearEvents()**
    # --- Prepare to start Routine "trial" ---
    continueRoutine = True
    routineForceEnded = False
    # update component parameters for each repeat
    # Run 'Begin Routine' code from code
    trial_ori = ori.pop() #takes the orientation, either LR or RL
        
    thisExp.addData('ORIENTATION' , trial_ori)
        
    if trial_ori == "RL":
        IMG = IMG_FILE_RL
    if trial_ori == "LR":
        IMG = IMG_FILE_LR
        
    nme = str(IMG)[:-4]+".wav" #to name the audio file based off the image name
    print(nme)
    
    stimulus.setImage(IMG)
    key_resp_end_mic.keys = []
    key_resp_end_mic.rt = []
    _key_resp_end_mic_allKeys = []
    # keep track of which components have finished
    trialComponents = [stimulus, key_resp_end_mic, mic]
    for thisComponent in trialComponents:
        thisComponent.tStart = None
        thisComponent.tStop = None
        thisComponent.tStartRefresh = None
        thisComponent.tStopRefresh = None
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    # reset timers
    t = 0
    _timeToFirstFrame = win.getFutureFlipTime(clock="now")
    frameN = -1
    #defaultKeyboard.clearEvents()
    # --- Run Routine "trial" ---
    while continueRoutine and routineTimer.getTime() < 15.5:
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *stimulus* updates
        if stimulus.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            stimulus.frameNStart = frameN  # exact frame index
            stimulus.tStart = t  # local t and not account for scr refresh
            stimulus.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(stimulus, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'stimulus.started')
            stimulus.setAutoDraw(True)
        if stimulus.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > stimulus.tStartRefresh + 15.5-frameTolerance:
                # keep track of stop time/frame for later
                stimulus.tStop = t  # not accounting for scr refresh
                stimulus.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'stimulus.stopped')
                stimulus.setAutoDraw(False)
        
        # *key_resp_end_mic* updates
        waitOnFlip = False
        if key_resp_end_mic.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            key_resp_end_mic.frameNStart = frameN  # exact frame index
            key_resp_end_mic.tStart = t  # local t and not account for scr refresh
            key_resp_end_mic.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(key_resp_end_mic, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'key_resp_end_mic.started')
            key_resp_end_mic.status = STARTED
            # keyboard checking is just starting
            waitOnFlip = True
            win.callOnFlip(key_resp_end_mic.clock.reset)  # t=0 on next screen flip
            win.callOnFlip(key_resp_end_mic.clearEvents, eventType='keyboard')  # clear events on next screen flip
        if key_resp_end_mic.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > key_resp_end_mic.tStartRefresh + 15.5-frameTolerance:
                # keep track of stop time/frame for later
                key_resp_end_mic.tStop = t  # not accounting for scr refresh
                key_resp_end_mic.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'key_resp_end_mic.stopped')
                key_resp_end_mic.status = FINISHED
        if key_resp_end_mic.status == STARTED and not waitOnFlip:
            theseKeys = key_resp_end_mic.getKeys(keyList=['space'], waitRelease=False)
            _key_resp_end_mic_allKeys.extend(theseKeys)
            if len(_key_resp_end_mic_allKeys):
                key_resp_end_mic.keys = _key_resp_end_mic_allKeys[-1].name  # just the last key pressed
                key_resp_end_mic.rt = _key_resp_end_mic_allKeys[-1].rt

        #look into clearing the keypresses at the beginning of the routine
        #psychopy.event.clearEvents(eventType=None)[source]
        #Clears all events currently in the event buffer.

        #Optional argument, eventType, specifies only certain types to be cleared.

        #Parameters
        #eventTypeNone, ‘mouse’, ‘joystick’, ‘keyboard’
        #If this is not None then only events of the given type are cleared
        
        # mic updates
        if mic.status == NOT_STARTED and t >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            mic.frameNStart = frameN  # exact frame index
            mic.tStart = t  # local t and not account for scr refresh
            mic.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(mic, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.addData('mic.started', t)
            # start recording with mic
            mic.start()
            mic.status = STARTED
        if mic.status == STARTED:
            # update recorded clip for mic
            mic.poll()    
        **if mic.status == STARTED:**
**            if event.getKeys(['space']):**
**                mic.stop()**
**                mic.status == FINISHED**
**                continueRoutine = False**
        if mic.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > mic.tStartRefresh + 15.0-frameTolerance:
                # keep track of stop time/frame for later
                mic.tStop = t  # not accounting for scr refresh
                mic.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.addData('mic.stopped', t)
                # stop recording with mic
                mic.stop()
                mic.status = FINISHED
        
        # check for quit (typically the Esc key)
        if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
            core.quit()
        
        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            routineForceEnded = True
            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
        
        # 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)
    # check responses
    if key_resp_end_mic.keys in ['', [], None]:  # No response was made
        key_resp_end_mic.keys = None
    trials.addData('key_resp_end_mic.keys',key_resp_end_mic.keys)
    if key_resp_end_mic.keys != None:  # we had a response
        trials.addData('key_resp_end_mic.rt', key_resp_end_mic.rt)
    # tell mic to keep hold of current recording in mic.clips and transcript (if applicable) in mic.scripts
    # this will also update mic.lastClip and mic.lastScript

    tag = data.utils.getDateStr()
    
     #audio files were not saving properly, found this way to save them from other forum code
    **audioClip = mic.getRecording()**
**    print(audioClip.duration)**
**    print(os.path.join(micRecFolder, '%s_%s.wav' % (TRIALCOUNTER,nme)))**
**    audioClip.save(os.path.join(micRecFolder, '%s_%s.wav' % (TRIALCOUNTER, nme)))**
**    TRIALCOUNTER += 1**
    trials.addData('mic.clip', os.path.join(micRecFolder, 'recording_mic_%s.wav' % tag))
    # the Routine "trial" was not non-slip safe, so reset the non-slip timer
    if routineForceEnded:
        routineTimer.reset()
    else:
        routineTimer.addTime(-12.500000)
    
    # --- Prepare to start Routine "break_2" ---
    continueRoutine = True
    routineForceEnded = False
    # update component parameters for each repeat
    key_resp8.keys = []
    key_resp8.rt = []
    _key_resp8_allKeys = []
    # keep track of which components have finished
    break_2Components = [key_resp8, polygon]
    for thisComponent in break_2Components:
        thisComponent.tStart = None
        thisComponent.tStop = None
        thisComponent.tStartRefresh = None
        thisComponent.tStopRefresh = None
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    # reset timers
    t = 0
    _timeToFirstFrame = win.getFutureFlipTime(clock="now")
    frameN = -1
    **#defaultKeyboard.clearEvents()**
    # --- Run Routine "break_2" ---
    while continueRoutine and routineTimer.getTime() < 1.5:
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *key_resp8* updates
        waitOnFlip = False
        if key_resp8.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            key_resp8.frameNStart = frameN  # exact frame index
            key_resp8.tStart = t  # local t and not account for scr refresh
            key_resp8.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(key_resp8, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'key_resp8.started')
            key_resp8.status = STARTED
            # keyboard checking is just starting
            waitOnFlip = True
            win.callOnFlip(key_resp8.clock.reset)  # t=0 on next screen flip
            win.callOnFlip(key_resp8.clearEvents, eventType='keyboard')  # clear events on next screen flip
        if key_resp8.status == STARTED and not waitOnFlip:
            theseKeys = key_resp8.getKeys(keyList=['p'], waitRelease=False)
            _key_resp8_allKeys.extend(theseKeys)
            if len(_key_resp8_allKeys):
                key_resp8.keys = _key_resp8_allKeys[-1].name  # just the last key pressed
                key_resp8.rt = _key_resp8_allKeys[-1].rt
                # a response ends the routine
                #continueRoutine = False
        **if event.getKeys(['p']):**
**            time.sleep(20)**
        # *polygon* updates
        if polygon.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            polygon.frameNStart = frameN  # exact frame index
            polygon.tStart = t  # local t and not account for scr refresh
            polygon.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(polygon, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'polygon.started')
            polygon.setAutoDraw(True)
        if polygon.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > polygon.tStartRefresh + 1.5-frameTolerance:
                # keep track of stop time/frame for later
                polygon.tStop = t  # not accounting for scr refresh
                polygon.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'polygon.stopped')
                polygon.setAutoDraw(False)
        # check for quit (typically the Esc key)
        if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
            core.quit()
        
        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            routineForceEnded = True
            break
        continueRoutine = False  # will revert to True if at least one component still running
        for thisComponent in break_2Components:
            if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished
        
        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()
    
    # --- Ending Routine "break_2" ---
    for thisComponent in break_2Components:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # check responses
    if key_resp8.keys in ['', [], None]:  # No response was made
        key_resp8.keys = None
    trials.addData('key_resp8.keys',key_resp8.keys)
    if key_resp8.keys != None:  # we had a response
        trials.addData('key_resp8.rt', key_resp8.rt)
    # the Routine "break_2" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset()
    thisExp.nextEntry()
    
# completed 1.0 repeats of 'trials'

dativeversion_FINAL_WORKING_BREAKTIMER_ONTIMER.py (59.1 KB)
.

That was probably a mistake - modifying a Builder script is best done by inserting graphical code components, rather than manually editing the generated script. The latter approach makes it very difficult for anyone else to review your experiment.

If you still have a Builder file, perhaps post that so that we understand the flow of the experiment. Generally in Builder, simply selecting “discard previous” in a keyboard component would be sufficient to stop an earlier keypress triggering a response.

1 Like