第二章节 Qt的元对象系统

目录

一、元对象系统

1、QMetaObject定义:

2、分析QObject类:

二、信号与槽

1、定义信号,如:

2、发射信号,如:

3、常用格式:

4、同时信号与槽连接方式:

5、信号与槽的关联方式有如下特点:

 7、断开该连接:

三、动态属性系统

1、Q_PROPERTY()原型:

2、示例:


一、元对象系统

元对象系统是一个基于标准C++的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。

元对象系统的三个基本条件:类必须继承自QObject、类声明Q_OBJECT宏(默认私有有)、元对象编译器moc。信号和槽机制是 QT 的核心机制,信号和槽是一种高级接口,应用于对象之间的通信,它是QT的核心特性,信号和槽是QT自行定义的一种通信机制,它独立于标准的 C/C++ 语言,要正确的处理信号和槽,必须借助一个称为 moc(Meta-Object Compiler),也就是“元对象编译器”。,它为高层次的事件处理自动生成所需要的必要代码。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果moc发现在一个类头文件中包含了宏 Q_OBJECT,则会生成以moc_className.cpp(自定义类名)的.cpp文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。新的文件同样将进入编译系统,与原文件一起参与编译。构建生成的.o文件包含moc生成的cpp文件。

Q_OBJECT定义在qobjectdefs.h文件中:

分析Q_OBJECT后,发现都是在操作元对象,并没有所谓的信号和槽,属性等内容,很显然,QObject对象能够支持这些功能,必然是通过QMetaObject这个元对象来实现的。

1、QMetaObject定义:

Qt元对象系统为何一定要继承自QObject?

2、分析QObject类:

所以,QObject之所以为元对象系统的基类,因其提供了元对象系统很多支持工作:

信号与槽、事件处理、属性设置、国际化支持(翻译)、对象树资源管理等等。

使用QObject作为基类而不使用Q_OBJECT宏和元对象代码是可以的,但是如果Q_OBJECT宏没有被使用,那么这个类声明的信号和槽,以及其他特征描述都不会被调用。

元对象系统除实现信号与槽外,还有很多其他特性,比如:            

QObject::metaObject()返回与该类绑定的meta-object对象。

QMetaObject::className()可以在运行时以字符串的形式返回类的名字,不需要C++编译器原生的运行时类型信息(RTTI)的支持。

QObject::inherits()函数返回继承信息:对象是否是QObject继承树上一个类的实例。

QObject::tr()和QObject::trUtf8()提供国际化支持,将字符串翻译成指定的语言。

QObject::setProperty()和QObject::property()通过名字动态设置和获取对象属性。

QMetaObject::newInstance()构造该类的一个新实例。

qObject_cast()动态类型转换。

一般建议在QObject的所有子类中使用Q_OBJECT宏,而不管它们是否使用了信号与槽。

二、信号与槽

GUI用户界面中,当用户操作一个窗口部件时,需要其他窗口部件响应,传统方式经常使用callback(回调机制)来实现。所谓回调即事先将函数指针作为参数传递另一个函数,然后在函数处理过程中适当地方调用回调函数回调机制有两个缺陷:类型不安全,不能保证调用函数过程中使用正确的参数;强耦合,处理函数必须知道调用哪个回调函数。Qt的信号与槽机制是类型安全的,松耦合,更灵活,更方便。

信号与槽(Signal & Slot)是 Qt 编程的基础,也是 Qt 的一大创新。因为有了信号与槽的编程机制,在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。
    信号(Signal)就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号。发射信号使用Qt的emit关键字。QT 的 signals 关键字指出进入了信号声明区,随后即可声明自己的信号。

1、定义信号,如:

2、发射信号,如:

槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以声明在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。

声明三个槽方法,如:

槽方法中处理需要处理的工作,如:

注意:使用qDebug()输出信息时注意添加头文件:

GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。

信号与槽关联是用 QObject::connect() 函数实现:

QMetaObject::Connection QObject::connect(

const QObject *sender,             //信号发送者

const char *signal,                     //发送的信号

const QObject *receiver,           //信号接收者

const char *method,                  //表示与信号连接的方式的字符串,可以是槽或信号

Qt::ConnectionType type = Qt::AutoConnection    //连接方式,默认自动连接

)

3、常用格式:

connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

用来表示信号和槽的参数都是字符串,Qt提供了两个宏用于构造这样的字符串:对于信号使用SIGNAL,对于槽则使用SLOT,用它们将函数的原型包围起来即可。注意connect方法采用SIGNAL()及SLOT()时,这里的函数原型只能写出类型,不能有任何参数名,否则连接将会失败。

