前言
Qt 提供了QShortcut来实现快捷键的功能,不过它的响应范围只局限于窗口内。如果脱离了进程窗口,如在桌面,或者正在使用其他软件,是无法响应的。
要解决这样的问题可以使用Windows 提供的API再结合qt的一些功能来实现一个全局可响应的快捷键。
需要用到的技术
RegisterHotKey()
这是是一个winAPI函数,它的作用是像系统注册一个快捷键,注册成功后系统会根据全局的键盘输入来判断快捷键是否被触发,触发之后通过WinMessage的方式来通知注册者。
BOOL WINAPI RegisterHotKey(
BOOL WINAPI RegisterHotKey(
HWND hWnd,
int id,
UINT fsModifiers,
UINT vk
);
BOOL WINAPI RegisterHotKey(
HWND hWnd,
int id,
UINT fsModifiers,
UINT vk
);
这是它的函数原型
参数介绍
hWnd:接收热键产生WM_HOTKEY消息的窗口句柄。若该参数NULL,传递给调用线程的WM_HOTKEY消息必须在消息循环中进行处理。
id:定义热键的标识符。调用线程中的其他热键,不能使用同样的标识符。应用程序必须定义一个0X0000-0xBFFF范围的值。一个共享的动态链接库(DLL)必须定义一个范围为0xC000-0xFFFF的值(GlobalAddAtomA函数返回该范围)。为了避免与其他动态链接库定义的热键冲突,一个DLL必须使用GlobalAddAtomA函数获得热键的标识符。
fsModifoers:定义为了产生WM_HOTKEY消息而必须与由nVirtKey参数定义的键一起按下的键。
该参数可以是如下值的组合:
键 |
值 |
含意 |
MOD_ALT |
0x0001 |
按下的可以是任一Alt键。 |
MOD_SHIFT |
0x0004 |
按下的可以是任一Shift键。 |
MOD_WIN |
0x0008 |
按下的可以是任一Windows徽标键。 |
MOD_NOREPEAT |
0x4000 |
更改热键行为,以便键盘自动重复不会产生多个热键通知。 |
MOD_CONTROL |
0x0002 |
按下的可以是任一Ctrl键。 |
vk: 定义热键的虚拟键码。
QT事件机制处理windows原生事件
QApplication::installNativeEventFilter() 是QT提供的一种事件机制,可以通过这个函数安装一个过滤器来处理Windows原生的事件,这个可以参考之前的博客 QT事件机制的简单使用。
使用时,需要继承QAbstractNativeEventFilter类,实现它的纯虚函数:
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) = 0
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) = 0
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) = 0
在这个函数里处理Windows原生的事件消息。
message指针可以强制转换为WinApi中的Msg类型,然后按照Msg中的信息处理即可。
最后调用QApplication::installNativeEventFilter() 传入类对象指针即可。
实现步骤
- 调用RegisterHotKey()注册一个系统级的快捷键
- 实现一个事件过滤器
- 将之间过滤器安装到QApplication
具体代码
.h
#include <QAbstractNativeEventFilter>
class NativeEventFilter :public QAbstractNativeEventFilter {
NativeEventFilter(const unsigned int& mod, const unsigned int& key);
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result);
void registerShortcut(const NativeEventFilter &filter);
#pragma once
#include <QAbstractNativeEventFilter>
class NativeEventFilter :public QAbstractNativeEventFilter {
public:
NativeEventFilter(const unsigned int& mod, const unsigned int& key);
public:
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result);
unsigned int mod, key;
};
class ShortcutTest{
public:
ShortcutTest() {};
~ShortcutTest() {};
public:
void registerShortcut(const NativeEventFilter &filter);
};
#pragma once
#include <QAbstractNativeEventFilter>
class NativeEventFilter :public QAbstractNativeEventFilter {
public:
NativeEventFilter(const unsigned int& mod, const unsigned int& key);
public:
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result);
unsigned int mod, key;
};
class ShortcutTest{
public:
ShortcutTest() {};
~ShortcutTest() {};
public:
void registerShortcut(const NativeEventFilter &filter);
};
.cpp
#include "ShortcutTest.h"
NativeEventFilter::NativeEventFilter(const unsigned int& mod, const unsigned int& key)
bool NativeEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY)
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
if (keycode == key && mod == modifiers)
std::cerr << "shortcut trigger!" << std::endl;
void ShortcutTest::registerShortcut(const NativeEventFilter& filter)
int id = filter.key ^ filter.mod;
BOOL ok = RegisterHotKey(0, id, filter.mod, filter.key);
std::cerr << "register shortcut failed!" << std::endl;
#include "ShortcutTest.h"
#include <windows.h>
#include <iostream>
NativeEventFilter::NativeEventFilter(const unsigned int& mod, const unsigned int& key)
{
this->mod = mod;
this->key = key;
}
bool NativeEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
{
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY)
{
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
if (keycode == key && mod == modifiers)
std::cerr << "shortcut trigger!" << std::endl;
}
return false;
}
void ShortcutTest::registerShortcut(const NativeEventFilter& filter)
{
int id = filter.key ^ filter.mod;
BOOL ok = RegisterHotKey(0, id, filter.mod, filter.key);
if (!ok)
std::cerr << "register shortcut failed!" << std::endl;
}
#include "ShortcutTest.h"
#include <windows.h>
#include <iostream>
NativeEventFilter::NativeEventFilter(const unsigned int& mod, const unsigned int& key)
{
this->mod = mod;
this->key = key;
}
bool NativeEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
{
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY)
{
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
if (keycode == key && mod == modifiers)
std::cerr << "shortcut trigger!" << std::endl;
}
return false;
}
void ShortcutTest::registerShortcut(const NativeEventFilter& filter)
{
int id = filter.key ^ filter.mod;
BOOL ok = RegisterHotKey(0, id, filter.mod, filter.key);
if (!ok)
std::cerr << "register shortcut failed!" << std::endl;
}
main()
int main(int argc, char *argv[])
QApplication a(argc, argv);
NativeEventFilter filter(MOD_ALT,'W');
a.installNativeEventFilter(&filter);
test.registerShortcut(filter);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
NativeEventFilter filter(MOD_ALT,'W');
a.installNativeEventFilter(&filter);
ShortcutTest test;
test.registerShortcut(filter);
return a.exec();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
NativeEventFilter filter(MOD_ALT,'W');
a.installNativeEventFilter(&filter);
ShortcutTest test;
test.registerShortcut(filter);
return a.exec();
}
运行结果:

