Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get printable name of any QKeyEvent key value

I'm using PyQt5. When I write a keyPressEvent handler, I would like to be able to print, for debugging purposes, a human-readable description of what keys were pressed. I want to be able to print such a thing no matter what, regardless of how many keys were pressed in the event, or whether they were modifiers or "regular" keys.

I have seen this previous question in which the accepted answer (using C++) suggests creating a QKeySequence and using its .toString method. I can do this like this:

def keyPressEvent(self, event):
    print("got a key event of ", QKeySequence(event.key()).toString())

However, this does not always work. For instance, if I press the Shift key, it will result in an encoding error when I try to output (or if I try to encode it to UTF-8). This seems to be because QKeySequence does not work on isolated modifier keys:

>>> QKeySequence(Qt.Key_Shift).toString().encode('unicode-escape')
b'\\u17c0\\udc20'

It gives gibberish instead of what I would expect, namely "Shift". It works if I use Qt.SHIFT (sort of, in that it gives "Shift+"), but that is of no use, because Qt.SHIFT is not what I get in event.key() if I press the Shift key.

How can I get Qt to give me a printable representation of anything that might ever be the value of event.key(), where event is a QKeyEvent?

like image 974
BrenBarn Avatar asked Jun 14 '18 06:06

BrenBarn


1 Answers

To deal with the specific question:

How can I get Qt to give me a printable representation of anything that might ever be the value of event.key(), where event is a QKeyEvent?

The first things to note is that event.key() returns an int, rather than a Qt.Key. This is simply because the key can be any unicode value whatsoever. As a consequnce of this, it is not really feasible to give a printable representation of literally anything, since not every unicode key is printable, and it is impractical to enumerate them all.

The only API Qt provides for this is the QKeySequnce class. However, as you have found, it does not handle all inputs in the way you want. So you will have to roll your own solution.

It might be tempting to use QMetaEnum to convert key values to their names wherever possible. However, this won't work here, because there is no staticMetaObject for the Qt object, and PyQt does not provide anything like qt_getQtMetaObject. It also doesn't currently implement QMetaEnum.fromType (although this is likely to change in future versions).

So the only available solution is to use normal python introspection to build a mapping, and build up a printable representation from that.

Here is a basic demo (only tested on Linux):

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget

keymap = {}
for key, value in vars(Qt).items():
    if isinstance(value, Qt.Key):
        keymap[value] = key.partition('_')[2]

modmap = {
    Qt.ControlModifier: keymap[Qt.Key_Control],
    Qt.AltModifier: keymap[Qt.Key_Alt],
    Qt.ShiftModifier: keymap[Qt.Key_Shift],
    Qt.MetaModifier: keymap[Qt.Key_Meta],
    Qt.GroupSwitchModifier: keymap[Qt.Key_AltGr],
    Qt.KeypadModifier: keymap[Qt.Key_NumLock],
    }

def keyevent_to_string(event):
    sequence = []
    for modifier, text in modmap.items():
        if event.modifiers() & modifier:
            sequence.append(text)
    key = keymap.get(event.key(), event.text())
    if key not in sequence:
        sequence.append(key)
    return '+'.join(sequence)

class Window(QWidget):
    def keyPressEvent(self, event):
        print(keyevent_to_string(event))

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    sys.exit(app.exec_())
like image 60
ekhumoro Avatar answered Nov 05 '22 13:11

ekhumoro