Qt新弹窗不响应键盘按键,难道也是无焦点?

简 述: 全局热键召唤的新窗口后,🖱不点击此程序的托盘图标和相关窗口(但是可以移动,开启光标跟踪),只点击键盘按键却无响应。但🖱点击过后,却可以响应⌨按键了。解决此怪异问题。


本文初发于 “偕臧的小站”,同步转载于此。


💻 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 学习,附学习由浅入深的目录。


参考

  1. Qt绘图:Keyboard Focus in Widgets 给予启发,灵光乍现
  2. Qt界面focus焦点设置的一些体会 虽然不是原因不同,其中参考文章却有关于焦点理解的价值
  3. QT 激活窗口 未尝试,若是本文未解决,可以试试这种方法
Qt中,处理键盘响应通常涉及到信号槽(Signal-Slot)机制。当你需要监听特定外设键(如USB键盘)的按键事件时,你可以通过以下几个步骤: 1. **QKeyListener或QObject::eventFilter**:如果你的应用程序是一个窗口,并希望直接处理所有键盘输入,可以将自定义的`QKeyListener`添加到窗口上,它会在接收到键盘事件时触发。 ```cpp class CustomKeyListener : public QObject { Q_OBJECT public: explicit CustomKeyListener(QWidget *parent = nullptr) : QObject(parent) {} protected: bool eventFilter(QObject *obj, QEvent *event) override { if (event->type() == QEvent::KeyRelease || event->type() == QEvent::KeyPress) { // 检查事件并相应地处理按键 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); handleKeypress(keyEvent); } return QObject::eventFilter(obj, event); } private: void handleKeypress(QKeyEvent *keyEvent); }; ``` 2. **QObject::connect**:然后你需要连接这个听键盘事件的对象到某个槽函数,以便于对按键做出反应。例如,当按下某个特定键时,执行某操作。 ```cpp void CustomKeyListener::handleKeypress(QKeyEvent *keyEvent) { if (keyEvent->key() == Qt::Key_F1) { // 示例:处理F1键 qDebug() << "F1 key pressed"; // 执行你的处理逻辑 } } // 在窗口初始化时连接事件过滤器 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), customListener(new CustomKeyListener(this)) { connect(customListener, &CustomKeyListener::handleKeypress, this, [this](QKeyEvent *keyEvent) { on_key_pressed(keyEvent); // 调用你的槽函数 }); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偕臧x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值