psychopy.org | Reference | Downloads | Github

Accents in Text Input

OS : Windows 10
PsychoPy version : 1.90.1
What are you trying to achieve?: I have added a code component to my experiment, where participants can type the answer at the same time it is displayed. This works perfectly, however, it does not support keys with accents (á, é, í, ó, ú). My experiment is in Spanish, so I need to fix this.
I really have no idea how to do this, so any help is welcomed.

My code component looks like this:

Begin experiment:

inputText = " "

Begin Routine:

theseKeys=""
shift_flag = False
phase1_input.alignHoriz =‘left’

Each frame:

n= len(theseKeys)
i = 0

while i < n:

if theseKeys[i] == 'space':
    # pressing SPACE means time to stop
    continueRoutine = False
    break

elif theseKeys[i] == 'backspace':
    inputText = inputText[:-1]  # lose the final character
    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

End routine:

thisExp.addData(‘inputText’, inputText)
inputText=""

Thanks in advance!

I hope you have a very good weekend!

Yeray

Also, I’ve tried using the code component written by @dvbridges from here, but it is not working either. I guess that I need to change something so I can type those characters. Normally, in my keyboard I press the apostrophe key (or ‘\xb4’) and then when I press any vowel it appears the special character (e.g., á, é, í, ó, ú).

If anyone knows how I can solve this, I would appreciate it very much. I’ve been going crazy for weeks.

Thank you!

Yeray.

Can anyone help? I have the same problem with an experiment in French. I know the keyboard component works differently in the new version of PsychoPy, so we need to use the new script @dvbridges is talking about here Displaying Typed Words on Screen during a Routine, but I can’t seem to be able to adapt it in order to for it to accept accents as the old script did in older versions of PsychoPy.

Thank you,

Marie

Hi @LittleMary,

I don’t know if it’ll help. It’s not the best solution, but it worked for me.

The first thing I did was change the keyboard setting to English. So even if your physical keyboard is in French (or Spanish in mi case), it will work like an English one.

To change the keyboard language: https://it.iwi.unisg.ch/2017/09/19/how-to-change-the-system-language-across-your-whole-windows-10-pc/

Now let’s add the accents. The English keyboard doesn’t have accents, so we have to create them. In the case of the Spanish keyboard, I needed the following characters: “á”, “é”, “í”, “ó”, “ú”, and “ñ”.

On a Spanish keyboard, to add any accented word what we normally do is press the apostrophe key (“´”), and then the vowel. (example: “´” + “a” = “á”).

The English keyboard doesn’t do this so we’re going to code it.

My code component looks like this:

Begin experiment:

inputText_test1 = u""

Begin Routine:

theseKeys=u""
shift_flag = False
phase2_input.alignHoriz ='left'

Each frame:


n= len(theseKeys)
i = 0

while i < n:

    if (theseKeys[i] == 'space'and inputText_test1 != ""):
        # pressing SPACE means time to stop, but only if a word is written
        continueRoutine = False
        break

    elif theseKeys[i] == 'backspace':
        inputText_test1 = inputText_test1[:-1]  # lose the final character
        i = i + 1

    elif theseKeys[i] in ['semicolon']: #this is for the ñ character
        inputText_test1 = inputText_test1 + u'\xf1'
        i = i + 1

    elif theseKeys[i] == 'apostrophe':
        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:
                if 'a' in theseKeys[i]:
                    inputText_test1 += u'á'
                    shift_flag = False
                else:
                    if 'e' in theseKeys[i]:
                        inputText_test1 += u'é'
                        shift_flag = False
                    else:
                        if 'i' in theseKeys[i]:
                            inputText_test1 += u'í'
                            shift_flag = False
                        else:
                            if 'o' in theseKeys[i]:
                                inputText_test1 += u'ó'
                                shift_flag = False
                            else:
                                if 'u' in theseKeys[i]:
                                    inputText_test1 += u'ú'
                                    shift_flag = False
                                else:
                                    inputText_test1 += unichr( ord(theseKeys[i]) + ord('B') + ord('B'))
                                    shift_flag = False
            else:
                inputText_test1 += theseKeys[i]
                i = i + 1

End routine:

# store the response input for initial test
thisExp.addData('inputText_test1', inputText_test1)
inputText_test1=""

Again, this solution is pretty crappy, but it worked for me :blush:

Good luck!

Yeray

