最近做一个游戏的辅助点击的软件,主要用Qt和opencv开发,挺简单的,主界面文件,各个独立的任务类文件,由于要频繁截图频繁点击游戏画面,所以需要游戏界面在前台,我的想法是利用键盘输入特定键来停止程序运行。
但是使用QKeyEvent事件函数后发现一直触发不了这个keyReleaseEvent函数,特别是我在截图程序里写了让游戏窗口置于前台,现在键盘又用不了,很难受,想不明白为什么?
于是我问了通义千问,他说可以用多线程,让工作线程去执行任务,好嘛,照着做了,结果没有用,
发现了Qt界面拿不到焦点,于是把设置游戏前台行给注释掉了,但是还是拿不到焦点,再问,说是可以设置焦点,好嘛,照着做了,甚至每次触发信号,来给主界面设置焦点,结果还是没用。
再问原来windows对焦点事件管控严格,很难设置,于是只能通过windows键调出Qt主界面使之处于前台,再按键盘,也可以触发键盘事件了!
有点不理解,其他软件的频繁点击是怎么做到键盘事件秒接收秒停止的?
其实要想实现随时通过键盘实现槽函数处理,我们需要使用操作系统级别的函数来获取事件,如下:
首先,在cpp文件中添加全局变量和全局回调函数,这是系统级别的,我是windows系统。
// 全局变量,用于存储钩子句柄
HHOOK g_hHook = NULL;
// 静态指针,指向 MainWindow 实例
static MainWindow *mainWindowInstance = nullptr;
// 全局键盘钩子回调函数
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode < 0) {
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
KBDLLHOOKSTRUCT *pKeyboard = (KBDLLHOOKSTRUCT *)lParam;
int keyCode = pKeyboard->vkCode;
if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
std::cout << "Global key released: " << keyCode << std::endl;
// 发送信号给 MainWindow
if (mainWindowInstance)
QMetaObject::invokeMethod(mainWindowInstance, "handleGlobalKeyRelease", Qt::QueuedConnection, Q_ARG(int, keyCode));
else
std::cout << "MainWindow not found" << std::endl;
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
在构造函数中安装全局键盘钩子
// 安装全局键盘钩子
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProc, nullptr, 0);
if (g_hHook == nullptr) {
std::cerr << "Failed to install global keyboard hook" << std::endl;
}
// 保存 MainWindow 实例指针q
mainWindowInstance = this;
在析构函数中卸载全局键盘钩子
// 卸载全局键盘钩子
if (g_hHook != nullptr)
{
UnhookWindowsHookEx(g_hHook);
}
添加槽函数
void handleGlobalKeyRelease(int keyCode) {
std::cout << "Handling global key release: " << keyCode << std::endl;
QKeyEvent fakeKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier, QKeySequence(keyCode).toString());
keyReleaseEvent(&fakeKeyEvent); // 使用 postEvent 将事件发送到事件队列
}
最后重写你的keyReleaseEvent即可,操作系统会识别所有的键盘事件,然后发给本类实例,触发回调函数,进行键盘事件处理。