Pause a loop with a key press

Dear Jon,

In my experiment I would like to have pause after a key press. My necessity is to stop the experiment in any moment at the end of each trial if a problem occurs. To do this I created (within a loop) a routine with a trial followed by a routine with the pause.

First I Inizialize a Pause_check variable set to 0.

Pause_check = 0

Then, what I would like to do is to change the status of Pause_check during the trial routine.
To do this, in the trial routine I added this code (each frame).

if trial_key.status ==  STARTED:
    if (len(theseKeys) > 0 and trial_key.keys == 'p'):
        Pause_check = 1

where trial_key is a component in which ā€˜pā€™ is an allowed key.

After the trial routine there is a Pause routine in which I put a Code component in the pause routine with the following code (each frame)

if Pause_check == 0:
        continueRoutine=False

My problem is that the pause is not working. I presume that my mistake is in the check to create the Pause_check object.

Any hint on how to create this pause? (or where Iā€™m wrong?)

Many thanks!

Hello Giorgio,

When posting code, itā€™s nice to format it so itā€™s easier for us to read. You can surround code with three backticks (`) like this:

```python
(code here)
```

And it will be easier for us to read.

But to your question, one problem is that if you do this:

if Pause_check == 0:
    continueRoutine = False

This wonā€™t pause your routine if youā€™re following psychopy customs, it will end it.

Also, thisā€¦

trial_key.keys == 'p'

ā€¦ is not doing what you think itā€™s doing. ā€˜theseKeysā€™ is a list of keys that were pressed the last time the system checked for them, so could simply do this to see if ā€˜pā€™ is in that list:

if 'p' in theseKeys:

So you simply want to pause the routine when they press ā€œpā€, and let them unpause it with ā€˜pā€™ as well?

You have to understand that computers never really ā€œpauseā€, they are always active. Just like if youā€™re playing a childā€™s ā€œfreezeā€ game where everyone has to stand still, youā€™re always actively waiting for the ā€˜goā€™ signal, you donā€™t actually shut your brain off. To imitate a pause like a computer would you have to think more like this:

  • Do experiment stuff, check for ā€˜pā€™ keypress
  • ā€˜pā€™ has been pressed!
  • Keep actively checking for another ā€˜pā€™, and do nothing else
  • ā€˜pā€™ has been pressed!
  • Back to experiment stuff.

So you want something like this:

continueRoutine = True
paused = False
while continueRoutine:

    if trial_key.status == STARTED:
        theseKeys = event.getKeys()
        if 'p' in theseKeys:
            if not paused:
                paused = True
                # the 'continue' statement will 
                # take us back to the top of the loop
                continue
            elif paused:
                paused = False

        # 'p' is not in theseKeys. If we're paused go to 
        # the top of the loop
        elif paused:
            continue

        # Rest of the code for the routine starts here
1 Like

I suspect that Daniel is correct in that the problem was that keys are returned as a list rather than as a single value.

But I think that otherwise Giorgio is on the right track. His logic is to have a ā€œpauseā€ routine after the trial routine, but that pause routine will only run if ā€˜pā€™ was pressed on the previous routine. i.e. if ā€˜pā€™ wasnā€™t pushed, continueRoutine for this pause routine is set to False in its ā€˜Begin routineā€™ tab, so that the pause routine doesnā€™t actually start on that iteration of the loop.

This is the recommended way to implement this sort of arrangement, but actually only works properly from the latest release of PsychoPy.

Danielā€™s solution (to implement a ā€˜real timeā€™ pause within a routine) would likely work but could have all sorts of hard-to-foresee consequences. Builder experiments are fundamentally structured on a drawing loop that runs once on every screen refresh. Using code like this would break that structure (i.e. it will pause drawing to the screen and everything else). This means that Builder will lose control of screen re-drawing and break its ability to time stimuli by frame counting. So Iā€™d suggest that Giorgio stick to his original arrangement in this case.

1 Like

Ah yes! That makes more sense for an experiment. So after each trial, there would be another routine that would last for a certain amount of time (maybe showing a fixation stimulus or something), and pause could be implemented then but not during a trial, right? That would make more sense.

I think so. I guess Giorgio would have a keyboard component on that ā€˜pauseā€™ routine so that it would end when a key is pressed, allowing the next iteration of the loop to proceed when the participant is ready.

Actually rereading your post Michael, I guess what I said in my comment would be different: if one were measuring reaction times, having to press a pause key within the trial would ruin that trial, though if itā€™s only for emergencies I guess there would already be a problemā€¦

Dear Daniel and Michael,
Thank you both for your help. I managed to achieve what I wanted adding this code in the trial routine (each frame):

''' check if a response is given and if it is p, set Pause_check to 1 '''
if trial_key.status == STARTED:
    if (len(theseKeys) > 0 and 'p' in theseKeys):
        Pause_check =1

In programming with a separate routine for the pause I actually followed some recommendations I think I found in the old psychopy forum.

Thank you again for the prompt response!

Hello @Michael, Iā€™m sorry to bring up a slightly old post, but itā€™s been bugging me. Since my experiments as of yet havenā€™t had to worry about frame rates my understanding of the issue is not complete.

You said that the solution I suggested would mess up the frame count, so how could we avoid that? Would it simply be by always calling win.flip() before returning to the top of the loop?

Hi @daniel.riggs1 ,

When working in Builder rather than Coder, we sacrifice some control for ease of use. Part of that control is that Builder has authority over the screen drawing cycle. Fundamentally, Builder is structured entirely around the screen refresh cycle: it expects to update stimuli, check for responses, execute code components, and so on, on every screen refresh. It also does all its timing checks on that cycle. i.e. even if you are specifying that your stimuli should last a certain period of time rather than a certain number of frames, Builder is checking that time once per frame.

So in our code components, there are certain things we should never do, in order to allow Builder to keep ticking along on the screen refresh cycle (including, but not limited to):

(1) Donā€™t create ā€˜pauseā€™ periods in which the screen wonā€™t be updated. For example, we can use event.getKeys() because it just makes an instantaneous check of the keyboard. But we should never use even'waitKeys(), as that pauses everything waiting for a keypress, and will break Builderā€™s screen refresh cycle. We also need to avoid any loops that could last more than one screen refresh (e.g. embedding an event.getKeys() check within an infinite while loop).

(2) We should never call win.flip(). This will prevent anything happening until the next screen refresh, including any housekeeping that Builder needs to do during that period. So for example, if the code component was at the top, any stimuli below it would not be updated by Builder on that frame. And Iā€™m not sure sure if Builder would even know it has missed a screen refresh, so its timing could then be thrown out. There would be all sorts of unpredictable effects.
So let Builder carry out the one and only win.flip() on each cycle, so it keeps control of the cycle and we get to go along for the ride.

If you find you need more control than fitting into this arrangement provides, then it really is time to switch to Coder, where you have complete flexibility over what happens and when.

Make sense?

Your question is a good one, and Builder should probably provide some automated warnings if it detects win flip() or event.waitKeys() in a code component (checking for potentially long-lasting loops would be harder).

Cheers Daniel,

Michael