Thanks for your answer. This is well thought, but sadly won’t work for me. The experiment I need accents in will be completed by young kids in whole classrooms. They only know one keyboard configuration, which includes pressing (for example) the simple “é” key available on their physical keyboard to type an “é”. I can’t change the keyboard configuration and ask them to learn to type 'e or something similar to get an é. It would interfere greatly with the response times.

I sure hope the developers will soon suggest a real solution like a working script for what we need, or will make an update to PsychoPy in order to take accents into account…

I’m not an expert in this by any means, but the Keyboard class works by mapping the hardware code for each physical key on a keyboard to a character name. e.g. on a Mac, key code 4 corresponds to a, 5 to b, 44 to space etc, while on Windows, 65 is a, 66 is b, 32 is space, and so on.

These mappings must necessarily differ on non-US English keyboards, and even the US and British English keyboards are physically different in some key mappings. If you look inside the PsychoPy Keyboard code (which was ported from the Psychtoolbox project), you’ll see that it only provides three mappings (Windows, Mac and Linux), and doesn’t seem to provide for any internationalisation within those:

So I guess we should really aim to extend this, so that different mappings are used depending on the keyboard used, in addition to the operating system.

Where does this leave you? Well I guess you could actually just edit your local keyboard.py inside your PsychoPy app (make a copy before touching it, of course). The way to know what values to use would be to switch to the Coder view and from the Demos menu, run the input -> keyboardNEW.py demo. When pushing a key, you should see the numeric code that for Psychtoolbox (PTB) corresponds to that physical key.

e.g. I have no idea what it will be on your keyboard, but push the é key and see what number shows up (presumably the key name will be wrong). Then edit the mapping table in keyboard.py that corresponds to that number to now show the é character instead of what it currently thinks that key produces.

Could you try that for one or two characters and let us know if it works? I’m sure somewhere on the internet, tables of these values must exist, but my google skills aren’t revealing them to me at the moment, so a bit of manual checking like this might be what is required to get things to work for you at least.

1 Like

Thanks for this answer. I’ll sure try it out and let you know how it goes. Though I’m working on a Mac and I can’t seem to be able to find that keyboard.py file I’d need to modify… I read this post Keyboard Component cannot get response from Num KeyPad, but it’s not use since it’s for Windows…

  • ⌘-click on PsychoPy’s icon in the Dock to reveal it in the Finder.
  • Right click on the PsychoPy app and select “Show package contents”
  • Navigate to PsychoPy3.app⁩ ▸ ⁨Contents⁩ ▸ ⁨Resources⁩ ▸ ⁨lib⁩ ▸ ⁨python3.6⁩ ▸ ⁨psychopy⁩ ▸ ⁨hardware⁩ ▸ keyboard.py
  • Duplicate that file (so you have something to go back to if needed) and try editing the original.

Awesome! I found it, thanks a lot!

Sadly, I can’t get the values via the keyboardNEW.py demo as you suggested. It seems to have the same restrictions. When I press one of the problematic keys, nothing changes in the graphic interface where I should get the name of the key and its value, and I get error messages in the output.

For example, when I press the é key, this is what I get :

Traceback (most recent call last):
File "ctypes/callbacks.c", line 234, in ‘calling callback function’
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/libs/darwin/cocoapy/runtime.py”, line 1121, in objc_method
result = f(py_self, *args)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/cocoa/pyglet_view.py”, line 164, in pygletKeyUp

symbol = getSymbol(nsevent)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/cocoa/pyglet_view.py”, line 52, in getSymbol
return charmap[chars[0].upper()]
KeyError: ‘\xc8’

When pressing other problematic keys, I also get messages like this one :

Traceback (most recent call last):
File "ctypes/callbacks.c", line 234, in ‘calling callback function’
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/libs/darwin/cocoapy/runtime.py”, line 1121, in objc_method
result = f(py_self, *args)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/cocoa/pyglet_view.py”, line 160, in pygletKeyDown

self._window.dispatch_event(‘on_key_press’, symbol, modifiers)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/init.py”, line 1232, in dispatch_event
if EventDispatcher.dispatch_event(self, *args) != False:
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/event.py”, line 371, in dispatch_event
event_type, args, getattr(self, event_type))
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/event.py”, line 367, in dispatch_event
if getattr(self, event_type)(*args):
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/psychopy/event.py”, line 215, in _onPygletKey
symbol).lower() # convert symbol into key string
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/key.py”, line 148, in symbol_string
if symbol < 1 << 32:
TypeError: ‘<’ not supported between instances of ‘NoneType’ and ‘int’

