In Qt's keyPressEvent() and keyReleaseEvent() I'm trying to get a numpad input of key + modifier.
Using void MyWidget::keyPressEvent(QKeyEvent *evt)
, evt->key()
gives the keycode (Qt::Key) and evt->modifiers()
gives the keyboard modifiers (QFlags).
I found out that the shift key overrides NumLock.
Below table shows a readout of all available Qt values about the key events.
Keystrokes to reproduce: having NumLock off, press and release num_5, then press and release shift, then press shift -> press num_5 -> release num_5 -> release shift, then toggle NumLock to on and repeat the same key presses.
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
You can see that shift seems to be released prior to the numpad key event.
The problem is that this "virtual" shift release event looks exactly the same as a regular shift release.
My ideal solution would be to get the true shift status in keyPressEvent().
As a workaround I'd be happy to test if NumLock is enabled in keyPressEvent() - then I could give a warning if the user presses shift and ask to disable NumLock.
I'm using Win7, but the solution should be portable, e.g. using Qt. Any ideas?
I'd also be content with an answer "This is not possible because...".
Such task is carried en Windows by the Windows API while in Unix systems is X11 who is in charge. If you want your code run in both you can use conditional compilation.
#ifdef _WIN32
// Code for windows
#else
#ifdef __linux__ // Systems based on the Linux kernel define this macro.
// Code for linux.
#else
For windows I'll recommend: GetKeyState function
Taken from Visual Studio Documentation:
GetKeyState
Return value type SHORT
The return value specifies the status of the specified virtual key, as follows:
If the high-order bit is 1, the key is down; otherwise, it is up.
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.
And for linux: Use XLib.
Here is a multiplatform example code:
#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;
}
Be aware that this code wont work on linux if you don't have the X Server running. This is not relevant since you're developing a desktop app, hence if you can run your app, you can run this code.
For this (for linux), besides Xlib.h, you will need to include the XKB extension.
#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
}
This snippet of code (the linux one) I took it from this answer from superuser.com.
Following up on Raydel Miranda's answer I found a simple and working solution.
For reference, I'll post Raydel Miranda's solution to obtain current NumLock status:
#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
}
The numpad key press results in different key codes depending on different states (using the middle key (labeled "5") for this example):
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 Mind how the key code changes depending on NumLock and shift state.
*2 You can see the Qt::ShiftModifier is missing here.
caveat: Strangely enough, the middle numpad key (and only this one) has no Qt::KeypadModifier flag set when isAutoRepeat() is true!
For comparison, here is the same result table for the number key "5" (above the letter keys):
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
We know the NumLock status and we know if the key was a numpad key. Now we can determine the true status of the Shift key as follows (again using the middle key labeled "5"):
So we can determine the correct shift status with this code snippet:
// 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);
}
Voilà:
ShiftPressed is now set to the true shift status (mind the caveat about Qt::Key_Clear, though).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With