一、看了一圈没有找到可以设置小键盘的情况。
这两天在研究快捷键的使用。发现qt的里的快捷键不是全局的。找了两个第三方快捷键QHotKey,还有一个QxtGlobalShortcut。但是这两个都不能设置小键盘的数字。
比如QKeySequenceEdit (Ctrl+1) 这个快捷键,这个1只会响应主键盘的数字1对应的键盘码是0x31.而小键盘的1键盘码是0x61.
所以就算是设置成功了,再按快捷键的时候也是响应的主键盘的,按小键盘没用。
二、准备工作
0、键盘码对应关系
Qt中的Qt::key0-9 对应的是0x30-0x39
windows 下 主键盘上的数字0-9对应的是0x30-0x39 刚好和qt的一致。
windwos下 小键盘上的数字0-9对应的键盘码是0x60-0x69.
看了Qt的Qt::key 枚举发现并没有 VK_NUMPAD0=0x60的枚举值。
仅有的0x60也是对应 Qt::Key_QuoteLeft并不是对应的小键盘的VK_NUMPAD0, 0x61-0x69是缺失状态。
还有一点需要注意的是,在看qt枚举的时候,发现在键盘(NumLock)不选中的情况下的小键盘0-9的码值。
对应的QKeySequenceEdit表现为:
alt+1->
alt+9->
其他的可以自己试试。但是这个不是我想要的。
三、初步思考
是不是qt没有枚举全windows键盘码,导致第三方库都办法响应小键盘的0-9?如果我把小键盘码搞进去是不是就能响应了?
接下来就查看源码。
四、源码解析,初步分析
在注册快捷键的时候用的是这两个函数 setShortcut 以及他的重载。
//! A class to define global, systemwide Hotkeys
class QHOTKEY_SHARED_EXPORT QHotkey : public QObject
{Q_OBJECTfriend class QHotkeyPrivate;//! Specifies whether this hotkey is currently registered or notQ_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged)//! Holds the shortcut this hotkey will be triggered onQ_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut)public://! Defines shortcut with native keycodesclass QHOTKEY_SHARED_EXPORT NativeShortcut {public://! The native keycodequint32 key;//! The native modifiersquint32 modifier;//! Creates an invalid native shortcutNativeShortcut();//! Creates a valid native shortcut, with the given key and modifiersNativeShortcut(quint32 key, quint32 modifier = 0);//! Checks, whether this shortcut is valid or notbool isValid() const;//! Equality operatorbool operator ==(const NativeShortcut &other) const;//! Inequality operatorbool operator !=(const NativeShortcut &other) const;private:bool valid;};//! Constructorexplicit QHotkey(QObject *parent = Q_NULLPTR);//! Constructs a hotkey with a shortcut and optionally registers itexplicit QHotkey(const QKeySequence &shortcut, bool autoRegister = false, QObject *parent = Q_NULLPTR);//! Constructs a hotkey with a key and modifiers and optionally registers itexplicit QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false, QObject *parent = Q_NULLPTR);//! Constructs a hotkey from a native shortcut and optionally registers itexplicit QHotkey(const NativeShortcut &shortcut, bool autoRegister = false, QObject *parent = Q_NULLPTR);//! Destructor~QHotkey();//! READ-Accessor for QHotkey::registeredbool isRegistered() const;//! READ-Accessor for QHotkey::shortcut - the key and modifiers as a QKeySequenceQKeySequence shortcut() const;//! READ-Accessor for QHotkey::shortcut - the key onlyQt::Key keyCode() const;//! READ-Accessor for QHotkey::shortcut - the modifiers onlyQt::KeyboardModifiers modifiers() const;//! Get the current native shortcutNativeShortcut currentNativeShortcut() const;public slots://! WRITE-Accessor for QHotkey::registeredbool setRegistered(bool registered);//! WRITE-Accessor for QHotkey::shortcutbool setShortcut(const QKeySequence &shortcut, bool autoRegister = false);//! WRITE-Accessor for QHotkey::shortcutbool setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false);//! RESET-Accessor for QHotkey::shortcutbool resetShortcut();//! Set this hotkey to a native shortcutbool setNativeShortcut(NativeShortcut nativeShortcut, bool autoRegister = false);signals://! Will be emitted if the shortcut is pressedvoid activated(QPrivateSignal);//! NOTIFY-Accessor for QHotkey::registeredvoid registeredChanged(bool registered);private:Qt::Key _keyCode;Qt::KeyboardModifiers _modifiers;NativeShortcut _nativeShortcut;bool _registered;
};
挑一个看看源码
bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister)
{if(shortcut.isEmpty()) {return resetShortcut();} else if(shortcut.count() > 1) {qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! ""Only the first shortcut will be used!");}return setShortcut(Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask),Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),autoRegister);
}
发现也是解析出来key(0-9 a-z) 和 组合键。(ctrl shift alt ...)
然后调用了 setShortCut.
bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister)
{if(_registered) {if(autoRegister) {if(!QHotkeyPrivate::instance()->removeShortcut(this))return false;} elsereturn false;}if(keyCode == Qt::Key_unknown) {_keyCode = Qt::Key_unknown;_modifiers = Qt::NoModifier;_nativeShortcut = NativeShortcut();return true;}_keyCode = keyCode;_modifiers = modifiers;_nativeShortcut = QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);if(_nativeShortcut.isValid()) {if(autoRegister)return QHotkeyPrivate::instance()->addShortcut(this);elsereturn true;} else {qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" << keyCode << "Modifiers:" << modifiers;_keyCode = Qt::Key_unknown;_modifiers = Qt::NoModifier;_nativeShortcut = NativeShortcut();return false;}
}
看了这两个函数就知道了。问题肯定是出在这里了。解析完是Qt::key 然而Qt::key中并没有小键盘的数字。所以?? 那是肯定不会响应的。
那么问题来了,怎么解决。
我的方法简单粗暴。直接把VK_NUMPAD0-9 强制转换成 Qt::key 但是这种会覆盖那个Qt::key中的0x60(Qt::Key_QuoteLeft) ,但是不管了。先搞再说。
于是我测试了一下:
QKeySequence keySequenceFromString = (QKeySequence(Qt::CTRL , Qt::ALT , VK_NUMPAD1));m_pMousePointGetHot->setShortcutEx(keySequenceFromString, true))
发现不行会出错。
Keysequences with multiple shortcuts are not allowed! " "Only the first shortcut will be used!
这两句话就是在解析的时候 之后调用setShortcut的时候 函数头出现的。意思很清晰。
到这里。麻了。难道思路有问题。
于是我看到了第二个setShortcut函数。
bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister)
上面的图片有内容。
我想着抛弃Qt::key中的0-9数字,把他转换成VK_NUMPAD0-9。搞个转换函数强制塞进去试试
Qt::Key TransNumToPadNum(const Qt::Key k)
{switch (k){case Qt::Key_0:return Qt::Key(VK_NUMPAD0);break;case Qt::Key_1:return Qt::Key(VK_NUMPAD1);break;case Qt::Key_2:return Qt::Key(VK_NUMPAD2);break;case Qt::Key_3:return Qt::Key(VK_NUMPAD3);break;case Qt::Key_4:return Qt::Key(VK_NUMPAD4);break;case Qt::Key_5:return Qt::Key(VK_NUMPAD5);break;case Qt::Key_6:return Qt::Key(VK_NUMPAD6);break;case Qt::Key_7:return Qt::Key(VK_NUMPAD7);break;case Qt::Key_8:return Qt::Key(VK_NUMPAD8);break;case Qt::Key_9:return Qt::Key(VK_NUMPAD9);break;default:return k;break;}
}
雅黑,可以了,发现不报错了。
但是快捷键按键没有效果....
于是就跟进去看看里面的源码:可以跟着看。
QHotkey::setShortcut ->
QHotkeyPrivate::instance()->nativeShortcut -> 这里判断了按键是否有效。
QHotkeyPrivate::nativeShortcutInvoked->
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers)
{bool ok1, ok2 = false;auto k = nativeKeycode(keycode, ok1);auto m = nativeModifiers(modifiers, ok2);if(ok1 && ok2)return {k, m};elsereturn {};
}
到这里已经大致知道什么原因了。
在转换的时候 ok1是false的。就看看nativeKeycode:
这里就是根源了。他把qt的key对应起来windows的键盘码做了一个转换。问题找到了。
就是在这里地方。还是因为qt里的qt::key 没有小键盘。所以根本不可能映射。
于是我修改了一下这个对应关系,新增VK_NUMPAD0-9。
quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok)
{ok = true;switch (keycode){case VK_NUMPAD0:case VK_NUMPAD1:case VK_NUMPAD2:case VK_NUMPAD3:case VK_NUMPAD4:case VK_NUMPAD5:case VK_NUMPAD6:case VK_NUMPAD7:case VK_NUMPAD8:case VK_NUMPAD9:return keycode;default:break;}if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character"const SHORT vKey = VkKeyScanW(keycode);if(vKey > -1)return LOBYTE(vKey);}//find key from switch/case --> Only finds a very small subset of keysswitch (keycode)...}
这样外面的key如果是小键盘的话就不会出错了。
再次测试。响应了,他可以了。完美,牛逼!!!
QKeySequence keySequenceFromString = "Ctrl+Alt+1";Qt::Key keyCode = Qt::Key(k[0] & ~Qt::KeyboardModifierMask);Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(k[0] & Qt::KeyboardModifierMask);
keyCode = ui.keySequenceEdit->TransNumToPadNum(keyCode);
QKeySequence ktmpex(keyCode, modifiers);
ktmp = ktmpex;
// 报错Keysequences with multiple shortcuts are not allowed! "
// "Only the first shortcut will be used!
//m_pMousePointGetHot->setShortcutWithPad(ktmp, true);
m_pMousePointGetHot->setShortcut(keyCode, modifiers, true);// 这里要把组合键和单键摘出来,调用另外一个setShortCut
五、封装以及整理。
在qhotkey.h中我新建一个注册快捷键的函数(其实搞了两个,但是用的是红色圈住的)
实现如下:这里有个弊端就是小键盘的字符必须
/*包含小键盘数字, 但是小键盘数字仅作为最后一个快捷按键使用
*/
bool QHotkey::setShortcutWithPad(const QKeySequence& shortcut, bool autoRegister) {if (shortcut.isEmpty()) {return resetShortcut();}else if (shortcut.count() > 1) {qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! ""Only the first shortcut will be used!");}// 小键盘数字quint32 keyCode = shortcut[shortcut.count() -1];return setShortcut((Qt::Key)keyCode,Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),autoRegister);
}
直接用这个bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false);这个函数也行,但是传递keycode的时候要传递 小键盘的0-9 VK_NUMPAD0-9。
由于Qt里是用的QKeySequenceEdit,但是他无法键入小键盘的keycode.所以重新简单集成封装一下:
CustomKeySeqEdit.h
#pragma once#include <QKeySequenceEdit>
#include <QKeyEvent>class CustomKeySeqEdit : public QKeySequenceEdit
{Q_OBJECTpublic:CustomKeySeqEdit(QWidget *parent);~CustomKeySeqEdit();void setKeyNumPadNumber(bool b);bool isKeyNumPadNumber();Qt::Key TransNumToPadNum(const Qt::Key k);
private:void keyPressEvent(QKeyEvent* e) override;//virtual bool nativeEvent(const QByteArray& eventType, void* message, long* result);
private:bool m_isNumPad = false;
};
CustomKeySeqEdit.cpp
#include "CustomKeySeqEdit.h"
#include <windows.h>
#include <windowsx.h> //提供消息关键字的识别CustomKeySeqEdit::CustomKeySeqEdit(QWidget *parent): QKeySequenceEdit(parent)
{//this->installEventFilter(this);
}CustomKeySeqEdit::~CustomKeySeqEdit()
{}void CustomKeySeqEdit::setKeyNumPadNumber(bool b)
{m_isNumPad = b;
}bool CustomKeySeqEdit::isKeyNumPadNumber()
{return m_isNumPad;
}// 这个很垃圾,不应该放在这里。但是为了测试快些就随便放了
Qt::Key CustomKeySeqEdit::TransNumToPadNum(const Qt::Key k)
{switch (k){case Qt::Key_0:return Qt::Key(VK_NUMPAD0);break;case Qt::Key_1:return Qt::Key(VK_NUMPAD1);break;case Qt::Key_2:return Qt::Key(VK_NUMPAD2);break;case Qt::Key_3:return Qt::Key(VK_NUMPAD3);break;case Qt::Key_4:return Qt::Key(VK_NUMPAD4);break;case Qt::Key_5:return Qt::Key(VK_NUMPAD5);break;case Qt::Key_6:return Qt::Key(VK_NUMPAD6);break;case Qt::Key_7:return Qt::Key(VK_NUMPAD7);break;case Qt::Key_8:return Qt::Key(VK_NUMPAD8);break;case Qt::Key_9:return Qt::Key(VK_NUMPAD9);break;default:return k;break;}
}#include <QDebug>
void CustomKeySeqEdit::keyPressEvent(QKeyEvent* e)
{if (e->key() < Qt::Key_0 || e->key() > Qt::Key_9) {return QKeySequenceEdit::keyPressEvent(e);}// 我们只关心小键盘int keyCode = e->nativeVirtualKey();m_isNumPad = false;qDebug() <<"key:" << e->key() << keyCode;if (keyCode >= 0x60 && keyCode <= 0x69) {m_isNumPad = true;}QKeySequenceEdit::keyPressEvent(e);}
/* 这个不行,不会响应。
bool CustomKeySeqEdit::nativeEvent(const QByteArray& eventType, void* message, long* result)
{MSG* msg = static_cast<MSG*>(message);switch (msg->message){case WM_KEYDOWN:{m_isNumPad = false;int value = msg->wParam;if (value >= VK_NUMPAD0 && value <= VK_NUMPAD9) {m_isNumPad = true;}}break;default:break;}return false;
}
*/
使用:
至此,算是愉快结束了。 可以用QKeySequenceEdit使用小键盘的数字了。
可以正常拾取坐标啦!!!