4、同时信号与槽连接方式:

1)Qt::AutoConnection:(默认连接方式)自动方式,由系统自动选择连接方式。
       2)Qt::DirectConnection:直接方式,信号发射时,立即调用槽。

3)Qt::QueuedConnection:队列方式,信号发射时产生一个事件进入队列,事件被处理时槽才能调用。

4)Qt::BlockQueuedConnection:阻塞队列方式,信号发射时产生一个事件进入队列,然后当前线程进入阻塞状态,直到事件处理完毕,若接收方位于发送信号的线程中,则程序会死锁,故此连接方式仅用于多线程。

信号可以看做是特殊的函数,需要带括号,可带参数,信号无需实现也不能实现。槽函数需要带括号,有参数时还需要指明参数。当信号和槽函数带有参数时,在 connect()函数里,要写明参数的类型。信号的参数需与槽的参数列表一致,允许比槽参数多。如果不匹配或参数过少,会出现编译错误或运行错误。在使用信号与槽的类中,必须在类的定义中加入宏 Q_OBJECT。当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。

5、信号与槽的关联方式有如下特点:

  • 一个信号连接一个槽:
connect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));

如:

  • 一个信号连接一个信号:
connect(sender, SIGNAL(single1()), receiver, SIGNAL(single2()));

如:

  • 一个信号连接多个槽,关联信号的槽函数按照建立连接时的顺序依次执行:
connect(sender, SIGNAL(single1()), receiver1, SLOT(slotFun()));
connect(sender, SIGNAL(single1()), receiver2, SLOT(slotFun()));

connect(sender, SIGNAL(single1()), receiver3, SLOT(slotFun()));

如:

  • 多个信号连接一个槽:
connect(sender1, SIGNAL(single1()), receiver, SLOT(slotFun()));
connect(sender2, SIGNAL(single2()), receiver, SLOT(slotFun()));
connect(sender3, SIGNAL(single3()), receiver, SLOT(slotFun()));

如:

  • 信号与槽的自动关联:

       ui_xxxx.h文件中 connectSlotsByName()方法通过对象名支持信号与槽的自动关联。不采用connect()函数而是采用on_objectName_signal命名方式命名槽达到自动关联的效果。

如类头文件中声明槽方法:

信号与槽的断开关联:

QObject::disconnect(const QObject* sender,const char* signal,const QObject *receiver,const char* method);
  • 断开与一个对象所有的信号的所有关联
disconnect(sender,0,0,0);  

等价于:

sender->disconnect();
  • 断开与一个指定信号的所有关联
disconnect(sender, SIGNAL(single1()), 0, 0);

等价于:

sender->disconnect(SIGNAL(single1()));
  •  断开与一个指定接受者receiver的所有关联
disconnect(sender, 0, receiver, 0);

等价于:

sender->disconnect(SIGNAL(single1()));
  • 断开指定信号与槽的关联:
disconnect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));

等价于:

disconnect(myConnection);  //myConnection为connect()的返回值

如:

xxx.h文件中添加m_res变量用于保存connect()返回值。

连接信号与槽:

 7、断开该连接:

信号与槽机制的优越性:

  1. 信号与槽机制是类型安全的,相关联的信号与槽参数必须匹配
  2. 信号与槽是松耦合的,信号发送者不知道也不需知道接受者的信息。
  3. 信号与槽可以使用任意类型的任意数量的参数。

三、动态属性系统

在标准C++中,为了保证封装性,我们经常声明一个私有变量,然后声明两个公有函数,即set函数和get函数。在Qt中我们可以使用宏Q_PROPERTY()宏来实现这些。一个属性可以使用常规函数QObject::property()和QObject::setProperty()进行读写,不用知道属性所在类的任何细节,除了属性的名字。

1、Q_PROPERTY()原型:

Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
              MEMBER memberName [(READ getFunction | WRITE setFunction)])
             [RESET resetFunction]
             [NOTIFY notifySignal]
             [REVISION int]
             [DESIGNABLE bool]
             [SCRIPTABLE bool]
             [STORED bool]
             [USER bool]
             [CONSTANT]
             [FINAL])

2、示例:

  1. 新建桌面应用程序TestProperty,父类QWidget,其他采用默认。
  2. 右键单击项目添加自定义类MyPropertyClass,父类QObject.
  3. mypropertyclass.h文件中Q_OBJECT下方声明属性宏:

        4.声明属性读取、设置函数,定义属性变更时发送的信号,定义成员变量m_mask保存属性值。

        5.widget.h添加槽函数声明

widget.cpp添加头文件:添加槽方法定义:

        6.Widget类构造函数中添加如下代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的鱼-blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值