QML 全局键盘 监控

开发环境

  • windows
  • QtCreator 4.10.2(Community)
  • C++和QML混合编辑

应用场景

应用程序分为登录界面和主界面2个窗口。要求在主界面有全局键盘监控的功能,比如按ESC时,确认后退回到登录窗口。

QML中的按键事件处理

三要素:

  • focus :true            //组件必须获得焦点,只有在获得焦点时,该组件的Key事件才有效
  • Key.enabled:true  //使能Key功能
  • Key.onPress:{}        //重写Key按下的处理时间

  举个例子:我们假想这个Rect是主界面最外部的包围框(root),即所有的界面元素都在这个Rect中。

 View Code

  按这个方法,假如主界面一开始加载完成时,获得焦点的是这个Rect,那么当然可以实现我们的需求。但是用户一旦在界面上操作一些获取焦点的组件,使得这个Rect失去了焦点。那么这个全局监控键盘得到功能就无法实现了。如果想反复设置Rect的focus属性,显然不是一个好办法。

Qt的事件过滤器

  QML不方便完成的事情,我们自然想到了C++了。原则上讲:QML负责界面逻辑,C++负责数据处理和功能实现。这边博客给了我基本的思路:https://blog.csdn.net/weixin_34354945/article/details/92973701

  思路:实现一个过滤器类。这个类只要是继承自QObject或其子类,它就有一个虚函数eventFilter。我们只要实现它,在其中捕获按钮事件,进行处理。其他事件放行。捕获到按键事件时,C++发出信号,QML处理信号。只要把这个过滤器类安装我们的主窗口就可以实现需求。

 过滤器类

 filterevent.h 

复制代码
复制代码
#ifndef FILTEREVENT_H
#define FILTEREVENT_H

#include <QObject>
#include <QDebug>

class FilterEvent : public QObject
{
    Q_OBJECT
public:
    explicit FilterEvent(QObject *parent = nullptr);
protected:
protected:
      bool eventFilter(QObject *obj, QEvent *ev) override;

signals:
      void myExited();

public slots:
};

#endif // FILTEREVENT_H
复制代码
复制代码

 filterevent.cpp 

复制代码
复制代码
#include "filterevent.h"

#include <qevent.h>

FilterEvent::FilterEvent(QObject *parent) : QObject(parent)
{

}

bool FilterEvent::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        switch (keyEvent->key()) {
        case Qt::Key_Escape:
            qDebug()<<"pressed Key_Escape";
            emit myExited();
            return true;
        case Qt::Key_Enter:
            qDebug()<<"pressed Key_Enter";
            return true;
        }
        qDebug() << "Ate key press" << keyEvent->key();
    }
    return false;
}
复制代码
复制代码

安装过滤器

  在这里,我遇到了一个问题:这个过滤器的实例对象应该放在哪里?放在 main.cpp 的main函数中,我们是可以通过调用 installEventFilter 安装到主窗口上,但是按键事件处理中的信号该怎么处理呢,信号的处理地点是要放在QML中的。

参照其它博客的写法试了试,最后QML会报错过滤器实例对象can not find(虽然名字是变蓝色了...)。最后结合自己的经历,想了另一个办法。

  • filterEvent实现在QML端,采用on+信号的形式处理按键事件中发出的信号
  • main.cpp 中通过查找objectName的方式,得到主窗口和filterEvent 的指针
  • 主窗口调用installEventFilter 函数安装上filterEvent

  首先要将过滤器类注册到QML的元象树系统中。第一个参数是自己定义的包名,要改成自己的,用到的时候QML端import。

qmlRegisterType<FilterEvent>("an.qt.UserDefine", 1, 0, "FilterEvent");

  然后安装

//安装事件过滤器
    QObject* mainRootItem = engine.rootObjects().at(1);
    QObject* filterEvent = mainRootItem->findChild<QObject*>("filterEvent");
    mainRootItem->installEventFilter(filterEvent);

  QML端

复制代码
复制代码
//事件过滤器
    FilterEvent{
        objectName: "filterEvent"  //objectName用以元象树查找元素
        onMyExited: {
            console.log("filterEvent trigged");
            //todo
        }
    }
复制代码
复制代码

补充说明

  这里要补充说明的是:QML的对象是以树的形式进行管理的。可以这样理解:最外围的最大的组件可以看成是根节点,它包含的组件呢,是根节点的分支。子节点再包含一些组件,就再分支。(个人对元象树的理解,错误请指出)。然后我们可以通过 findChild<QObject*>("objectName") 的方式得到我们想要找的对象。比如可以进行一些信号与槽函数绑定。

  安装过滤器中的 mainRootItem 也是这样获得的。之所以是 at(1) 是因为我第一步加载的是登录界面(0)。

  信号与槽函数绑定举例

复制代码
复制代码
    QObject* logInRootItem = engine.rootObjects().at(0);
    QObject* logInButton = logInRootItem->findChild<QObject*>("logInButton");//根据qml中的objectName找到这个对象,获取到它的指针
    if(logInButton)
    {
        //连接QML元素中的信号到C++的槽函数
        QObject::connect(logInButton,SIGNAL(logIn(QString)),&control,SLOT(showWindow(QString)));
    }
 
 
https://www.cnblogs.com/yinxiuzhe/p/13278189.html
 
复制代码
posted @   imxiangzi  阅读(221)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示