当前位置: 首页 > news >正文

Qt中使用系统级全局热键

 Qt 中实现系统级全局热键(含组合键)的几种方法:

1、使用 QHotkey 第三方库

2、平台特定API实现

一. 使用平台特定 API

Windows 方案

使用 RegisterHotKey 和 nativeEvent

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QKeySequence>#ifdef Q_OS_WIN
#include <windows.h>
#endifclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();protected:bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;private:void registerSystemHotkey(int id, const QKeySequence &shortcut);void unregisterSystemHotkey(int id);private slots:void onHotkeyTriggered(int id);private:QHash<int, QKeySequence> m_hotkeys;
};#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include <QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{// 注册系统热键registerSystemHotkey(1, QKeySequence("Ctrl+Shift+F1"));  // 热键ID, 快捷键registerSystemHotkey(2, QKeySequence("Ctrl+Shift+F2"));registerSystemHotkey(3, QKeySequence("Win+Space"));      // Windows键组合
}MainWindow::~MainWindow()
{// 注销所有热键for (int id : m_hotkeys.keys()) {unregisterSystemHotkey(id);}
}void MainWindow::registerSystemHotkey(int id, const QKeySequence &shortcut)
{
#ifdef Q_OS_WINQString keyStr = shortcut.toString().toUpper();UINT modifiers = 0;UINT vk = 0;// 解析修饰键if (keyStr.contains("CTRL")) modifiers |= MOD_CONTROL;if (keyStr.contains("ALT")) modifiers |= MOD_ALT;if (keyStr.contains("SHIFT")) modifiers |= MOD_SHIFT;if (keyStr.contains("WIN")) modifiers |= MOD_WIN;// 解析主键if (keyStr.contains("F1")) vk = VK_F1;else if (keyStr.contains("F2")) vk = VK_F2;else if (keyStr.contains("F3")) vk = VK_F3;else if (keyStr.contains("F4")) vk = VK_F4;else if (keyStr.contains("F5")) vk = VK_F5;else if (keyStr.contains("F6")) vk = VK_F6;else if (keyStr.contains("F7")) vk = VK_F7;else if (keyStr.contains("F8")) vk = VK_F8;else if (keyStr.contains("F9")) vk = VK_F9;else if (keyStr.contains("F10")) vk = VK_F10;else if (keyStr.contains("F11")) vk = VK_F11;else if (keyStr.contains("F12")) vk = VK_F12;else if (keyStr.contains("SPACE")) vk = VK_SPACE;else if (keyStr.contains("A")) vk = 'A';else if (keyStr.contains("B")) vk = 'B';else if (keyStr.contains("0")) vk = '0';else if (keyStr.contains("1")) vk = '1';// 可以继续添加其他按键...if (vk != 0) {if (RegisterHotKey((HWND)this->winId(), id, modifiers, vk)) {m_hotkeys[id] = shortcut;qDebug() << "Registered hotkey:" << shortcut.toString() << "ID:" << id;} else {qWarning() << "Failed to register hotkey:" << shortcut.toString();}}
#endif
}void MainWindow::unregisterSystemHotkey(int id)
{
#ifdef Q_OS_WINUnregisterHotKey((HWND)this->winId(), id);
#endif
}bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef Q_OS_WINif (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") {MSG *msg = static_cast<MSG*>(message);if (msg->message == WM_HOTKEY) {int hotkeyId = static_cast<int>(msg->wParam);onHotkeyTriggered(hotkeyId);*result = 0;return true;}}
#endifreturn QMainWindow::nativeEvent(eventType, message, result);
}void MainWindow::onHotkeyTriggered(int id)
{QKeySequence shortcut = m_hotkeys.value(id);QString message = QString("系统热键触发! ID: %1 快捷键: %2").arg(id).arg(shortcut.toString());qDebug() << message;// 确保窗口显示在最前面this->show();this->raise();this->activateWindow();QMessageBox::information(this, "系统热键", message);
}

项目配置pro

# 如果是 Windows 平台
win32 {LIBS += -luser32
}# 或者更具体的写法
win32: LIBS += -luser32# 如果使用 MSVC 编译器,也可以这样写
win32:msvc {LIBS += user32.lib
}# 如果使用 MinGW 编译器
win32:g++ {LIBS += -luser32
}

