Changing quit experiment key press from escape

OS (e.g. Win10): MacOS, Win7
PsychoPy version (e.g. 1.84.x): 3.0.0
**Standard Standalone? Yes
**What are you trying to achieve?:I would like to change the keypress to end an experiment from escape to another key, such as ctrl+escape. I am having my participants enter numbers to respond to stimuli and have had problems where they accidentally hit escape when trying to press 1.

**What did you try to make it work?: I tried to enter Coder view and replace escape with ctrl+escape.

**What specifically went wrong when you tried that?:PsychoPy will not respond to ctrl+escape or escape when I am in the experiment and am forced to ctrl+alt+del to end PsychoPy

Hi @eyexciteme, to disable the escape key, go to your experiment settings, and deselect the Enable escape key option. In PsychoPy, once you have disabled the escape key, you cannot reuse it in combination with other keys. Instead, you could use another common key combination. Choose your combination of keys to quit the experiment, add a code component, and in the Every Frame tab:

keys = event.getKeys()
for key in keys:
if ‘ctrl’ in key and ‘z’ in key:
core.quit()

2 Likes

Great. Thank you for the help!

We’ve tried this solution with no luck. If I understand correctly, this snippet should be put in every routine in code components on the “each frame” tab.

I have tried multiple combination of keys, and the experiment quits when I input any three keys (regardless of timing). For example, if I press spacebar to advance past the two routines, then after the third spacebar, the experiment will quit.

I’ve tried naming all the “keys” objects differently. I’ve also tried just putting this code in one routine. Then, the experiment doesn’t close like above, but I also can’t close the experiment unless I’m in that one routine.

Any ideas?

Please show us the actual code you are using. The snippet of code above is actually a bit mangled and wouldn’t work as described.

1 Like

I tried virtually the same as what @dvbridges suggested. After disabling ‘esc’ key, in each routine under the “every frame” tab:

keys = event.getKeys()
for key in keys:
    if 'b' and 'p' in key:
        core.quit()
1 Like

Personally I would try

keys = event.getKeys()
if 'b' in keys and 'p' in keys:
        core.quit()
1 Like

Thanks. That sort-of works. If I put that code in the “begin routine” tab for each routine, and I press those two buttons, it won’t quit immediately, but it will quit when that routine finishes. A workable fix, I suppose.

That code should be in Each Frame

Yeah that code has a typo, I will edit the original just to avoid confusion - not sure if it works now.

I think Wakefield has it correct in this post:

ie putting a for loop in there will break things, as you want to check that both keys are in the entire list, rather than iterate over individual items, which by definition can’t have two entries.

1 Like

I can confirm that this doesn’t work, unfortunately. I’m assuming if keys gets created every frame, it won’t quit unless ‘b’ and ‘p’ are input on the same frame. The best workaround I’ve found is creating keys in “Begin Routine” and then looking for ‘b’ and ‘p’ each frame. However, this still only quits at the end of the routine.
edit: pressing them simultaneously works

@dvbridges’s new code above also does not work :confused:

Yeah, that code above has been crossed out. If you’re just looking Python-only fix that uses key modifiers in combination with normal characters, try the following in the Each Frame tab, which will cause experiment to quit using “ctrl” and “d”:

keys = event.getKeys(modifiers=True)
if len(keys):
    key = keys[-1]
    if key[0] == 'd' and key[1]['ctrl']:
        core.quit()

The other solution with the new keyboard involves storing all keypresses and clearing them if they are not they keys you want. In your case above you want to participant to press “b” and “p”. E.g.,

# Start exp
kb = keyboard.Keyboard()

# Each Frame
keys = kb.getKeys(clear=False)
if len(keys) > 1:
    if keys[0].name == "b" and keys[1].name == "p" or keys[0].name == "p" and keys[1].name == "b":
        core.quit()
    else:
        kb.clearEvents()
1 Like

Are you sure my code doesn’t work for simultaneous key presses (which is what I assumed you wanted).

Alternatives could be appending new key presses to a list each frame and then checking whether the two keys are in that list, or resetting a timer when one key is pressed and then ending the routine if the other key is pressed within a certain time.

1 Like

Aha! Simultaneously worked. I was pressing them in rapid succession (but clearly not fast enough). Thanks so much for clarifying.

Rapid succession would only work if you’re within the same frame.

This iohub based solution could also be an option, and both keys do not need to be initially pressed within the same frame:

At start of experiment:

from psychopy.iohub import launchHubServer
# Start iohub and get the keyboard device
io_keyboard = launchHubServer().devices.keyboard

on each frame, when you want to check if ‘left-control’ and ‘c’ are currently pressed:

if 'lctrl' in io_keyboard.state and 'c' in keyboard.state:
    print("exiting....")
    core.quit()

Here’s a complete python example that checks for lctrl+c for 10 seconds:

from psychopy import iohub, core

keyboard = iohub.launchHubServer().devices.keyboard

print("Press lctrl + c...")
stime = core.getTime()
while core.getTime() - stime < 10:
    if 'lctrl' in keyboard.state and 'c' in keyboard.state:
        print("lctrl + c are pressed")
        core.quit()
print("Timeout")