1

I first noticed this when I was using IDLE for a task, but it shows up in my own tkinter programs as well. There are two sets of arrow keys on many keyboards, the regular arrow keys that are often next to the spacebar row, and the arrow keys on the key pad, which double as number entry keys when numlock is turned on. In tkinter the two sets produce different event names when pressed.

My issue is that when using various built in tkinter widgets, such as Text, and Entry, the default tkinter behaviour appears to be to ignore the key pad events. I would like to have my program treat both sets the same.

I am hoping that there is a relatively simple method of accomplishing this, such as setting a variable in the module after I import it, or binding the KP_* events to functions which then emit corresponding regular arrow key events back to my widgets. However the only thing I have found that even acknowledges the existence of this quirk is this other, unanswered, Stack Overflow question.

Community
  • 1
  • 1
Nick
  • 258
  • 1
  • 3
  • 7
  • 10 key up-arrow is something like "KP_Up" and normal up-arrow is just "Up" for event.keysym when a key a pressed. Hope this gives you enough to search for a good answer. One listing of keysym and keycodes is on p65 & 66 (in French) at http://ateliers.mse.free.fr/tkinter/tkinter-ref5.pdf –  May 24 '16 at 05:32
  • So you can bind multiple key events to an Entry for example, going to the same function. You can also capture any key event and filter the ones you want the program to respond to. A simple example would help. If you want 10 key up-arrow to move up one line in a Text, you may have to program that yourself, i.e. move the cursor one line up. –  May 24 '16 at 05:40
  • Thank you, I am aware of the KP_arrow vs arrow event names. I have considered programming in the alterations to the widget state myself, although that would lead to another question (which I would need to research before asking) about how to access the internal state of the widgets. – Nick May 24 '16 at 19:36

1 Answers1

0

So after digging around some more I created this function to forward events to another event name. It probably has a number of potential improvements, but this is the general idea.

The function is called on the widget that you want to remap events for, calling it on the root widget will remap events for at least all other widgets that haven't been bound to anything else.

One can change which events are translated to which other events using the kmap variable. If you do, be sure you don't have any loops in your forwarding, this function does zero sanity checking.

I have only copied the modifier key state from the original event here, since that was enough for it to do what I wanted, but in some circumstances you could need to copy more information.

I found the key event information from the link in the Python docs for tkinter. I got the event_generate() and event property information from the same source.

Be aware that this solution, while it worked for my simple issue, may well require much more care when applied to a more complicated situation.

def fix_keypad(widget):
    kmap = {
        '<KP_Left>': '<Left>',
        '<KP_Right>': '<Right>',
        '<KP_Up>': '<Up>',
        '<KP_Down>': '<Down>',
        '<KP_Home>': '<Home>',
        '<KP_End>': '<End>',
        '<KP_Next>': '<Next>',
        '<KP_Prior>': '<Prior>',
        '<KP_Enter>': '<Return>',
    }
    for i in kmap:
        def mfunc(event, key=i):
            widget.event_generate(kmap[key], **{'state': event.state})
        widget.bind(i, mfunc)
Nick
  • 258
  • 1
  • 3
  • 7