Qt 元对象系统 自定义类型注册 与 事件过滤器安装 (*)

自定义类型  注册到Qt元对象系统中

自定义事件  的注册

Qt事件过滤器安装  原理(installEventFilter函数)

===============================

注册自定义类型到Qt元对象系统中

注册自定义结构体为例

1.使用 Q_DECLARE_METATYPE 标记自定义类型

#include <QMetaType>
struct MyStruct {
    QString name;
    QString color;
};

//标记自定义类型
Q_DECLARE_METATYPE(MyStruct)

2.在main函数中使用 qRegisterMetaType 注册自定义类型到元对象系统中。

int main(int argc, char *argv[]) {
    ...
    qRegisterMetaType<MyStruct>();
    ...
    return 0;
}

作用

  • 被 Q_DECLARE_METATYPE Q标记的类型可以让QMetaType查询到类型,也可以让QVariant识别到。例如:
MyStruct myStruct;
QVariant variant;
variant.setValue(myStruct); // 不使用 Q_DECLARE_METATYPE Q标记的自定义类型会出现编译错误。
...
MyStruct myStruct2 = variant.value<MyStruct>();
...
  • 使用 qRegisterMetaType 注册自定义类型到元对象系统中主要作用为QObject的属性系统(信号槽)中使用该自定义类型

Qt注册自定义类型

一、自定义类型注册必要性

如果要在Qt信号槽中使用自定义类型,需要注意使用qRegisterMetaType对自定义类型进行注册,当然在不跨线程时使用自定义类型signal/slot来传递,可能不会出现什么问题;一旦涉及跨线程就很容易出错,回想下信号槽的作用就是用来对象与对象之间通信的,难免会跨线程,建议在使用自定义类型利用信号槽通信时,最好先通过qRegisterMetaType()将自定义类型进行注册,以免出错。
 

二、qRegisterMetaType使用方法

总结qRegisterMetaType使用方法如下:
1、注册位置:在第一次使用此类链接跨线程的signal/slot之前,一般在当前类的构造函数中进行注册;
2、注册方法:在当前类的顶部包含:#include <QMetaType>,构造函数中加入代码:qRegisterMetaType<MyClass>("Myclass");
3、Myclass的引用类型需单独注册:qRegisterMetaType<MyClass>("Myclass&");

如果是自己定义的类型如果想使用signal/slot来传递的话,则没有这么简单。直接使用的话,会产生下面这种错误:
QObject::connect: Cannot queue arguments of type ‘TextAndNumber’ (Make sure ‘TextAndNumber’ is registed using qRegisterMetaType().)

原因:当一个signal被放到队列中(queued)时,它的参数(arguments)也会被一起一起放到队列中(queued起来),这就意味着参数在被传送到slot之前需要被拷贝、存储在队列中(queue)中;为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎样去作这些事情,参数的类型需要使用qRegisterMetaType来注册(如错误提示中的说明)。
 

步骤:(以自定义TextAndNumber类型为例)

1. 自定一种类型,在这个类型的顶部包含:

    #include <QMetaType>

2. 在类型定义完成后,加入声明:

    Q_DECLARE_METATYPE(TextAndNumber);

3. 在main()函数中注册这种类型:

    qRegisterMetaType<TextAndNumber>("TextAndNumber");

4. 如果还希望使用这种类型的引用,可同样要注册:

    qRegisterMetaType<TextAndNumber>("TextAndNumber&");


————————————————
版权声明:本文为CSDN博主「夏之七」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/locahuang/article/details/110221959

 ========================================

Qt事件系统:自定义事件的注册

https://blog.csdn.net/ken2232/article/details/129483703

Qt 提供了一个函数 registerEventType() 专门用于自定义事件的注册。如下:

const QEvent::Type MyEvent::type = (QEvent::Type)QEvent::registerEventType()

 ========================================

Qt事件过滤器原理(installEventFilter函数)

事件过滤器用于拦截传递到目标对象的事件,这样可以实现监视目标对象事件的作用。

1、Qt实现事件过滤器的步骤如下:

①、Qt调用

void QObject::installEventFilter (QObject* filterObj)

把filterObj对象安装(或注册)为事件过滤器,filterObj也称为过滤器对象。事件过滤器通常在构造函数中进行注册。

②、在上一步注册的filterObj对象,通过调用

bool QObject::eventFilter(QObject* obj, QEvent* e);

来接收拦截到的事件。也就是说拦截到的事件在filterObj对象中的eventFilter函数中处理。eventFilter的第一个参数obj指向的是事件本应传递到的目标对象。

③、使用QObject::removeEventFilter(QObject *obj)函数可以删除事件过滤器。

QObject::removeEventFilter(QObject *obj);

2、事件过滤器处理事件的规则

①、过滤器对象的eventFilter()函数可以接受或拒绝拦截到的事件,若该函数返回false,则表示事件需要作进一步处理,此时事件会被发送到目标对象本身进行处理(注意:这里并未向父对象进行传递),若evetnFilter()返回true,则表示停止处理该事件,此时目标对象和后面安装的事件过滤器就无法获得该事件。

②、若同一对象安装了多个事件过滤器,则最后安装的过滤器首先被激活。

3、为什么使用事件过滤器

使用事件过滤器可以简化程序代码。

比如按钮1和标签1,对按下A键的事件响应相同的操作,若不使用事件过滤器,则需要分别子类化按钮和标签部件,并重新实现各自的事件处理函数。

再如使用同一个子类化按钮的类C创建的按钮1和按钮2,对按下键A,按钮1和按钮2需要作不同的响应,若不使用事件过滤器,则他们的响应是相同的,若使用事件过滤器,则可以拦截按钮1或按钮2的事件并作特殊处理。