macOS 方案

// 对于 macOS,可以使用 Carbon API 或第三方库
#ifdef Q_OS_MAC
#include <Carbon/Carbon.h>// 注册热键的函数
OSStatus macRegisterHotKey(int id, UInt32 modKeys, UInt32 keyCode)
{EventHotKeyRef hotKeyRef;EventHotKeyID hotKeyID;hotKeyID.signature = 'QTAP';hotKeyID.id = id;return RegisterEventHotKey(keyCode, modKeys, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef);
}
#endif

Linux X11方案

// x11globalhotkey.h
#ifndef X11GLOBALHOTKEY_H
#define X11GLOBALHOTKEY_H#include <QObject>
#include <QHash>
#include <QKeySequence>#ifdef Q_OS_LINUX
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <QX11Info>
#endifclass X11GlobalHotkey : public QObject
{Q_OBJECTpublic:explicit X11GlobalHotkey(QObject *parent = nullptr);~X11GlobalHotkey();bool registerHotkey(int id, const QKeySequence &shortcut);bool unregisterHotkey(int id);void unregisterAll();static unsigned int getNativeKeycode(Display *display, Qt::Key key);static unsigned int getNativeModifiers(Qt::KeyboardModifiers modifiers);signals:void hotkeyTriggered(int id);protected:bool x11EventFilter(void *message);private:
#ifdef Q_OS_LINUXDisplay *m_display;Window m_rootWindow;
#endifQHash<int, QKeySequence> m_hotkeys;
};#endif // X11GLOBALHOTKEY_H
// x11globalhotkey.cpp
#include "x11globalhotkey.h"
#include <QDebug>
#include <QCoreApplication>
#include <QKeyEvent>#ifdef Q_OS_LINUX
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <QX11Info>
#endifX11GlobalHotkey::X11GlobalHotkey(QObject *parent): QObject(parent)
{
#ifdef Q_OS_LINUXm_display = QX11Info::display();if (!m_display) {m_display = XOpenDisplay(nullptr);}if (m_display) {m_rootWindow = DefaultRootWindow(m_display);qDebug() << "X11 display initialized successfully";} else {qWarning() << "Failed to open X11 display";}
#endif
}X11GlobalHotkey::~X11GlobalHotkey()
{unregisterAll();
#ifdef Q_OS_LINUXif (m_display && m_display != QX11Info::display()) {XCloseDisplay(m_display);}
#endif
}bool X11GlobalHotkey::registerHotkey(int id, const QKeySequence &shortcut)
{
#ifdef Q_OS_LINUXif (!m_display) {qWarning() << "X11 display not available";return false;}if (shortcut.isEmpty() || shortcut.count() == 0) {qWarning() << "Invalid shortcut sequence";return false;}int key = shortcut[0] & ~Qt::KeyboardModifierMask;Qt::KeyboardModifiers modifiers = static_cast<Qt::KeyboardModifiers>(shortcut[0] & Qt::KeyboardModifierMask);unsigned int nativeModifiers = getNativeModifiers(modifiers);unsigned int nativeKeycode = getNativeKeycode(m_display, static_cast<Qt::Key>(key));if (nativeKeycode == 0) {qWarning() << "Unsupported key:" << key;return false;}// 注册热键Bool result = XGrabKey(m_display, nativeKeycode, nativeModifiers, m_rootWindow,True, GrabModeAsync, GrabModeAsync);if (result) {m_hotkeys[id] = shortcut;qDebug() << "Registered X11 hotkey:" << shortcut.toString() << "Keycode:" << nativeKeycode << "Modifiers:" << nativeModifiers;// 刷新显示连接XSync(m_display, False);return true;} else {qWarning() << "Failed to register X11 hotkey:" << shortcut.toString();return false;}
#elseqWarning() << "X11GlobalHotkey is only supported on Linux";return false;
#endif
}bool X11GlobalHotkey::unregisterHotkey(int id)
{
#ifdef Q_OS_LINUXif (!m_display || !m_hotkeys.contains(id)) {return false;}QKeySequence shortcut = m_hotkeys[id];int key = shortcut[0] & ~Qt::KeyboardModifierMask;Qt::KeyboardModifiers modifiers = static_cast<Qt::KeyboardModifiers>(shortcut[0] & Qt::KeyboardModifierMask);unsigned int nativeModifiers = getNativeModifiers(modifiers);unsigned int nativeKeycode = getNativeKeycode(m_display, static_cast<Qt::Key>(key));if (nativeKeycode != 0) {XUngrabKey(m_display, nativeKeycode, nativeModifiers, m_rootWindow);m_hotkeys.remove(id);XSync(m_display, False);qDebug() << "Unregistered X11 hotkey ID:" << id;return true;}
#endifreturn false;
}void X11GlobalHotkey::unregisterAll()
{
#ifdef Q_OS_LINUXif (!m_display) return;for (int id : m_hotkeys.keys()) {QKeySequence shortcut = m_hotkeys[id];int key = shortcut[0] & ~Qt::KeyboardModifierMask;Qt::KeyboardModifiers modifiers = static_cast<Qt::KeyboardModifiers>(shortcut[0] & Qt::KeyboardModifierMask);unsigned int nativeModifiers = getNativeModifiers(modifiers);unsigned int nativeKeycode = getNativeKeycode(m_display, static_cast<Qt::Key>(key));if (nativeKeycode != 0) {XUngrabKey(m_display, nativeKeycode, nativeModifiers, m_rootWindow);}}m_hotkeys.clear();XSync(m_display, False);
#endif
}unsigned int X11GlobalHotkey::getNativeKeycode(Display *display, Qt::Key key)
{
#ifdef Q_OS_LINUXif (!display) return 0;KeySym keysym = 0;switch (key) {case Qt::Key_F1: keysym = XK_F1; break;case Qt::Key_F2: keysym = XK_F2; break;case Qt::Key_F3: keysym = XK_F3; break;case Qt::Key_F4: keysym = XK_F4; break;case Qt::Key_F5: keysym = XK_F5; break;case Qt::Key_F6: keysym = XK_F6; break;case Qt::Key_F7: keysym = XK_F7; break;case Qt::Key_F8: keysym = XK_F8; break;case Qt::Key_F9: keysym = XK_F9; break;case Qt::Key_F10: keysym = XK_F10; break;case Qt::Key_F11: keysym = XK_F11; break;case Qt::Key_F12: keysym = XK_F12; break;case Qt::Key_Space: keysym = XK_space; break;case Qt::Key_Enter: keysym = XK_Return; break;case Qt::Key_Return: keysym = XK_Return; break;case Qt::Key_Escape: keysym = XK_Escape; break;case Qt::Key_Delete: keysym = XK_Delete; break;case Qt::Key_Insert: keysym = XK_Insert; break;case Qt::Key_Home: keysym = XK_Home; break;case Qt::Key_End: keysym = XK_End; break;case Qt::Key_PageUp: keysym = XK_Page_Up; break;case Qt::Key_PageDown: keysym = XK_Page_Down; break;case Qt::Key_Up: keysym = XK_Up; break;case Qt::Key_Down: keysym = XK_Down; break;case Qt::Key_Left: keysym = XK_Left; break;case Qt::Key_Right: keysym = XK_Right; break;case Qt::Key_Tab: keysym = XK_Tab; break;case Qt::Key_Backspace: keysym = XK_BackSpace; break;case Qt::Key_A: keysym = XK_A; break;case Qt::Key_B: keysym = XK_B; break;case Qt::Key_C: keysym = XK_C; break;case Qt::Key_D: keysym = XK_D; break;case Qt::Key_E: keysym = XK_E; break;case Qt::Key_F: keysym = XK_F; break;case Qt::Key_G: keysym = XK_G; break;case Qt::Key_H: keysym = XK_H; break;case Qt::Key_I: keysym = XK_I; break;case Qt::Key_J: keysym = XK_J; break;case Qt::Key_K: keysym = XK_K; break;case Qt::Key_L: keysym = XK_L; break;case Qt::Key_M: keysym = XK_M; break;case Qt::Key_N: keysym = XK_N; break;case Qt::Key_O: keysym = XK_O; break;case Qt::Key_P: keysym = XK_P; break;case Qt::Key_Q: keysym = XK_Q; break;case Qt::Key_R: keysym = XK_R; break;case Qt::Key_S: keysym = XK_S; break;case Qt::Key_T: keysym = XK_T; break;case Qt::Key_U: keysym = XK_U; break;case Qt::Key_V: keysym = XK_V; break;case Qt::Key_W: keysym = XK_W; break;case Qt::Key_X: keysym = XK_X; break;case Qt::Key_Y: keysym = XK_Y; break;case Qt::Key_Z: keysym = XK_Z; break;case Qt::Key_0: keysym = XK_0; break;case Qt::Key_1: keysym = XK_1; break;case Qt::Key_2: keysym = XK_2; break;case Qt::Key_3: keysym = XK_3; break;case Qt::Key_4: keysym = XK_4; break;case Qt::Key_5: keysym = XK_5; break;case Qt::Key_6: keysym = XK_6; break;case Qt::Key_7: keysym = XK_7; break;case Qt::Key_8: keysym = XK_8; break;case Qt::Key_9: keysym = XK_9; break;default:qWarning() << "Unsupported key for X11:" << key;return 0;}return XKeysymToKeycode(display, keysym);
#elsereturn 0;
#endif
}unsigned int X11GlobalHotkey::getNativeModifiers(Qt::KeyboardModifiers modifiers)
{
#ifdef Q_OS_LINUXunsigned int nativeModifiers = 0;if (modifiers & Qt::ShiftModifier) nativeModifiers |= ShiftMask;if (modifiers & Qt::ControlModifier) nativeModifiers |= ControlMask;if (modifiers & Qt::AltModifier) nativeModifiers |= Mod1Mask;if (modifiers & Qt::MetaModifier) nativeModifiers |= Mod4Mask; // 通常 Super/Win 键return nativeModifiers;
#elsereturn 0;
#endif
}bool X11GlobalHotkey::x11EventFilter(void *message)
{
#ifdef Q_OS_LINUXXEvent *event = static_cast<XEvent*>(message);if (event->type == KeyPress) {XKeyEvent *keyEvent = &event->xkey;// 检查所有注册的热键for (auto it = m_hotkeys.constBegin(); it != m_hotkeys.constEnd(); ++it) {int id = it.key();QKeySequence shortcut = it.value();int key = shortcut[0] & ~Qt::KeyboardModifierMask;Qt::KeyboardModifiers modifiers = static_cast<Qt::KeyboardModifiers>(shortcut[0] & Qt::KeyboardModifierMask);unsigned int expectedModifiers = getNativeModifiers(modifiers);unsigned int expectedKeycode = getNativeKeycode(m_display, static_cast<Qt::Key>(key));if (keyEvent->keycode == expectedKeycode && keyEvent->state == expectedModifiers) {qDebug() << "X11 hotkey triggered:" << id;emit hotkeyTriggered(id);return true;}}}
#endifreturn false;
}