补充
在代码中传虚拟按键代码时,传了一个char ‘w’ 过去,最开始我是以为字母区的虚拟码应该是VK_xx这样的格式,尝试了以下并不是。然后就尝试到头文件中去找,找到了这样一段注释:
* VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
* VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
/*
* VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
* 0x40 : unassigned
* VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
*/
/*
* VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
* 0x40 : unassigned
* VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
*/
因为数字键和字母键的键码和他们的ASCII码是一样的,所以在头文件中没有定义。
注意:这里的字母要使用大写的,小写的不行
Qt::key 转换为WinAPi的虚拟键码的互相转换
QT 中提供QKeySequenceEdit 给用户输入快捷键,得到是结果是Qt::key类型的,想要使用WinAPI注册快捷键还需要一些转换,这里提供以下转换函数,否则又是一个体力活。
unsigned int nativeModifiers(Qt::KeyboardModifiers modifiers)
if (modifiers & Qt::ShiftModifier)
if (modifiers & Qt::ControlModifier)
if (modifiers & Qt::AltModifier)
if (modifiers & Qt::MetaModifier)
unsigned int nativeKeycode(Qt::Key key)
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
return VK_MEDIA_PLAY_PAUSE;
unsigned int nativeModifiers(Qt::KeyboardModifiers modifiers)
{
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= MOD_SHIFT;
if (modifiers & Qt::ControlModifier)
native |= MOD_CONTROL;
if (modifiers & Qt::AltModifier)
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
return native;
}
unsigned int nativeKeycode(Qt::Key key)
{
switch (key)
{
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_MediaNext:
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
case Qt::Key_MediaPlay:
return VK_MEDIA_PLAY_PAUSE;
case Qt::Key_MediaStop:
return VK_MEDIA_STOP;
case Qt::Key_VolumeDown:
return VK_VOLUME_DOWN;
case Qt::Key_VolumeUp:
return VK_VOLUME_UP;
case Qt::Key_VolumeMute:
return VK_VOLUME_MUTE;
case Qt::Key_0:
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
case Qt::Key_9:
return key;
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
case Qt::Key_E:
case Qt::Key_F:
case Qt::Key_G:
case Qt::Key_H:
case Qt::Key_I:
case Qt::Key_J:
case Qt::Key_K:
case Qt::Key_L:
case Qt::Key_M:
case Qt::Key_N:
case Qt::Key_O:
case Qt::Key_P:
case Qt::Key_Q:
case Qt::Key_R:
case Qt::Key_S:
case Qt::Key_T:
case Qt::Key_U:
case Qt::Key_V:
case Qt::Key_W:
case Qt::Key_X:
case Qt::Key_Y:
case Qt::Key_Z:
return key;
default:
return 0;
}
}
unsigned int nativeModifiers(Qt::KeyboardModifiers modifiers)
{
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= MOD_SHIFT;
if (modifiers & Qt::ControlModifier)
native |= MOD_CONTROL;
if (modifiers & Qt::AltModifier)
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
return native;
}
unsigned int nativeKeycode(Qt::Key key)
{
switch (key)
{
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_MediaNext:
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
case Qt::Key_MediaPlay:
return VK_MEDIA_PLAY_PAUSE;
case Qt::Key_MediaStop:
return VK_MEDIA_STOP;
case Qt::Key_VolumeDown:
return VK_VOLUME_DOWN;
case Qt::Key_VolumeUp:
return VK_VOLUME_UP;
case Qt::Key_VolumeMute:
return VK_VOLUME_MUTE;
case Qt::Key_0:
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
case Qt::Key_9:
return key;
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
case Qt::Key_E:
case Qt::Key_F:
case Qt::Key_G:
case Qt::Key_H:
case Qt::Key_I:
case Qt::Key_J:
case Qt::Key_K:
case Qt::Key_L:
case Qt::Key_M:
case Qt::Key_N:
case Qt::Key_O:
case Qt::Key_P:
case Qt::Key_Q:
case Qt::Key_R:
case Qt::Key_S:
case Qt::Key_T:
case Qt::Key_U:
case Qt::Key_V:
case Qt::Key_W:
case Qt::Key_X:
case Qt::Key_Y:
case Qt::Key_Z:
return key;
default:
return 0;
}
}
![]()
文章评论
哈哈 搞定了
大佬,为啥我抄你的大佬运行不了啊。。 加个QQ 解答下呗2602118005