在Qt的keyPressEvent()和keyReleaseEvent()中,我试图获取key +修饰符的小键盘输入。

使用void MyWidget::keyPressEvent(QKeyEvent *evt)evt->key()提供键码(Qt::Key),evt->modifiers()提供键盘修饰符(QFlags)。

  • 对于所有“常规”键,我可以检测到所有需要的修饰符(shift,alt,ctrl)。
  • 对于numpad键,如果numLock已关闭,我将获得正确的修饰符。
  • 如果启用了NumLock,我会收到ctrl和alt,但不会移位。

  • 我发现shift key overrides NumLock

    下表显示了有关按键事件的所有可用Qt值的读数。
    要重现的按键:关闭NumLock,按下并释放num_5,然后按下并释放shift,然后按shift->按num_5->释放num_5->释放shift,然后将NumLock切换为开并重复相同的按键操作。
    table headers:
    natSC        = evt->nativeScanCode()
    natMods      = evt->nativeModifiers()
    kbdMods      = QGuiApplication::keyboardModifiers()
    queryKbdMods = QGuiApplication::queryKeyboardModifiers()
    
    NumLock |    Action     |   Event    | evt->key()  | evt->modifiers() | natSC | natMods  |   kbdMods    | queryKbdMods
    --------+---------------+------------+-------------+------------------+-------+----------+--------------+-------------
    off     | Num_5 down    | keyPress   | Key_Clear   | Keypad           |    76 |        0 | Keypad       | 0
    off     | Num_5 up      | keyRelease | Key_Clear   | Keypad           |    76 |        0 | Keypad       | 0
    off     | Shift down    | keyPress   | Key_Shift   | Shift            |    42 |        1 | 0            | Shift
    off     | Shift up      | keyRelease | Key_Shift   | 0                |    42 |        0 | Shift        | 0
    off     | Shift down    | keyPress   | Key_Shift   | Shift            |    42 |        1 | 0            | Shift
    off     | Num_5 down    | keyPress   | Key_Clear   | Shift+Keypad     |    76 |        1 | Shift+Keypad | Shift
    off     | Num_5 up      | keyRelease | Key_Clear   | Shift+Keypad     |    76 |        1 | Shift+Keypad | Shift
    off     | Shift up      | keyRelease | Key_Shift   | 0                |    42 |        0 | Shift        | 0
    --------+---------------+------------+-------------+------------------+-------+----------+--------------+-------------
    on      | NumLock dwn   | keyPress   | Key_NumLock | Keypad           |   325 | 16777728 | Keypad       | 0
    on      | NumLock up    | keyRelease | Key_NumLock | Keypad           |   325 | 16777728 | Keypad       | 0
    --------+---------------+------------+-------------+------------------+-------+----------+--------------+-------------
    on      | Num_5 down    | keyPress   | Key_5       | Keypad           |    76 |      512 | Keypad       | 0
    on      | Num_5 up      | keyRelease | Key_5       | Keypad           |    76 |      512 | Keypad       | 0
    on      | Shift down    | keyPress   | Key_Shift   | Shift            |    42 |      513 | 0            | Shift
    on      | Shift up      | keyRelease | Key_Shift   | 0                |    42 |      512 | Shift        | 0
    on      | Shift down    | keyPress   | Key_Shift   | Shift            |    42 |      513 | 0            | Shift
    on      | Num_5 down    | keyRelease | Key_Shift   | 0                |    42 |      512 | Shift        | 0
    on      | ...Num_5 down | keyPress   | Key_Clear   | Keypad           |    76 |      512 | Keypad       | 0
    on      | Num_5 up      | keyRelease | Key_Clear   | Keypad           |    76 |      512 | Keypad       | Shift
    on      | ...Num_5 up   | keyPress   | Key_Shift   | Shift            |    42 |      513 | 0            | Shift
    on      | Shift up      | keyRelease | Key_Shift   | 0                |    42 |      512 | Shift        | 0
    

    您可以看到该移位似乎是在numpad键事件之前释放的。
    问题在于,此“虚拟”换档事件看起来与常规换档完全相同。

    我理想的解决方案是在keyPressEvent()中获得真实的类次状态。
    作为一种解决方法,我很乐意测试是否在keyPressEvent()中启用了NumLock-然后,如果用户按下shift键并要求禁用NumLock,我可以给出警告。

    我正在使用Win7,但是解决方案应该是可移植的,例如使用Qt。有任何想法吗?

    我也会满意地回答“这不可能,因为...”。

    最佳答案

    获取NumLock状态
    此类任务由Windows API在Windows中执行,而在Unix系统中由X11负责。如果您想同时运行代码,则可以使用条件编译。

    #ifdef  _WIN32
    // Code for windows
    #else
    
    #ifdef  __linux__  // Systems based on the Linux kernel define this macro.
    // Code for linux.
    #else
    
    对于Windows,我建议: GetKeyState 函数
    摘自Visual Studio文档:
    GetKeyState
    返回值类型简短

    对于Linux:使用XLib。
    这是一个多平台示例代码:
    #ifdef _WIN32
    #include <Windows.h>
    #endif
    
    #ifdef _UNIX
    #include <X11/Xlib.h>
    #endif
    
    #include <iostream>
    
    bool is_numlock_activated()
    {
    #ifdef _WIN32
        short status = GetKeyState(VK_NUMLOCK);
        return status == 1;
    #endif
    
    #ifdef _UNIX
        Display *dpy = XOpenDisplay(":0");
        XKeyboardState x;
        XGetKeyboardControl(dpy, &x);
        XCloseDisplay(dpy);
        return x.led_mask & 2;
    #endif
    }
    
    int main()
    {
        if (is_numlock_activated())
            std::cout << "NumLock is activated.";
        else
            std::cout << "NumLock is deactivated.";
        std::cout << std::endl;
        return 0;
    }
    
    请注意,如果您没有运行X Server,则此代码在Linux上将不起作用。这是无关紧要的,因为您正在开发桌面应用程序,因此,如果可以运行您的应用程序,则可以运行此代码。
    获取轮类状态
    为此(对于Linux),除了 Xlib.h 之外,您还需要包括XKB扩展名。
    #ifdef _UNIX
        #include <X11/XKBlib.h>
    #endif
    
    bool is_shift_pressed()
    {
        bool result;
    #ifdef _WIN32
        short status = GetKeyState(VK_SHIFT);
        return status == 0xF0;  // Here we are checking the High order bit.
                                // See GetKeyState documentation above.
    #endif
    
    #ifdef _UNIX
        Display *dpy = XOpenDisplay(":0");
        XkbStateRec sate;
        XkbGetSate(dpy, XkbUseCoreKbd, &sate);
        XCloseDisplay(dpy);
        return state.mods & 1;                  // 1 is the mask for modifier SHIFT.
    #endif
    }
    
    我是从 superuser.com 的这个answer中获得的这一代码段(Linux的代码段)。

    09-06 05:37