项目配置pro

# Linux 系统的 .pro 文件配置
QT += widgets x11extras# Linux 特定配置
linux {CONFIG += link_pkgconfigPKGCONFIG += x11 xcb# 或者直接链接库LIBS += -lX11 -lXext
}SOURCES += \main.cpp \linuxhotkeyhandler.cppHEADERS += \linuxhotkeyhandler.h

二、使用QHotkey第三方库(跨平台方案)

安装 QHotkey

方法1: 使用 vcpkg

bash

vcpkg install qhotkey

方法2: 手动集成到项目

  1. 下载 QHotkey 源码

  2. 将 qhotkey 文件夹复制到项目目录

  3. 在 .pro 文件中添加:

pro

include(qhotkey/qhotkey.pri)

使用示例:

#include <QApplication>
#include <QHotkey>
#include <QMessageBox>int main(int argc, char *argv[])
{QApplication app(argc, argv);// 创建全局热键QHotkey *hotkey1 = new QHotkey(QKeySequence("Ctrl+Alt+Q"), true, &app);QHotkey *hotkey2 = new QHotkey(QKeySequence("Win+Space"), true, &app);QObject::connect(hotkey1, &QHotkey::activated, [](){qDebug() << "热键 Ctrl+Alt+Q 触发!";QMessageBox::information(nullptr, "热键", "Ctrl+Alt+Q 触发!");});QObject::connect(hotkey2, &QHotkey::activated, [](){qDebug() << "热键 Win+Space 触发!";// 执行你的操作...});// 检查热键是否注册成功if (hotkey1->isRegistered()) {qDebug() << "热键1 注册成功";} else {qDebug() << "热键1 注册失败 - 可能已被其他程序占用";}return app.exec();
}

