如何告诉如果shift是按下numpad输入与NumLock上?或者至少获得NumLock状态
How to tell if shift is pressed on numpad input with NumLock on? Or at least get NumLock status?
在Qt的keyPressEvent()和keyReleaseEvent()我试图得到一个numpad输入的键+修饰符。
使用void MyWidget::keyPressEvent(QKeyEvent *evt)
, evt->key()
给出了键码(Qt::Key), evt->modifiers()
给出了键盘修饰符(QFlags)。
- 对于所有"常规"键,我可以检测到所有需要的修饰符(shift, alt, ctrl)。
- 对于numpad键,如果numLock是关闭的,我得到正确的修饰符。
- 如果NumLock是on,我收到ctrl和alt,但没有shift。
我发现shift键覆盖了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似乎在numpad键事件之前被释放。 我理想的解决方案是在keyPressEvent()中获得真正的移位状态。 我正在使用Win7,但解决方案应该是可移植的,例如使用Qt。 我也会满足于回答"这是不可能的,因为…"。
问题是,这个"虚拟的"移位释放事件看起来与常规的移位释放完全相同。
作为一种解决方案,我很乐意测试NumLock是否在keyPressEvent()中启用-然后如果用户按shift并要求禁用NumLock,我可以给出警告。
获取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 function
摘自Visual Studio文档:
GetKeyState
返回值类型SHORT
返回值指定虚拟密钥的状态,如下所示:
如果高阶位为1,则密钥为down;
如果低阶位为1,则打开密钥。如果某个键(如CAPS LOCK键)已打开,则该键处于切换状态。如果低阶位为0,则该键关闭且未切换。键盘上的切换键指示灯(如果有的话)将在该键被切换时亮,当该键未被切换时灭。
对于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
}
这段代码(linux的那一段)我从superuser.com的答案中得到的
根据Raydel Miranda的回答,我找到了一个简单而有效的解决方案。
作为参考,我将发布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
}
按numpad键会根据不同的状态产生不同的键码(在本例中使用中间键(标记为"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和shift状态。
*2你可以看到Qt::ShiftModifier在这里丢失了。
警告:奇怪的是,当isAutoRepeat()为true时,中间numpad键(并且只有这一个)没有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的状态,我们知道这个键是否是numpad键。现在我们可以确定Shift键的真实状态,如下所示(同样使用中间标记为"5"的键):
- 如果key不是numpad键(Qt::KeypadModifier未设置):
- 如果Qt::ShiftModifier设置为 ,则按Shift
- 其他
- 如果NumLock是off (IsNumLockOn()返回false)
- 如果Qt::ShiftModifier设置为 ,则按Shift
- 其他
- 如果keycode为Qt::Key_Clear
- 按下Shift键
- Else (keycode是Qt::Key_5)
- Shift未按下
- 如果keycode为Qt::Key_Clear
- 如果NumLock是off (IsNumLockOn()返回false)
// 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的警告,虽然)。
- 地图计数确实很重要,或者只是检查是否存在
- 错误 C2679:二进制"<<":未找到采用类型 'std::string_view' 的右侧操作数的运算符(或者没有可接受的转换)
- 为什么文件不是由 F 流创建的,或者即使它是输出只是垃圾值?
- C2678 二进制 '==':未找到采用 'Card' 类型左操作数的运算符(或者没有可接受的转换)
- 使用宏编译时使用用户定义的数学函数,或者仅使用 c++ 中标准数学库中的函数
- 如何仅使用一次固定<<设置精度(2)?或者至少恢复到默认行为?
- 我们应该如何使用枚举类进行索引(或者我们应该更好地避免这种情况)?
- GoogleTest 在使用 libgtest_main.a 时给出多个主定义,或者在不使用 libgtest_main
- 使用unique_ptr并返回引用,或者我应该使用shared_ptr并在需要时制作副本
- 我是否访问了已释放的内存,或者在这种情况下DrMemory报告不正确?
- C++类:virtual和override,或者两者都没有
- 二进制 '==':未找到采用 'Enemy' 类型左侧操作数的运算符(或者没有可接受的转换)
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 这种比较是否不一致(或者存在其他问题)?
- 只需要知道我在c ++中打印模式的方式是否有效,或者有另一种方法可以有效地做到这一点
- 这是系统资源吗?(或者我怎么知道我是否需要删除指针) - 在 C++ 中使用 C
- 计时器坏了或者其他什么的
- 我的随机生成器是否不工作,或者我决定人/骨架是否击中对手的方式是否有错误
- 为什么C++数组索引值是有符号的,而不是围绕size_t类型构建的(或者我错了)
- 如何告诉如果shift是按下numpad输入与NumLock上?或者至少获得NumLock状态