psychopy.org | Reference | Downloads | Github

Routine Won't Stay Up for Duration of Specified Time

Hello! I have a task in the experiment that requires participants to make words out of the letters in another word (i.e. you can make “straw” and “bye” out of “strawberry”). They enter in their answers with the keyboard and have 120 seconds to enter as many as they can. The code works well on the first task (“crustacean” or wordsearch1), but halfway through the second task (“librarian” or wordsearch2), the screen goes black and won’t allow further entries or respond to button presses. It eventually moves on to the end screen. Here’s the code (sorry for formatting):

    #------Prepare to start Routine "wordsearch"-------
    t = 0
    wordsearchClock.reset()  # clock 
    frameN = -1
    routineTimer.add(120.000000)
    # update component parameters for each repeat
    theseKeys=""
    shift_flag = False
    text_16.alignHoriz ='left'
    
    key_resp_wordsearch = event.BuilderKeyResponse()  # create an object of type KeyResponse
    key_resp_wordsearch.status = NOT_STARTED
    # keep track of which components have finished
    wordsearchComponents = []
    wordsearchComponents.append(text_16)
    wordsearchComponents.append(key_resp_wordsearch)
    for thisComponent in wordsearchComponents:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    
    #-------Start Routine "wordsearch"-------
    continueRoutine = True
    while continueRoutine and routineTimer.getTime() > 0:
        # get current time
        t = wordsearchClock.getTime()
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        n= len(theseKeys)
        i = 0
        while i < n:
        
            if theseKeys[i] == 'backspace':
                inputText = inputText[:-1]  # lose the final character
                i = i + 1
        
            elif theseKeys[i] == 'space':
                inputText += ' '
                i = i + 1
        
            elif theseKeys[i] in ['lshift', 'rshift']:
                shift_flag = True
                i = i + 1
        
            else:
                if len(theseKeys[i]) == 1:
                    # we only have 1 char so should be a normal key, 
                    # otherwise it might be 'ctrl' or similar so ignore it
                    if shift_flag:
                        inputText += chr( ord(theseKeys[i]) - ord(' '))
                        shift_flag = False
                    else:
                        inputText += theseKeys[i]
        
                i = i + 1
        
        
        # *text_16* updates
        if t >= 0.0 and text_16.status == NOT_STARTED:
            # keep track of start time/frame for later
            text_16.tStart = t  # underestimates by a little under one frame
            text_16.frameNStart = frameN  # exact frame index
            text_16.setAutoDraw(True)
        if text_16.status == STARTED and t >= (120-win.monitorFramePeriod*0.75): #most of one frame period left
            text_16.setAutoDraw(False)
        if text_16.status == STARTED:  # only update if being drawn
            text_16.setText((word + '\n' + inputText), log=False)
        
        # *key_resp_wordsearch* updates
        if t >= 0.0 and key_resp_wordsearch.status == NOT_STARTED:
            # keep track of start time/frame for later
            key_resp_wordsearch.tStart = t  # underestimates by a little under one frame
            key_resp_wordsearch.frameNStart = frameN  # exact frame index
            key_resp_wordsearch.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(key_resp_wordsearch.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if key_resp_wordsearch.status == STARTED and t >= (120-win.monitorFramePeriod*0.75): #most of one frame period left
            key_resp_wordsearch.status = STOPPED
        if key_resp_wordsearch.status == STARTED:
            theseKeys = event.getKeys()
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                key_resp_wordsearch.keys.extend(theseKeys)  # storing all keys
                key_resp_wordsearch.rt.append(key_resp_wordsearch.clock.getTime())
        
        # 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 wordsearchComponents:
            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 "wordsearch"-------
    for thisComponent in wordsearchComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # let's store the final text string into the results finle...
    thisExp.addData('inputText', inputText)
    inputText=""
    
    
    # check responses
    if key_resp_wordsearch.keys in ['', [], None]:  # No response was made
       key_resp_wordsearch.keys=None
    # store data for trials_wordsearchfiller (TrialHandler)
    trials_wordsearchfiller.addData('key_resp_wordsearch.keys',key_resp_wordsearch.keys)
    if key_resp_wordsearch.keys != None:  # we had a response
        trials_wordsearchfiller.addData('key_resp_wordsearch.rt', key_resp_wordsearch.rt)
    thisExp.nextEntry()
    
# completed 1 repeats of 'trials_wordsearchfiller'


#------Prepare to start Routine "filler_break"-------
t = 0
filler_breakClock.reset()  # clock 
frameN = -1
# update component parameters for each repeat
key_resp_5 = event.BuilderKeyResponse()  # create an object of type KeyResponse
key_resp_5.status = NOT_STARTED
# keep track of which components have finished
filler_breakComponents = []
filler_breakComponents.append(text_22)
filler_breakComponents.append(key_resp_5)
for thisComponent in filler_breakComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

#-------Start Routine "filler_break"-------
continueRoutine = True
while continueRoutine:
    # get current time
    t = filler_breakClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *text_22* updates
    if t >= 0.0 and text_22.status == NOT_STARTED:
        # keep track of start time/frame for later
        text_22.tStart = t  # underestimates by a little under one frame
        text_22.frameNStart = frameN  # exact frame index
        text_22.setAutoDraw(True)
    
    # *key_resp_5* updates
    if t >= 0.0 and key_resp_5.status == NOT_STARTED:
        # keep track of start time/frame for later
        key_resp_5.tStart = t  # underestimates by a little under one frame
        key_resp_5.frameNStart = frameN  # exact frame index
        key_resp_5.status = STARTED
        # keyboard checking is just starting
        win.callOnFlip(key_resp_5.clock.reset)  # t=0 on next screen flip
        event.clearEvents(eventType='keyboard')
    if key_resp_5.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_5.keys = theseKeys[-1]  # just the last key pressed
            key_resp_5.rt = key_resp_5.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 filler_breakComponents:
        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 "filler_break"-------
for thisComponent in filler_breakComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
# check responses
if key_resp_5.keys in ['', [], None]:  # No response was made
   key_resp_5.keys=None
# store data for thisExp (ExperimentHandler)
thisExp.addData('key_resp_5.keys',key_resp_5.keys)
if key_resp_5.keys != None:  # we had a response
    thisExp.addData('key_resp_5.rt', key_resp_5.rt)
thisExp.nextEntry()
# the Routine "filler_break" was not non-slip safe, so reset the non-slip timer
routineTimer.reset()

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

for thisTrials_wordsearch2 in trials_wordsearch2:
    currentLoop = trials_wordsearch2
    # abbreviate parameter names if possible (e.g. rgb = thisTrials_wordsearch2.rgb)
    if thisTrials_wordsearch2 != None:
        for paramName in thisTrials_wordsearch2.keys():
            exec(paramName + '= thisTrials_wordsearch2.' + paramName)
    
    #------Prepare to start Routine "wordsearch_2"-------
    t = 0
    wordsearch_2Clock.reset()  # clock 
    frameN = -1
    routineTimer.add(120.000000)
    # update component parameters for each repeat
    theseKeys=""
    shift_flag = False
    text_16.alignHoriz ='left'
    
    key_resp_wordsearch_2 = event.BuilderKeyResponse()  # create an object of type KeyResponse
    key_resp_wordsearch_2.status = NOT_STARTED
    # keep track of which components have finished
    wordsearch_2Components = []
    wordsearch_2Components.append(text_21)
    wordsearch_2Components.append(key_resp_wordsearch_2)
    for thisComponent in wordsearch_2Components:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    
    #-------Start Routine "wordsearch_2"-------
    continueRoutine = True
    while continueRoutine and routineTimer.getTime() > 0:
        # get current time
        t = wordsearch_2Clock.getTime()
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        n= len(theseKeys)
        i = 0
        while i < n:
        
            if theseKeys[i] == 'backspace':
                inputText = inputText[:-1]  # lose the final character
                i = i + 1
        
            elif theseKeys[i] == 'space':
                inputText += ' '
                i = i + 1
        
            elif theseKeys[i] in ['lshift', 'rshift']:
                shift_flag = True
                i = i + 1
        
            else:
                if len(theseKeys[i]) == 1:
                    # we only have 1 char so should be a normal key, 
                    # otherwise it might be 'ctrl' or similar so ignore it
                    if shift_flag:
                        inputText += chr( ord(theseKeys[i]) - ord(' '))
                        shift_flag = False
                    else:
                        inputText += theseKeys[i]
        
                i = i + 1
        
        
        # *text_21* updates
        if t >= 0.0 and text_21.status == NOT_STARTED:
            # keep track of start time/frame for later
            text_21.tStart = t  # underestimates by a little under one frame
            text_21.frameNStart = frameN  # exact frame index
            text_21.setAutoDraw(True)
        if text_21.status == STARTED and t >= (120-win.monitorFramePeriod*0.75): #most of one frame period left
            text_21.setAutoDraw(False)
        if text_21.status == STARTED:  # only update if being drawn
            text_21.setText((word + '\n' + inputText), log=False)
        
        # *key_resp_wordsearch_2* updates
        if t >= 0.0 and key_resp_wordsearch_2.status == NOT_STARTED:
            # keep track of start time/frame for later
            key_resp_wordsearch_2.tStart = t  # underestimates by a little under one frame
            key_resp_wordsearch_2.frameNStart = frameN  # exact frame index
            key_resp_wordsearch_2.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(key_resp_wordsearch_2.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if key_resp_wordsearch_2.status == STARTED and t >= (120-win.monitorFramePeriod*0.75): #most of one frame period left
            key_resp_wordsearch_2.status = STOPPED
        if key_resp_wordsearch_2.status == STARTED:
            theseKeys = event.getKeys()
            
            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                key_resp_wordsearch_2.keys.extend(theseKeys)  # storing all keys
                key_resp_wordsearch_2.rt.append(key_resp_wordsearch_2.clock.getTime())
        
        # 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 wordsearch_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 "wordsearch_2"-------
    for thisComponent in wordsearch_2Components:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # let's store the final text string into the results finle...
    thisExp.addData('inputText', inputText)
    inputText=""
    
    
    # check responses
    if key_resp_wordsearch_2.keys in ['', [], None]:  # No response was made
       key_resp_wordsearch_2.keys=None
    # store data for trials_wordsearch2 (TrialHandler)
    trials_wordsearch2.addData('key_resp_wordsearch_2.keys',key_resp_wordsearch_2.keys)
    if key_resp_wordsearch_2.keys != None:  # we had a response
        trials_wordsearch2.addData('key_resp_wordsearch_2.rt', key_resp_wordsearch_2.rt)
    thisExp.nextEntry()
    
# completed 1 repeats of 'trials_wordsearch2'

I just copied the code from the first task for the second, so I don’t understand why this is not working. Any advice or help on this would be much appreciated! :smiley:

but also note that people are much less likely to spend time reading through a huge listing of code than if you have posted just the more relevant sections.

Thank you very much for the heads up. I edited it to correct for this. I want to also limit the code I include, as you suggest, but I know so little about Python that I don’t know what’s important and what’s not. I just included the start and ending of the first word search task and the second word search task (along with a simple text screen in the middle that acts as a break between the tasks) to see if that would be enough. I don’t understand why one would work and the other would not if they are the same code…
Thanks again!

Can I just check that you are actually running this in Coder, or are you actually running the task from the Builder interface, with your custom code inserted into Code components?

From what I can see, there is no reason why it couldn’t be the latter, which will make any issues much easier to manage. Builder generates very verbose code, which can make it difficult to edit and adjust manually. It’s much easier and safer to use code components (and that way, we can just look at the custom code snippets you are using, rather than the entire generated script).

Hi, Michael. Thanks for your quick response. I think you’re right that I’m running the task from the Builder interface with custom code inserted into Code components. I tried to attach a word document with screenshots to have one link instead of multiple pictures, but it didn’t accept the file type. Here’s everything and likely more than you need:

!

I did a test run where I deleted everything that came before these tasks (there are a couple different tasks before this one), and it ran fine. Does that mean it has something to do with memory and the inability to run the full code? Like I said, the second word search task runs with the full task, it just switches to a black screen before the full 120 sec is over and you have to wait, staring at the black screen before the end screen. Thank you so much for any advice or help on this!

No, memory is very unlikely to be an issue. It sounds like there is some sort of conflict between what you deleted and remainder of the experiment (e.g. perhaps a variable name that gets re-used and still contains its earlier value). We probably need to see the complete .psyexp file.

I have attached the .psyexp file here. CRSP.psyexp (176.0 KB)
One thing I should note is that when it worked, I was also running the code on a computer with a larger monitor. Perhaps it’s something to do with scale or display? Something I saw today was that although the screen goes black for the second half, the computer is still storing data properly. So you’ll be entering in words where you can physically see those words as you’re typing for about 1 minute and then the screen goes black and the instructions disappear and you can’t see anything, BUT if you keep typing and look at the Excel data later, you can see whatever you typed. Does that point to a display problem instead of input? Do the words not wrap around and start to move to another “page” in a way? Thanks again for your replies.

I can’t see anything obviously wrong but it is a very large set-up, with lots of different routines, which makes it a challenge to debug. A key principle of programming is DRY: Don’t Repeat Yourself. Wherever possible in programming, we should strive to re-use rather than repeat. Copying and pasting code (or routines in this case) can lead to errors and complexity due to the level of duplication.

e.g. your routines wordsearch and wordsearch_2 seem to be functionally identical. You could just re-use the word search routine in the second loop, and delete the wordsearch_2 routine entirely. Note that in the current wordsearch_2, there is a slight error: in the code component, you set the alignment of text_16 rather than text_21. This is a problem with duplicating code: errors creep in and are hard to detect.

Similarly, all of your instruction routines seem to be functionally identical: the only thing that differs between them is the text to be displayed. Meanwhile, they are needlessly set to store the keypress which ends the routine, even though it isn’t informative and will just serve to add additional columns to your data file. But changing that setting would need to be done repeatedly for every routine. What you could do to rationalise this is to delete all of the instruction routines but one. In that one, insert a code component, and in the “begin experiment” tab, define all of the text prompts in code, in a list like this:

instructions = ['Welcome to the experiment! \n We will ask you to complete various tasks. \n If you have a question, you can always open your door and ask the experimenter.', 
'Now you will be asked to complete several questionnaires.  \n Please read each set of instructions carefully before you begin entering your responses.',
'etc']

Then in the text field of your instruction routine, put something like this:

$instructions.pop(0) + '\n \n Press SPACEBAR to continue.'

Then you can just insert that single routine anywhere you like in the experiment. Depending on its order of presentation, it will pop the next item from the list of instructions. Now you can, say, change the size or colour of the text there and it will automatically apply to all of the instructions throughout the experiment: no need to do it multiple times.

I can’t see the source of your issue at the moment, but this sort of rationalisation will greatly trim down the code in your experiment file and hopefully make it easier to locate where any issue might lie.

Extra credit: similarly there is a lot of duplication of actual Python code, such that used to handle the timing on screen. Ideally that would be put into a single function that gets defined at the start of the experiment, and then just get called as a single line of code in each routine as needed.