开发环境
- 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 标签:filterEvent,界面,FilterEvent,QObject,键盘,QML,过滤器,全局 From: https://www.cnblogs.com/im18620660608/p/17081091.html