Hi, thanks for working through this. The good news might be that these errors seem to be coming from the old event module, which uses the third party pyglet package to handle keypresses. So we should try commenting out those lines from keyboardNEW.py so that only calls to the new Psychtoolbox keyboard code get made. So in keyboardNEW.py, can you comment out these lines:

pygKey = event.waitKeys()[0]
pygRT = timer.getTime()

and:

txt += ('waitKeys: {},\n    RT={}\n\n'
        .format(pygKey, pygRT))

and:

txt += ('new was {:.3f}ms faster'
        .format((pygRT-ptbKey.rt)*1000))

and:

if pygKey == 'escape':
    continuing = False

and let us know what happens? (NB pushing escape to end won’t work anymore, so you’ll need to exit by pushing the red “stop” button in the Coder window)

Sorry for the remote debugging like this: I don’t have a non-US keyboard anywhere.

So this is what I’m getting now (the graphic interface appears and then disappears in less than a second) :

Traceback (most recent call last):
File “/Users/marie/Documents/keyboardNEWmodified.py”, line 25, in
5.3047 WARNING Monitor specification not found. Creating a temporary one…
ptbKey = kb.getKeys(waitRelease=False)[0]
IndexError: list index out of range

Ahh, OK, that is probably because we took out the earlier call to event.waitKeys()

Try replacing the line with the error with something like:

ptbKey = None

while ptbKey is None:
    ptbKey = kb.getKeys(waitRelease=False)
        if len(ptbKey):
            ptbKey = ptbKey[0]
        else:
            core.wait(0.001)

so it will wait until a key is pressed.

