开发环境
- windows
- QtCreator 4.10.2(Community)
- C++和QML混合编辑
应用场景
应用程序分为登录界面和主界面2个窗口。要求在主界面有全局键盘监控的功能,比如按ESC时,确认后退回到登录窗口。
QML中的按键事件处理
三要素:
- focus :true //组件必须获得焦点,只有在获得焦点时,该组件的Key事件才有效
- Key.enabled:true //使能Key功能
- Key.onPress:{} //重写Key按下的处理时间
举个例子:我们假想这个Rect是主界面最外部的包围框(root),即所有的界面元素都在这个Rect中。
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
#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
#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
安装过滤器中的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
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!