简 述: 全局热键召唤的新窗口后,🖱不点击此程序的托盘图标和相关窗口(但是可以移动,开启光标跟踪),只点击键盘按键却无响应。但🖱点击过后,却可以响应⌨按键了。解决此怪异问题。
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
💻 win10 21H2
📎 Qt 5.12.11
背景
写截图时,当源码在 IDE 中,通过编译和运行后,右下加出现一个托盘图标
,表示程序在运行中,此时通过快捷键 F6
可顺利召唤出截图窗口
(无标题栏 + 最大化 + 置顶)。
操作如下: 此时鼠标故意不点击此截图窗口
和托盘图标
,仅移动,可以看到实时显示其中光标的绝对坐标的变化。此时按下快捷键 Esc
,截图窗口
并不会消失。甚是奇怪??? 但倘若是通过右键点击托盘菜单,召唤的 截图窗口
,按下 Esc
则有关闭响应。后续再快捷键召唤 截图窗口
和 Esc
则总是正常的。
分析
一开始是认为程序缺少焦点导致,尝试 【QT】新弹窗默认无焦点 中两种设置焦点方法,均无效。
索性写了一个 TestHotKey 来验证这个全局热键功能,原因为何?
托盘相关代码如下
// -------------------- tray.h --------------------
class Tray : public QObject
{
Q_OBJECT
public:
explicit Tray(QObject *parent = nullptr);
public slots:
void onScreenShot();
private:
QAction* m_screenShot;
QAction* m_quit;
QMenu* m_menuTary;
QSystemTrayIcon* m_sysTary;
QHotkey* m_hkScrnShot; // 热键
};
// -------------------- tray.cpp --------------------
Tray::Tray(QObject *parent)
: QObject(parent)
, m_screenShot(nullptr)
, m_quit(nullptr)
, m_menuTary(nullptr)
, m_sysTary(nullptr)
, m_hkScrnShot(new QHotkey(QKeySequence("f2"), true, qApp))
{
m_screenShot = new QAction(tr("ScreenShot"), this);
m_quit = new QAction(tr("Quit"), this);
m_menuTary = new QMenu();
m_menuTary->addAction(m_screenShot);
m_menuTary->addSeparator();
m_menuTary->addAction(m_quit);
m_sysTary = new QSystemTrayIcon(this);
m_sysTary->setIcon(QIcon(":/resources/PicShot_32.svg"));
m_sysTary->setToolTip(tr("PicShot Test"));
m_sysTary->setContextMenu(m_menuTary);
m_sysTary->setVisible(true);
// 快捷键响应
connect(m_hkScrnShot, &QHotkey::activated, this, &Tray::onScreenShot);
// 右键菜单触发
connect(m_screenShot, &QAction::triggered, this, &Tray::onScreenShot);
connect(m_quit, &QAction::triggered, []() {qApp->quit();});
}
void Tray::onScreenShot()
{
auto& ins = Widget::instance();
ins.show();
}
截图窗口代码如下:
// -------------------- tray.cpp --------------------
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
QDesktopWidget *desktop = QApplication::desktop(); // 获取桌面的窗体对象
const QRect geom = desktop->geometry(); // 多屏的矩形取并集
setWindowFlags(Qt::FramelessWindowHint /*| Qt::WindowStaysOnTopHint */| windowFlags()); // 去掉标题栏 + 置顶
// setAttribute(Qt::WA_ShowWithoutActivating,true);
// setFocusPolicy(Qt::StrongFocus);
// setFixedSize(QSize(geom.size().width() / 4, geom.size().height()));
setFixedSize(QSize(512, geom.size().height()));
setMouseTracking(true);
}
void Widget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
qDebug() << "Key_Escape";
hide();
} else if (event->key() == Qt::Key_A) {
qDebug() << "Key_A";
}
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter pa(this);
pa.drawText(100, 200, QString("m_pos(%1, %2)").arg(m_pos.x()).arg(m_pos.y()));
}
突破点在 Widget::keyPressEvent()
,想知道到底对应状态是否会响应键盘按键,确认此便可以找到原因。
根因解决
此次思路感觉有点欧亨利式:
知乎此篇 —> tab 切换窗口 —> 联想到 激活窗口 —> google key: focus 变成 窗口激活, —> 去掉置顶后尝试(去掉干扰因素) —> ok
使用全局热键召唤出来的截图窗口,此时不属于激活窗口。 解决方法,再 show() 后,将此窗口设置为激活窗口即可。
void Tray::onScreenShot() {
auto& ins = Widget::instance();
ins.show();
// 解决方案: show() 之后,设置为激活窗口即可
if(!ins.isActiveWindow())
ins.activateWindow();
}
系列地址
QtExamples 『TestHotKey』
欢迎 star
⭐ 和 fork
🍴 这个系列的 C++ / QT / DTK
学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”
参考
- Qt绘图:Keyboard Focus in Widgets 给予启发,灵光乍现
- Qt界面focus焦点设置的一些体会 虽然不是原因不同,其中参考文章却有关于焦点理解的价值
- QT 激活窗口 未尝试,若是本文未解决,可以试试这种方法
- 附: 考虑跨平台或需参考 Qt激活窗口