I’m getting the same error message and still nothing appearing in the graphic interface :(.

Traceback (most recent call last):
File "ctypes/callbacks.c", line 234, in ‘calling callback function’
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/libs/darwin/cocoapy/runtime.py”, line 1121, in objc_method
result = f(py_self, *args)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/cocoa/pyglet_view.py”, line 164, in pygletKeyUp

symbol = getSymbol(nsevent)
File “/private/var/folders/l3/ylchv7yx6bg5c4jxt0chldk00000gv/T/AppTranslocation/5D374AED-76DE-4AF3-9007-CB76C8989EAF/d/PsychoPy3.app/Contents/Resources/lib/python3.6/pyglet/window/cocoa/pyglet_view.py”, line 52, in getSymbol
return charmap[chars[0].upper()]
KeyError: ‘\xc9’

OK, I’m getting a bit out of my depth here - might need to seek the opinion of someone like @richard who might be more familiar with issues of using non-US keyboards?

It surprises me that this hasn’t been an issue for others before, as it seems so fundamental.

What happens if you switch your Mac OS keyboard settings to US? I find that if I switch OS settings to French (with my physical US keyboard), the keyboardNEW.py correctly swaps the q and a keys, but doesn’t register anything when I press the 7 key (which should correspond to é, I think)…

When I switch to English (US) keyboard settings, I get the values available in the keyNamesMac dictionary of the file keyboard.py. By the way, I have a Canadian keyboard with French Canadian setting, but I’m working on PsychoPy experiments that will be used in classrooms in Switzerland (I’ll have someone there get the values of the keyboards they use if we manage to find a way to get those values).

About this not being an issue already, I must say that the older version of the keyboard in PsychoPy was allowing me to type accentuated letters on screen. I never managed to make it work on Pavlovia, though. We interacted about that here Accents in onscreen user keyboard input.

@LittleMary I had a similar problem. I need my participants to type in solutions to anagrams in Russian. The code I used for offline experiments worked fine (as you mention in the comment below, the older version of the keyboard allowed it). Unfortunately, I couldn’t make this code work online on Pavlovia.
I then used code from here and tried many options to make the use of Cyrillic letters possible, but only the following two worked for me.

For python
The solution is quite simple. I’ve just addressed to a different method.

# Begin routine
modify = False
text.text = ''
event.clearEvents('keyboard')
kb = keyboard.Keyboard() # here is something new

# Each frame
keys = kb.getKeys(waitRelease=False) # and here are some changes
if len(keys):
    if 'space' in keys:
        text.text = text.text + ' '
    elif 'backspace' in keys:
        text.text = text.text[:-1]
    elif 'lshift' in keys or 'rshift' in keys:
        modify = True
    elif 'return' in keys:
        continueRoutine = False
    else:
        if modify:
            text.text = text.text + keys[0].upper()
            modify = False
        else:
            text.text = text.text + keys[0].name

# End routine
thisExp.addData("typedWord", text.text)

This code reads my keyboard configuration (Russian or English) and uses corresponding letters (Cyrillic or Latin).

For JavaScript
I didn’t manage to find a simple solution for JS to launch the experiment online. So, my variant here is pretty much the same as @Yeray_Mera suggested. I’ve just manually assigned particular Cyrillic symbol to each Latin symbol based on their layout.

// Begin routine and End routine are the same as in original code by @dvbridges
// Each frame
let theseKeys = psychoJS.eventManager.getKeys();

if (theseKeys.length > 0) { 
  textAdd = theseKeys[theseKeys.length-1]; 
  }

// To make it short, I put not the whole "alphabet", just an example
if (textAdd === 'q') {
    textAdd = 'й'
    text.text = text.text + textAdd;
} else if (textAdd === 'w') {
    textAdd = 'ц';
    text.text = text.text + textAdd;
} else if (textAdd === 'e') {
    textAdd = 'у';
    text.text = text.text + textAdd;
} else if (textAdd === 'r') {
    textAdd = 'к';
    text.text = text.text + textAdd;
} else if (textAdd === 't') {
    textAdd = 'е';
    text.text = text.text + textAdd;
} else if (textAdd === 'y') {
    textAdd = 'н';
    text.text = text.text + textAdd;
} else if (textAdd === 'u') {
    textAdd = 'г';
    text.text = text.text + textAdd;
} else if (textAdd === 'bracketleft') {
    textAdd = 'х';
    text.text = text.text + textAdd;
} else if (textAdd === 'bracketright') {
    textAdd = 'ъ';
    text.text = text.text + textAdd;
} else if (textAdd === 'semicolon') {
    textAdd = 'ж';
    text.text = text.text + textAdd;
} else if (textAdd === 'apostrophe') {
    textAdd = 'э';
    text.text = text.text + textAdd;
} else if (textAdd === 'comma') {
    textAdd = 'б';
    text.text = text.text + textAdd;
} else if (textAdd === 'period') {
    textAdd = 'ю';
    text.text = text.text + textAdd;
} else if (textAdd === 'quoteleft') {
    textAdd = 'ё';
    text.text = text.text + textAdd;
} else if (textAdd === 'return') {
    textAdd = ''; 
    continueRoutine = false;
} else if (textAdd === 'space') {
    textAdd = ' ';
} else if (textAdd === 'backspace') {
    text.text = text.text.slice(0, -1);
    textAdd = undefined;
} else {
    textAdd = undefined;
}

This solution is far from perfect, but it works. Hope, it’ll help you!

2 Likes

Hi, I was with the same problem and figured it out. Probably it is not the best way, but it worked!

Begin routine:

> modify = False
> text.text = ''
> event.clearEvents('keyboard')
> til = False
> circunflexo = False
> agudo = False

Each frame

> keys = event.getKeys()
> if len(keys):
>     if 'space' in keys:
>         text.text = text.text + ' '
>     elif 'backspace' in keys:
>         text.text = text.text[:-1]
>     elif '~' in keys:
>         til = True
>     elif '´' in keys:
>         agudo = True
>     elif '^' in keys:
>         circunflexo = True
>     elif 'a' in keys:
>         if til:
>             text.text = text.text + 'ã'
>             til = False
>         elif agudo:
>             text.text = text.text + 'á'
>             agudo = False
>         else:
>             text.text = text.text + 'a'
>     elif 'o' in keys:
>         if til:
>             text.text = text.text + 'õ'
>             til = False
>         elif agudo:
>             text.text = text.text + 'ó'
>             agudo = False
>         elif circunflexo:
>             text.text = text.text + 'ô'
>             circunflexo = False
>         else:
>             text.text = text.text + 'o'
>     elif 'e' in keys:
>         if agudo:
>             text.text = text.text + 'é'
>             agudo = False
>         elif circunflexo:
>             text.text = text.text + 'ê'
>             circunflexo = False
>         else:
>             text.text = text.text + 'e'
>     elif 'i' in keys:
>         if agudo:
>             text.text = text.text + 'í'
>             agudo = False
>         else:
>             text.text = text.text + 'i'
>     elif 'u' in keys:
>         if agudo:
>             text.text = text.text + 'ú'
>             agudo = False
>         else:
>             text.text = text.text + 'u'
>     elif 'lshift' in keys or 'rshift' in keys:
>         modify = True
>     elif 'return' in keys:
>         continueRoutine = False
>     else:
>         if modify:
>             text.text = text.text + keys[0].upper()
>             modify = False
>         else:
>             text.text = text.text + keys[0]

End routine

thisExp.addData("typedWord", text.text)

2 Likes