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中。
QML 全局键盘 监控QML 全局键盘 监控

Rectangle{
     id: root
     width:  512
     height: 512
     color: "gray"

     focus: true
     Keys.enabled: true
     Keys.onPressed: {
             switch (event.key){
             case Qt.Key_Left:
                 //todo
                 break;
             case Qt.Key_Right:
                  //todo
                 break;
             case Qt.Key_Up:
                  //todo
                 break;
             case Qt.Key_Down:
                  //todo
                 break;
             default:
                 return;
             }
             event.accepted = true;//表示这些列出的按键事件已经处理,    
                                              //不再往下传递
         }
}

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
QML 全局键盘 监控QML 全局键盘 监控

#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

View Code
filterevent.cpp
QML 全局键盘 监控QML 全局键盘 监控

#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;
}

View Code

安装过滤器

在这里,我遇到了一个问题:这个过滤器的实例对象应该放在哪里?放在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("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

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/200217

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午8:22
下一篇 2023年2月12日 下午8:22

相关推荐