• Qt第三方库QHotKey设置小键盘数字快捷键


    一、看了一圈没有找到可以设置小键盘的情况。

    这两天在研究快捷键的使用。发现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 以及他的重载。

    1. //! A class to define global, systemwide Hotkeys
    2. class QHOTKEY_SHARED_EXPORT QHotkey : public QObject
    3. {
    4. Q_OBJECT
    5. friend class QHotkeyPrivate;
    6. //! Specifies whether this hotkey is currently registered or not
    7. Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged)
    8. //! Holds the shortcut this hotkey will be triggered on
    9. Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut)
    10. public:
    11. //! Defines shortcut with native keycodes
    12. class QHOTKEY_SHARED_EXPORT NativeShortcut {
    13. public:
    14. //! The native keycode
    15. quint32 key;
    16. //! The native modifiers
    17. quint32 modifier;
    18. //! Creates an invalid native shortcut
    19. NativeShortcut();
    20. //! Creates a valid native shortcut, with the given key and modifiers
    21. NativeShortcut(quint32 key, quint32 modifier = 0);
    22. //! Checks, whether this shortcut is valid or not
    23. bool isValid() const;
    24. //! Equality operator
    25. bool operator ==(const NativeShortcut &other) const;
    26. //! Inequality operator
    27. bool operator !=(const NativeShortcut &other) const;
    28. private:
    29. bool valid;
    30. };
    31. //! Constructor
    32. explicit QHotkey(QObject *parent = Q_NULLPTR);
    33. //! Constructs a hotkey with a shortcut and optionally registers it
    34. explicit QHotkey(const QKeySequence &shortcut, bool autoRegister = false, QObject *parent = Q_NULLPTR);
    35. //! Constructs a hotkey with a key and modifiers and optionally registers it
    36. explicit QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false, QObject *parent = Q_NULLPTR);
    37. //! Constructs a hotkey from a native shortcut and optionally registers it
    38. explicit QHotkey(const NativeShortcut &shortcut, bool autoRegister = false, QObject *parent = Q_NULLPTR);
    39. //! Destructor
    40. ~QHotkey();
    41. //! READ-Accessor for QHotkey::registered
    42. bool isRegistered() const;
    43. //! READ-Accessor for QHotkey::shortcut - the key and modifiers as a QKeySequence
    44. QKeySequence shortcut() const;
    45. //! READ-Accessor for QHotkey::shortcut - the key only
    46. Qt::Key keyCode() const;
    47. //! READ-Accessor for QHotkey::shortcut - the modifiers only
    48. Qt::KeyboardModifiers modifiers() const;
    49. //! Get the current native shortcut
    50. NativeShortcut currentNativeShortcut() const;
    51. public slots:
    52. //! WRITE-Accessor for QHotkey::registered
    53. bool setRegistered(bool registered);
    54. //! WRITE-Accessor for QHotkey::shortcut
    55. bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false);
    56. //! WRITE-Accessor for QHotkey::shortcut
    57. bool setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false);
    58. //! RESET-Accessor for QHotkey::shortcut
    59. bool resetShortcut();
    60. //! Set this hotkey to a native shortcut
    61. bool setNativeShortcut(NativeShortcut nativeShortcut, bool autoRegister = false);
    62. signals:
    63. //! Will be emitted if the shortcut is pressed
    64. void activated(QPrivateSignal);
    65. //! NOTIFY-Accessor for QHotkey::registered
    66. void registeredChanged(bool registered);
    67. private:
    68. Qt::Key _keyCode;
    69. Qt::KeyboardModifiers _modifiers;
    70. NativeShortcut _nativeShortcut;
    71. bool _registered;
    72. };

    挑一个看看源码

    1. bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister)
    2. {
    3. if(shortcut.isEmpty()) {
    4. return resetShortcut();
    5. } else if(shortcut.count() > 1) {
    6. qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! "
    7. "Only the first shortcut will be used!");
    8. }
    9. return setShortcut(Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask),
    10. Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),
    11. autoRegister);
    12. }

    发现也是解析出来key(0-9 a-z) 和 组合键。(ctrl shift alt ...)

    然后调用了 setShortCut.

    1. bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister)
    2. {
    3. if(_registered) {
    4. if(autoRegister) {
    5. if(!QHotkeyPrivate::instance()->removeShortcut(this))
    6. return false;
    7. } else
    8. return false;
    9. }
    10. if(keyCode == Qt::Key_unknown) {
    11. _keyCode = Qt::Key_unknown;
    12. _modifiers = Qt::NoModifier;
    13. _nativeShortcut = NativeShortcut();
    14. return true;
    15. }
    16. _keyCode = keyCode;
    17. _modifiers = modifiers;
    18. _nativeShortcut = QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);
    19. if(_nativeShortcut.isValid()) {
    20. if(autoRegister)
    21. return QHotkeyPrivate::instance()->addShortcut(this);
    22. else
    23. return true;
    24. } else {
    25. qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" << keyCode << "Modifiers:" << modifiers;
    26. _keyCode = Qt::Key_unknown;
    27. _modifiers = Qt::NoModifier;
    28. _nativeShortcut = NativeShortcut();
    29. return false;
    30. }
    31. }

    看了这两个函数就知道了。问题肯定是出在这里了。解析完是Qt::key 然而Qt::key中并没有小键盘的数字。所以?? 那是肯定不会响应的。

    那么问题来了,怎么解决。

    我的方法简单粗暴。直接把VK_NUMPAD0-9 强制转换成 Qt::key 但是这种会覆盖那个Qt::key中的0x60(Qt::Key_QuoteLeft) ,但是不管了。先搞再说。

    于是我测试了一下:

    1. QKeySequence keySequenceFromString = (QKeySequence(Qt::CTRL , Qt::ALT , VK_NUMPAD1));
    2. 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。搞个转换函数强制塞进去试试

    1. Qt::Key TransNumToPadNum(const Qt::Key k)
    2. {
    3. switch (k)
    4. {
    5. case Qt::Key_0:
    6. return Qt::Key(VK_NUMPAD0);
    7. break;
    8. case Qt::Key_1:
    9. return Qt::Key(VK_NUMPAD1);
    10. break;
    11. case Qt::Key_2:
    12. return Qt::Key(VK_NUMPAD2);
    13. break;
    14. case Qt::Key_3:
    15. return Qt::Key(VK_NUMPAD3);
    16. break;
    17. case Qt::Key_4:
    18. return Qt::Key(VK_NUMPAD4);
    19. break;
    20. case Qt::Key_5:
    21. return Qt::Key(VK_NUMPAD5);
    22. break;
    23. case Qt::Key_6:
    24. return Qt::Key(VK_NUMPAD6);
    25. break;
    26. case Qt::Key_7:
    27. return Qt::Key(VK_NUMPAD7);
    28. break;
    29. case Qt::Key_8:
    30. return Qt::Key(VK_NUMPAD8);
    31. break;
    32. case Qt::Key_9:
    33. return Qt::Key(VK_NUMPAD9);
    34. break;
    35. default:
    36. return k;
    37. break;
    38. }
    39. }

    雅黑,可以了,发现不报错了。

    但是快捷键按键没有效果....

    于是就跟进去看看里面的源码:可以跟着看。

    QHotkey::setShortcut  ->

    QHotkeyPrivate::instance()->nativeShortcut  ->  这里判断了按键是否有效。

    QHotkeyPrivate::nativeShortcutInvoked->

    1. QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers)
    2. {
    3. bool ok1, ok2 = false;
    4. auto k = nativeKeycode(keycode, ok1);
    5. auto m = nativeModifiers(modifiers, ok2);
    6. if(ok1 && ok2)
    7. return {k, m};
    8. else
    9. return {};
    10. }

    到这里已经大致知道什么原因了。

    在转换的时候 ok1是false的。就看看nativeKeycode:

    这里就是根源了。他把qt的key对应起来windows的键盘码做了一个转换。问题找到了。

    就是在这里地方。还是因为qt里的qt::key 没有小键盘。所以根本不可能映射。

    于是我修改了一下这个对应关系,新增VK_NUMPAD0-9。

    1. quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok)
    2. {
    3. ok = true;
    4. switch (keycode)
    5. {
    6. case VK_NUMPAD0:
    7. case VK_NUMPAD1:
    8. case VK_NUMPAD2:
    9. case VK_NUMPAD3:
    10. case VK_NUMPAD4:
    11. case VK_NUMPAD5:
    12. case VK_NUMPAD6:
    13. case VK_NUMPAD7:
    14. case VK_NUMPAD8:
    15. case VK_NUMPAD9:
    16. return keycode;
    17. default:
    18. break;
    19. }
    20. if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character"
    21. const SHORT vKey = VkKeyScanW(keycode);
    22. if(vKey > -1)
    23. return LOBYTE(vKey);
    24. }
    25. //find key from switch/case --> Only finds a very small subset of keys
    26. switch (keycode)
    27. ...
    28. }

    这样外面的key如果是小键盘的话就不会出错了。

    再次测试。响应了,他可以了。完美,牛逼!!!

    1. QKeySequence keySequenceFromString = "Ctrl+Alt+1";
    2. Qt::Key keyCode = Qt::Key(k[0] & ~Qt::KeyboardModifierMask);
    3. Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(k[0] & Qt::KeyboardModifierMask);
    4. keyCode = ui.keySequenceEdit->TransNumToPadNum(keyCode);
    5. QKeySequence ktmpex(keyCode, modifiers);
    6. ktmp = ktmpex;
    7. // 报错Keysequences with multiple shortcuts are not allowed! "
    8. // "Only the first shortcut will be used!
    9. //m_pMousePointGetHot->setShortcutWithPad(ktmp, true);
    10. m_pMousePointGetHot->setShortcut(keyCode, modifiers, true);
    11. // 这里要把组合键和单键摘出来,调用另外一个setShortCut

    五、封装以及整理。

     在qhotkey.h中我新建一个注册快捷键的函数(其实搞了两个,但是用的是红色圈住的)

    实现如下:这里有个弊端就是小键盘的字符必须

    1. /*
    2. 包含小键盘数字, 但是小键盘数字仅作为最后一个快捷按键使用
    3. */
    4. bool QHotkey::setShortcutWithPad(const QKeySequence& shortcut, bool autoRegister) {
    5. if (shortcut.isEmpty()) {
    6. return resetShortcut();
    7. }
    8. else if (shortcut.count() > 1) {
    9. qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! "
    10. "Only the first shortcut will be used!");
    11. }
    12. // 小键盘数字
    13. quint32 keyCode = shortcut[shortcut.count() -1];
    14. return setShortcut((Qt::Key)keyCode,
    15. Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),
    16. autoRegister);
    17. }

    直接用这个bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false);这个函数也行,但是传递keycode的时候要传递 小键盘的0-9 VK_NUMPAD0-9。

    由于Qt里是用的QKeySequenceEdit,但是他无法键入小键盘的keycode.所以重新简单集成封装一下:

    CustomKeySeqEdit.h

    1. #pragma once
    2. #include
    3. #include
    4. class CustomKeySeqEdit : public QKeySequenceEdit
    5. {
    6. Q_OBJECT
    7. public:
    8. CustomKeySeqEdit(QWidget *parent);
    9. ~CustomKeySeqEdit();
    10. void setKeyNumPadNumber(bool b);
    11. bool isKeyNumPadNumber();
    12. Qt::Key TransNumToPadNum(const Qt::Key k);
    13. private:
    14. void keyPressEvent(QKeyEvent* e) override;
    15. //virtual bool nativeEvent(const QByteArray& eventType, void* message, long* result);
    16. private:
    17. bool m_isNumPad = false;
    18. };

    CustomKeySeqEdit.cpp

    1. #include "CustomKeySeqEdit.h"
    2. #include
    3. #include //提供消息关键字的识别
    4. CustomKeySeqEdit::CustomKeySeqEdit(QWidget *parent)
    5. : QKeySequenceEdit(parent)
    6. {
    7. //this->installEventFilter(this);
    8. }
    9. CustomKeySeqEdit::~CustomKeySeqEdit()
    10. {}
    11. void CustomKeySeqEdit::setKeyNumPadNumber(bool b)
    12. {
    13. m_isNumPad = b;
    14. }
    15. bool CustomKeySeqEdit::isKeyNumPadNumber()
    16. {
    17. return m_isNumPad;
    18. }
    19. // 这个很垃圾,不应该放在这里。但是为了测试快些就随便放了
    20. Qt::Key CustomKeySeqEdit::TransNumToPadNum(const Qt::Key k)
    21. {
    22. switch (k)
    23. {
    24. case Qt::Key_0:
    25. return Qt::Key(VK_NUMPAD0);
    26. break;
    27. case Qt::Key_1:
    28. return Qt::Key(VK_NUMPAD1);
    29. break;
    30. case Qt::Key_2:
    31. return Qt::Key(VK_NUMPAD2);
    32. break;
    33. case Qt::Key_3:
    34. return Qt::Key(VK_NUMPAD3);
    35. break;
    36. case Qt::Key_4:
    37. return Qt::Key(VK_NUMPAD4);
    38. break;
    39. case Qt::Key_5:
    40. return Qt::Key(VK_NUMPAD5);
    41. break;
    42. case Qt::Key_6:
    43. return Qt::Key(VK_NUMPAD6);
    44. break;
    45. case Qt::Key_7:
    46. return Qt::Key(VK_NUMPAD7);
    47. break;
    48. case Qt::Key_8:
    49. return Qt::Key(VK_NUMPAD8);
    50. break;
    51. case Qt::Key_9:
    52. return Qt::Key(VK_NUMPAD9);
    53. break;
    54. default:
    55. return k;
    56. break;
    57. }
    58. }
    59. #include
    60. void CustomKeySeqEdit::keyPressEvent(QKeyEvent* e)
    61. {
    62. if (e->key() < Qt::Key_0 || e->key() > Qt::Key_9) {
    63. return QKeySequenceEdit::keyPressEvent(e);
    64. }
    65. // 我们只关心小键盘
    66. int keyCode = e->nativeVirtualKey();
    67. m_isNumPad = false;
    68. qDebug() <<"key:" << e->key() << keyCode;
    69. if (keyCode >= 0x60 && keyCode <= 0x69) {
    70. m_isNumPad = true;
    71. }
    72. QKeySequenceEdit::keyPressEvent(e);
    73. }
    74. /* 这个不行,不会响应。
    75. bool CustomKeySeqEdit::nativeEvent(const QByteArray& eventType, void* message, long* result)
    76. {
    77. MSG* msg = static_cast(message);
    78. switch (msg->message)
    79. {
    80. case WM_KEYDOWN:
    81. {
    82. m_isNumPad = false;
    83. int value = msg->wParam;
    84. if (value >= VK_NUMPAD0 && value <= VK_NUMPAD9) {
    85. m_isNumPad = true;
    86. }
    87. }
    88. break;
    89. default:
    90. break;
    91. }
    92. return false;
    93. }
    94. */

    使用:

    至此,算是愉快结束了。 可以用QKeySequenceEdit使用小键盘的数字了。

    可以正常拾取坐标啦!!!

  • 相关阅读:
    webrtc QOS笔记一 Neteq直方图算法浅读
    策略回测从2小时提速到1分钟,DolphinDB 助力华夏基金量化投研
    java回收算法学习
    LeetCode算法心得——和可被 K 整除的子数组(前缀和+HashMap)
    剑指 Offer 26. 树的子结构
    数据研发“新人”如何快速落地?
    BOA服务器(一):简介
    C++流程控制总结,看这一篇就够了
    Git多人开发解决冲突案例
    【Linux】Linux+Nginx部署项目(负载均衡&动静分离)
  • 原文地址:https://blog.csdn.net/xiaobai_2511/article/details/139601398