c++ - 如何判断是否在启用 NumLock 的情况下在小键盘输入上按下了 shift?或者至少获得 NumLock 状态?

标签 c++ qt keyboard

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

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

  • 对于所有“常规”键,我可以检测到所有需要的修饰符(shift、alt、ctrl)。
  • 对于小键盘键,如果 numLock 关闭,我会得到正确的修饰符。
  • 如果 NumLock 打开,我会收到 ctrl 和 alt,但不会收到 shift。

我发现 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           

您可以看到 shift 似乎在小键盘键事件之前被释放。
问题在于,这个“虚拟”轮类发布事件看起来完全与常规轮类发布相同。

我理想的解决方案是在 keyPressEvent() 中获取真正的转换状态。
作为解决方法,我很乐意测试是否在 keyPressEvent() 中启用了 NumLock - 如果用户按下 shift 并要求禁用 NumLock,我可以发出警告。

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

我也会满足于回答“这是不可能的,因为......”。

最佳答案

获取 NumLock 状态

此类任务在 Windows 中由 Windows API 执行,而在 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 文档:

获取 key 状态

返回值类型SHORT

The return value specifies the status of the specified virtual key, as follows:

  1. If the high-order bit is 1, the key is down; otherwise, it is up.

  2. If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, and off when the key is untoggled.

对于 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 服务器,此代码将无法在 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
}

这段代码(linux 代码)是我从 answer 中提取的来自 superuser.com

关于c++ - 如何判断是否在启用 NumLock 的情况下在小键盘输入上按下了 shift?或者至少获得 NumLock 状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24822505/

相关文章:

c++ - 在 replyFinished() 中获取发起 QNetworkRequest 的函数

c++ - 不强调 QTextBrowser 中的超链接

android - 软键盘打开时 DialogFragment 不会上升

linux - 绑定(bind)具有相同 key 代码的 key ,不会丢失第二个 key

c++ - 英特尔 <math.h> vs C <math.h>?

Qt 不承认声明的类

c++ - QObject* 和 void* 之间的转换

ios - 如何解决 Swift 3 中的键盘问题?

c++ - C++继承更改成员

c++ - 优化器会根据编译时常量推导出数学表达式吗?