如何判断NumLock上的小键盘输入是否按下了shift?或者至少获得NumLock身份?

时间:2014-07-18 10:11:39

标签: c++ qt keyboard

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

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

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

我发现了shift key overrides NumLock

下表显示了关键事件的所有可用Qt值的读数 重现击键:关闭NumLock,按下并释放num_5,然后按下并释放shift,然后按shift - >按num_5 - >发布num_5 - >释放班次,然后将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           

你可以看到这种转变似乎是在小键盘事件发生之前发布的 问题是这个“虚拟”转移发布事件看起来完全与常规转移发布相同。

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

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

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

2 个答案:

答案 0 :(得分:3)

获取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

返回值类型简短

  

返回值指定指定虚拟键的状态,如下所示:

     
      
  1. 如果高位为1,则键为关闭;否则,它就结束了。

  2.   
  3. 如果低位为1,则切换键。如果键打开,则会切换一个键,例如CAPS LOCK键。如果低位为0,则键关闭并取消加盖。当键切换时,键盘上的切换键指示灯(如果有)将打开,而当键未被切换时,键将关闭。

  4.   

对于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 one)。

答案 1 :(得分:1)

跟进Raydel Miranda's answer我发现了一个简单而有效的解决方案。

作为参考,我将发布Raydel Miranda的解决方案以获取当前的NumLock状态:

#ifdef _WIN32
#include <Windows.h>
#endif

#ifdef _UNIX
#include <X11/Xlib.h>
#endif

bool IsNumLockOn()
{
#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
}

小键盘按键会根据不同的状态产生不同的按键代码 (对于此示例,使用中间键(标记为“5”)):

NumLock | Shift | Key           | Modifiers
  off   |  off  | Qt::Key_Clear | Qt::KeypadModifier
  off   |   on  | Qt::Key_Clear | Qt::KeypadModifier + Qt::ShiftModifier
   on   |  off  | Qt::Key_5  *1 | Qt::KeypadModifier
   on   |   on  | Qt::Key_Clear | Qt::KeypadModifier *2

* 1注意密钥代码如何根据NumLock和移位状态而变化 * 2你可以看到这里缺少Qt :: ShiftModifier。

警告:奇怪的是,当isAutoRepeat()为真时,中间小数键(并且只有这一个)没有设置Qt :: KeypadModifier标志!

为了比较,这里是数字键“5”(字母键上方)的相同结果表:

NumLock | Shift | Key           | Modifiers
  off   |  off  | Qt::Key_5     | Qt::NoModifier
  off   |   on  | Qt::Key_5     | Qt::ShiftModifier
   on   |  off  | Qt::Key_5     | Qt::NoModifier
   on   |   on  | Qt::Key_5     | Qt::ShiftModifier

我们知道NumLock状态,我们知道密钥是否是一个小键盘密钥。现在我们可以按如下方式确定Shift键的真实状态(再次使用标记为“5”的中间键):

  • 如果key不是小键盘键(未设置Qt :: KeypadModifier):
    • 如果设置了Qt :: ShiftModifier,则按下Shift键
  • 否则
    • 如果NumLock关闭(IsNumLockOn()返回false)
      • 如果设置了Qt :: ShiftModifier,则按下Shift键
    • 否则
      • 如果keycode是Qt :: Key_Clear
        • 按下Shift键
      • 否则(键码为Qt :: Key_5)
        • 未按下Shift键

因此,我们可以使用此代码段确定正确的班次状态:

// using QKeyEvent *evt (e.g. in keyPressEvent())
Qt::Key key = Qt::Key(evt->key());
Qt::KeyboardModifiers mods = evt->modifiers();

bool NumLockOn = IsNumLockOn(); // see Raydel Miranda's answer

// set shift pressed if Qt::ShiftModifier is found
bool ShiftPressed = mods.testFlag(Qt::ShiftModifier);

// this list contains all the keycodes 'changed' on shift
QList<Qt::Key> lNumPadKeys = QList<Qt::Key>() << Qt::Key_Insert 
    << Qt::Key_End << Qt::Key_Down << Qt::Key_PageDown 
    << Qt::Key_Left << Qt::Key_Clear << Qt::Key_Right 
    << Qt::Key_Home << Qt::Key_Up << Qt::Key_PageUp 
    << Qt::Key_Delete;
// if shift wasn't pressed, the keycodes would be Qt::Key_0, Qt::Key_1, ..., Qt::Key_Comma instead

// correct the ShiftPressed value on NumLock + keypad input
if(mods.testFlag(Qt::KeypadModifier) && NumLockOn && !ShiftPressed)
{
    // keycode appears in the list -> shift must have been pressed
    // these keycodes can only result from keypad keys with NumLock on if shift was pressed 
    ShiftPressed = lNumPadKeys.contains(key);
}

瞧:

ShiftPressed现在设置为真正的移位状态(请注意关于Qt :: Key_Clear的警告)。