使用系统托盘配合热键

#include <QSystemTrayIcon>
#include <QMenu>
#include <QHotkey>class TrayApp : public QObject
{Q_OBJECT
public:TrayApp() {// 创建系统托盘trayIcon = new QSystemTrayIcon(QIcon(":/icon.png"), this);// 创建热键showHideHotkey = new QHotkey(QKeySequence("Ctrl+Shift+T"), true, this);connect(showHideHotkey, &QHotkey::activated, this, &TrayApp::toggleVisibility);setupTrayMenu();}private slots:void toggleVisibility() {// 切换主窗口显示/隐藏if (mainWindow->isVisible()) {mainWindow->hide();} else {mainWindow->show();mainWindow->raise();mainWindow->activateWindow();}}private:void setupTrayMenu() {QMenu *trayMenu = new QMenu();trayMenu->addAction("显示/隐藏", this, &TrayApp::toggleVisibility, QKeySequence("Ctrl+Shift+T"));trayMenu->addSeparator();trayMenu->addAction("退出", qApp, &QCoreApplication::quit);trayIcon->setContextMenu(trayMenu);trayIcon->show();}QSystemTrayIcon *trayIcon;QHotkey *showHideHotkey;QMainWindow *mainWindow;
};

三、注意事项

  1. 权限问题

    • macOS 需要用户授权辅助功能权限

    • Windows 通常不需要特殊权限

  2. 热键冲突

    • 热键可能被其他程序占用

    • 应该检查注册是否成功

    • 提供用户重新配置的选项

  3. 跨平台兼容性

    • Windows: 使用 RegisterHotKey API

    • macOS: 使用 RegisterEventHotKey

    • Linux: 使用 X11 相关 API

  4. 资源管理

    • 应用退出前务必注销所有热键

    • 处理应用暂停/恢复时的热键状态

http://www.dtcms.com/a/577795.html

相关文章:

  • 零基础新手小白快速了解掌握服务集群与自动化运维(十八)监控模块--Zabbix监控--Rocky9基于MySQL安装Zabbix7
  • pc微信ccd 3.55算法。
  • DeepSpeed 分布式训练
  • 昭和仙君(五十七)标签票据模板渲染技术——东方仙盟筑基期
  • QScrollArea技术详解:构建流畅滚动体验
  • 基础数据结构之链表的反转链表:反转整个链表(leecode 206题 简单题)
  • 广东省网站集约化建设方案建设网站需要哪个软件
  • 网站开发技术视频教程wordpress添加菜单分类目录是灰的
  • 一种双重形式化表征方法:为人工智能与人类智慧的协同进化提供了全新的方法论基础
  • ETCD 权限配置
  • 数据结构(c++版):深入理解哈希表
  • HIKVISION前端一面面经整理
  • Rocky9基于MySQL安装Zabbix7
  • 安庆网站制作1688阿里巴巴国际站首页
  • 阿里云微服务引擎 MSE 及 API 网关 2025 年 10 月产品动态
  • 太原网站建设内蒙古建设工程造价信息网官网中项网
  • Oracle 19C RAC下TRUNCATE TABLE的REUSE STORAGE选项作用和风险浅析!
  • CentOS 7 Oracle 11g RAC+DataGuard 分阶段静默部署脚本
  • 索牛网站建设江苏省建设厅官网网站首页
  • 三网合一网站系统晋城市网站建设
  • 智慧幼儿园管理系统-幼儿园多园区管理小程序的技术架构与应用实践:重构幼教领域数字化管理范式-幼儿园小程序开发-幼儿园软件开发-幼儿园系统开发定制
  • 精准招聘新纪元:AI 重构选才逻辑
  • 超聚变联手英特尔打造边缘智算一体机,重构工作站市场格局
  • 英国服务器Windows系统远程桌面安装与优化
  • 青岛做网站优化大屏网站模板
  • 多项分布 (Multinomial Distribution)
  • 网站gif横幅广告怎么做网站开发人员篡改客户数据
  • 大模型-vllm的知识点记录-1
  • 哪些网站是用织梦做的php做的直播网站
  • 为云原生加速:深入解析PoleFS分布式缓存系统BlobCache