Non-ASCII keypress recorded, but not actionable

OS (e.g. Win10): Win7
PsychoPy version (e.g. 1.84.x): 3.15
Standard Standalone? (y/n) y
What are you trying to achieve?: I can trying to record an MRI trigger, which send a ~/`.
I have previously created an experiment file using the Builder with v3.0.6 to receive “quoteleft” and “grave” and it worked perfectly.

Any version after 3.0.6 does not work with these keys. The problem is that the keypress is recorded in the log file, but it does not force the routine to end as it should.

What did you try to make it work?: I have tried both the original experiment file and also created another experiment file from scratch to test. In all versions from 3.0.6 to 3.1.5 (somehow v.3.0.7 did not allow me to create the Python script), “quoteleft” was recorded in the log file. However, only in 3.0.6 did it force the routine to end as intended.

I tried replacing the required keypress with “space” and it worked with all versions from 3.0.6 to 3.1.5.

What specifically went wrong when you tried that?:
There was no error message. Just that the key was not actionable.

I would greatly appreciate it if I can get help in incorporating non-ASCII keypresses (specifically “quoteleft”) to be actionable. Thanks!

Best,
Darren

Can you show those lines from the log file?

Keyboard handling changed greatly from version 3.1 onwards, to provide much improved timing and add capacities such as measuring keypress duration. Maybe as a result, the reported key name for these keys might have changed?

Hi Michael,

Thanks for following up!
These are the relevant lines from the log file:

|34.8540 |EXP |blankscreen: autoDraw = True|
|37.3919 |DATA |Keypress: quoteleft|
|40.2620 |DATA |Keypress: space|
|40.2750 |EXP |blankscreen: autoDraw = False|

Here is the relevant chunk of code:

# ------Prepare to start Routine "triggerScan"-------
t = 0
triggerScanClock.reset()  # clock
frameN = -1
continueRoutine = True
# update component parameters for each repeat
trigger = keyboard.Keyboard()
# keep track of which components have finished
triggerScanComponents = [blankscreen, trigger]
for thisComponent in triggerScanComponents:
    thisComponent.tStart = None
    thisComponent.tStop = None
    thisComponent.tStartRefresh = None
    thisComponent.tStopRefresh = None
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

# -------Start Routine "triggerScan"-------
while continueRoutine:
    # get current time
    t = triggerScanClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *blankscreen* updates
    if t >= 0.0 and blankscreen.status == NOT_STARTED:
        # keep track of start time/frame for later
        blankscreen.tStart = t  # not accounting for scr refresh
        blankscreen.frameNStart = frameN  # exact frame index
        win.timeOnFlip(blankscreen, 'tStartRefresh')  # time at next scr refresh
        blankscreen.setAutoDraw(True)
    
    # *trigger* updates
    if t >= 0.0 and trigger.status == NOT_STARTED:
        # keep track of start time/frame for later
        trigger.tStart = t  # not accounting for scr refresh
        trigger.frameNStart = frameN  # exact frame index
        win.timeOnFlip(trigger, 'tStartRefresh')  # time at next scr refresh
        trigger.status = STARTED
        # keyboard checking is just starting
        win.callOnFlip(trigger.clock.reset)  # t=0 on next screen flip
        trigger.clearEvents(eventType='keyboard')
    if trigger.status == STARTED:
        theseKeys = trigger.getKeys(keyList=['quoteleft', '`', 'grave', 'space', '1'], waitRelease=False)
        if len(theseKeys):
            theseKeys = theseKeys[0]  # at least one key was pressed
            
            # check for quit:
            if "escape" == theseKeys:
                endExpNow = True
            if trigger.keys == []:  # then this was the first keypress
                trigger.keys = theseKeys.name  # just the first key pressed
                trigger.rt = theseKeys.rt
                # a response ends the routine
                continueRoutine = 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
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in triggerScanComponents:
        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 "triggerScan"-------
for thisComponent in triggerScanComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
thisExp.addData('blankscreen.started', blankscreen.tStartRefresh)
thisExp.addData('blankscreen.stopped', blankscreen.tStopRefresh)
# check responses
if trigger.keys in ['', [], None]:  # No response was made
    trigger.keys = None
thisExp.addData('trigger.keys',trigger.keys)
if trigger.keys != None:  # we had a response
    thisExp.addData('trigger.rt', trigger.rt)
thisExp.addData('trigger.started', trigger.tStartRefresh)
thisExp.addData('trigger.stopped', trigger.tStopRefresh)
thisExp.nextEntry()
# the Routine "triggerScan" was not non-slip safe, so reset the non-slip timer
routineTimer.reset()

The routine ends with ‘space’ and ‘1’, but not ‘quoteleft’. I tried adding variants of u"", which I read from other posts that it might help, but to no avail.

Thanks!

Hi Michael,

I hope you’re doing well.
I just wanted to check in with you regarding this issue.

I noticed that although the log file is able to log ‘quoteleft’, it is not recorded as the first key when I selected “Store first key” in the keyboard component. So, my first guess was that PsychoPy is not treating the keypress as a valid one. However, when I have the allowed keys to be any key (i.e., leaving the allowed keys field blank), it treats that very same key as valid and forces the routine to end as intended.

I would prefer not to accept any key to avoid the routine to end with an accidental keypresses. May I know if there is a way to use numbered key codes instead?

Thanks!

Hi, sorry this dropped off my radar. What happens when you switch to the Coder view and from the “Demos” menu, run the “Input” -> 'KeyboardNEW.py" demo?

It seems like the new Psychtoolbox Keyboard class returns different names for some keys compared to the old event module code used prior to version 3.1 of PsychoPy. Run that demo and use the values shown on-screen for “PTB” (Psychtoolbox) rather than the old values (which are labelled “waitKeys”).

1 Like

Hi Michael! No worries at all. Your solution worked! Psychtoolbox recognizes ‘quoteleft’ as ‘apostrophe’ now. I have also used the numbers on the keypad as input and they are recognized differently as well. For instance,
‘num_0’ is ‘insert’ and ‘enter’ is ‘return’. This approach helped greatly for anyone who finds the keypresses not taking any action as intended.

Thanks for your advice and assistance again, Michael!

We are having the same problem in the other direction. Psychopy now recognizes “quoteleft” as “apostrophe”, and “apostrophe” as “pound”. We triggered off “apostrophe” and that no longer works. It took a long time to find the problem because the logfile records the event key names, which are as expected but don’t match the keyboard key names for those two keys. We can work around it by telling the routine to trigger off “pound” instead for now. It would be good to find a real fix for this. Not sure if US vs UK keyboards have anything to do with it.