4、理解事件过滤器

观察者模式:其原理为,设有一目标对象S,它有多个观察该对象的对象G1,G2,G3,当S发生变化时,S的观察者会依情形改变自身。

应用于Qt事件过滤器,则是,首先使用S的成员函数installEventFilter函数把G1,G2,G3设置为S的观察者,所有本应传递给S的事件E,则先传递给观察者G1,G2,G3,

然后观察者调用其成员函数eventFilter对传递进来的事件进行处理,若eventFilter返回true表示事件处理完毕,返回false则返回给被观察者S进行处理。

见图2-13。
在这里插入图片描述

示例2.22:事件过滤器的使用

#include <QApplication>
#include<QWidget>
#include<QMouseEvent>
#include<QPushButton>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{public:  //该类的对象用作过滤器对象,使用事件过滤器需继承QObject
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress)
                {cout<<w->objectName().toStdString(); //验证w为事件本应到达的目标对象
                cout<<"=Ak"<<endl;
                    return 1;  //返回1表示该事件不再进一步处理
                }
                return 0;}  };  /*返回0表示其余事件交还给目标对象处理,本例应返回0,否则添加了该过滤器的安钮会无法显示。*/
class B:public A{public:   //继承自类A
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress){
cout<<w->objectName().toStdString()<<"=Bk"<<endl;
return 0;}
return 0;}    };
class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}};
class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}};

int main(int argc, char *argv[]){
QApplication a(argc,argv);
//创建对象,注意:本例父对象应先创建,以避免生命期过早结束
    A ma;       B mb;        C mc;        D *pd=new D;        D *pd1=new D;
    pd->setText("AAA");    pd->move(22,22);      pd1->setText("BBB");    pd1->move(99,22);
    //设置对象名
    ma.setObjectName("ma");       mb.setObjectName("mb");       mc.setObjectName("mc");
    pd->setObjectName("pd");       pd1->setObjectName("pd1");
    //设置父对象
    pd->setParent(&mc);    pd1->setParent(&mc);
    mb.setParent(&ma);    //①
     //注册过滤器对象
    pd->installEventFilter(&mb);  //②
    pd1->installEventFilter(&ma); //③

mc.resize(333,222);    mc.show();    a.exec();
return 0;     }

程序运行结果及说明(见图2-14)

在这里插入图片描述

当用鼠标按下按钮AAA时,输出pd=Bk和Dk。因为按钮AAA安装的过滤器对象为mb,因此由mb的eventFilter函数处理该事件,输出pd=BK,此时mb::eventFilter()返回0,表示此事件需作进一步处理,于是把该事件传递给目标对象处理(即pd所指向的对象),注意:本例虽然为mb设置了父对象ma,但事件并不会传递给父对象处理,而是返回给目标对象。此时调用D::mousePressEvent函数,输出Dk,至此事件处理结束。用鼠标按下按钮BBB输出pd1=Ak的原理略(较简单)


示例2.23:添加多个事件过滤器

#include <QApplication>
#include<QWidget>
#include<QMouseEvent>
#include<QPushButton>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{public:  //该类的对象用作过滤器对象,使用事件过滤器需继承QObject
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress)
                {cout<<w->objectName().toStdString(); //验证w为事件本应到达的目标对象
                cout<<"=Ak"<<endl;
                    return 1;  //返回1表示该事件不再进一步处理
                }
                return 0;}  };  /*返回0表示其余事件交还给目标对象处理,本例应返回0,否则添加了该过滤器的安钮会无法显示。*/
class B:public A{public:   //继承自类A
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress){
cout<<w->objectName().toStdString()<<"=Bk"<<endl;
return 0;}
return 0;}    };
class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}};
class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}};

int main(int argc, char *argv[]){
QApplication a(argc,argv);
//创建对象,注意:本例父对象应先创建,以避免生命期过早结束
    A ma;       B mb;        C mc;        D *pd=new D;        D *pd1=new D;
    pd->setText("AAA");    pd->move(22,22);      pd1->setText("BBB");    pd1->move(99,22);
    //设置对象名
    ma.setObjectName("ma");       mb.setObjectName("mb");       mc.setObjectName("mc");
    pd->setObjectName("pd");       pd1->setObjectName("pd1");
    //设置父对象
    pd->setParent(&mc);    pd1->setParent(&mc);
    mb.setParent(&ma);    //①
     //注册过滤器对象
    pd->installEventFilter(&mb);  //②
    pd1->installEventFilter(&ma); //③

mc.resize(333,222);    mc.show();    a.exec();
return 0;     }

程序运行结果及说明(见图2-15)

在这里插入图片描述

按钮AAA安装的过滤器对象依次为ma,mb,mc,mc1,
因此按下鼠标时,依次调用对象mc1,mc,mb,ma(即逆序)
的eventFilter函数,需要注意的是:当安装了多个事件过滤
器之后,eventFilter函数返回0并不会使事件返回给目标对
象,而是传递给下一个过滤器对象,当所有过滤器对象都不处理该事件时才会传递给目标对象。

https://www.cnblogs.com/ybqjymy/p/13780235.html

====================

注:

如果不采用“注册自定义类型”,以及“Qt事件过滤器”,那么,编译器就要遍历所有的可能,这样会使得编译器的效率变低,非常低;甚至是不可能。?

比如,不同的自定义类型变量的总量,是近乎无限的,不可能实现自动遍历。?

因此,自定义类型注册与事件过滤器安装,可以让 IDE,编译器等等的设计变得可行,高效,简单。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值