【Qt元对象系统解析】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 概述
  • 一、元对象系统
  • 二、信号与槽
    • 1.connect()函数的不同参数形式
    • 2. 使用 sender()获得信号发射者
    • 3. 自定义信号及其使用


概述

Qt实质上是一个跨平台的C++开发类库,是用标准 C++编写的类库,它为开发GUI应用程序和非 GUI应用程序提供了各种类。Ot对标准 C++进行了扩展,引入了一些新的概念和功能,例如信号与槽、对象属性等。Qt的元对象编译器(Meta-ObjectCompiler,MOC)是一个预处理器,在源程序被编译前先将这些Qt 特性的程序转换为标准 C++兼容的形式,然后再由标准C++编译器进行编译。这就是为什么在使用信号与槽机制的类里,必须添加一个Q_OBJECT宏的原因,只有添加了这个宏,moc才能对类里的信号与槽的代码进行预处理。Qt Core 模块是 Qt 类库的核心,所有其他模块都依赖于此模块,如果使用 qmake 来构建项目,QtCore 模块则是被自动加入的。Qt为 C++语言增加的特性就是在 QtCore 模块里实现的,这些扩展特性由 Qt的元对象系统实现包括信号与槽机制、属性系统、动态类型转换等。


一、元对象系统

Qt的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
元对象系统由以下三个基础组成:
1、QObject 类是所有使用元对象系统的类的基类。
2、类如果要使用元对象的特性,比如信号与槽、动态属性,必须在类的private部分声明Q_OBJECT宏。
3、MOC(元对象编译器)为每个QObiect的子类提供必要的代码来实现元对象系统的特性。构建项目时,MOC 工具读取C++源文件,当它发现类的定义里有QOBJECT宏时,它就会为这个类生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被编译和连接。

除了信号与槽机制,元对象还提供其他功能:
QObject::metaObject()函数返回类关联的元对象,元对象类 QMetaObject 包含了访问元对象的一些接口函数,例如 QMetaObject::className()函数可在运行时返回类的名称字符串。

QObject *obj = new QPushButton;
obj->meta0bject()->className();
//返回"QPushButton"

QMetaObject::newInstance()函数创建类的一个新的实例。
QObject::inherits(const char *className)函数判断一个对象实例是否是名称为 className 的类或 QObject的子类的实例。例如:

// QTimer 是Q0bject 的子类
QTimer *timer = new imer;
//返回 true
timer->inherits("QTimer");
//返回 true
timer->inherits("QObject");
//返回 false,不是QAbstractButton 的子类
timer->inherits("QAbstractButton");

QObject::tr()和 QObject::trUtf8()函数可翻译字符串,用于多语言界面设计。
QObject::setProperty()和 QObject::property()函数用于通过属性名称动态设置和获取属性值。
对于 QObject及其子类,还可以使用 qobject_cast()函数进行动态投射(dynamic cast)。例如,假设 QMyWidget 是 QWidget 的子类并且在类定义中声明了Q_OBJECT宏。创建实例使用下面的语句:

QObject *obj = new QMywidget;

变量 obj 定义为 QObject 指针,但它实际指向 QMyWidget 类,所以可以正确投射为QWidget,即:

QWidget *widget = qobject_cast<Qwidget*>(obj);

从 QObiect 到 QWidget 的投射是成功的,因为obj实际是 QMyWidget 类,是 QWidget 的子类。也可以将其成功投射为 QMyWidget,即:

QMyWidget *mywidget = qobject_cast<QMywidget *>(obj);

投射为 QMyWidget是成功的,因为qobject_cast()并不区分 Qt 内建的类型和用户自定义类型。但是,若要将 obj 投射为 QLabel 则是失败的,即:

QLabel *label = qobject_cast<QLabel *>(obj);

这样投射是失败的,返回指针 label为NULL,因为QMyWidget 不是 QLabel 的子类。
使用动态投射,使得程序可以在运行时对不同的对象做不同的处理。

二、信号与槽

1.connect()函数的不同参数形式

QObject::connect()函数有多重参数形式,一种参数形式的函数原型是:

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal
const QObject *receiver, const char *method,Qt: :ConnectionType type = Qt: :AutoConnection)

使用这种参数形式的connectO)进行信号与槽函数的连接时,一般句法如下:

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

这里使用了宏 SIGNAL()和 SLOT()指定信号和槽函数,而且如果信号和槽函数带有参数,还需注明参数类型,如:

connect(spinNum, SIGNAL(valueChanged (int)), this, SLOT(updateStatus(int));

另外一种参数形式的connect()函数的原型是:

QMetaObject::Connection Q0bject::connect(const Q0bject* sender, const 0MetaMethod& signal, const QObject* receiver, const QMetaMethod& method, Qt::ConnectionType type = Qt::AutoConnection)

对于具有默认参数的信号与槽(即信号名称是唯一的,没有参数不同而同名的两个信号),可以使用这种函数指针形式进行关联,如:

connect(lineEdit, &QLineEdit::textChanged, this, &widget::on_textChanged) ;

QLineEdit 只有一个信号 textChanged(QString),在自定义窗体类 widget 里定义一个槽函数on_textChanged(QString),就可以用上面的语句将此信号与槽关联起来,无需出现函数参数。这在信号的参数比较多时更简便一些。
而对于具有不同参数的同名信号就不能采用函数指针的方式进行信号与槽的关联,例如QSpinBox有两个valueChanged()信号,分别是:

void QSpinBox::valueChanged(int i)
void QSpinBox::valueChanged(const QString &text)

即使在自定义窗体 widget 里定义了一个槽函数,如:

void onValueChanged(int i);

在使用下面的语句进行关联时,编译会出错。

connect(spinNum, &QSpinBox::valueChanged, this, &widget::onValuechanged);

不管是哪种参数形式的 connect()函数,最后都有一个参数 Qt::ConnectionType type,缺省值为Qt::AutoConnection。枚举类型 Qt::ConnectionType 表示了信号与槽之间的关联方式,有以下几种取值。
1、Qt::AutoConnection(缺省值):如果信号的接收者与发射者在同一个线程,就使用 Qt::DirectConnection方式;否则使用 Qt:QueuedConnection方式,在信号发射时自动确定关联方式。
2、Qt:DirectConnection:信号被发射时槽函数立即执行,槽函数与信号在同一个线程。
3、Qt::QueuedConnection:在事件循环回到接收者线程后执行槽函数,槽函数与信号在不同的线程。
4、Qt:BlockingQueuedConnection:与Qt::QueuedConnection 相似,只是信号线程会阻塞直到槽函数执行完毕。当信号与槽函数在同一个线程时绝对不能使用这种方式,否则会造成死锁。

2. 使用 sender()获得信号发射者

在槽函数里,使用 QObject:sender()可以获取信号发射者的指针。如果知道信号发射者的类型,可以将指针投射为确定的类型,然后使用这个确定类的接口函数。
例如,在 QSpinBox 的 valueChanged(int )信号的槽函数里,可以通过 sender()和 qobject_cast获得信号发射者的指针,从而对信号发射者进行操作。

SpinBox* spinBox = qobject_cast<QSpinBox*>(sender());

3. 自定义信号及其使用

在自己设计的类里也可以自定义信号,信号就是在类定义里声明的一个函数,但是这个函数无需实现,只需发射(emit)。
例如,在下面的自定义类 QPerson 的 signals 部分定义一个信号 ageChanged(int )。

class QPerson : public QObject 
{
	Q_OBJECT
private:
	int m_age=10;
public :
	void incAge();
signals :
	void ageChanged( int value);
}

信号函数必须是无返回值的函数,但是可以有输入参数。信号函数无需实现,只需在某些条件下发射信号。例如,在incAge()函数中发射信号,其代码如下。

void QPerson::incAge()
{
	m age++;
	emit ageChanged(m age);//发射信号
}

在 incAgeO)函数里,当私有变量 m age 变化后,发射信号 ageChanged(int),表示年龄发生了变化。至于是否有与此信号相关联的槽函数,信号发射者并不管。如果在使用QPerson 类对象的程序中为此信号关联了槽函数,在incAge0)函数里发射此信号时,就会执行相关联的槽函数。至于是否立即执行槽函数,发射信号的线程是否等待槽函数执行完之后再执行后面的代码,与connect()函数设置信号与槽关联时设置的连接类型以及信号与槽是否在同一个线程有关。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/598110.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring @Repository 注解

Spring 的项目严重依赖注解。 Repository 注解 在Spring2.0之前的版本中&#xff0c;Repository注解可以标记在任何的类上&#xff0c;用来表明该类是用来执行与数据库相关的操作(即dao对象)&#xff0c;并支持自动处理数据库操作产生的异常 在Spring2.5版本中&#xff0c;引…

3度带6度带中央经线及带号换算问题

前言&#xff1a;3度带6度带中央经线及代号换算问题是比较繁琐的一个问题&#xff0c;不经常用&#xff0c;公式记不住&#xff0c;因此本文特此整理&#xff0c;以备查。 1.基本概况 我国基本比例尺地形图除1&#xff1a;100万采用兰勃特投影&#xff08;lambert&#xff09;外…

信息系统项目管理师0090:项目经理的影响力范围(6项目管理概论—6.3项目经理的角色—6.3.2项目经理的影响力范围)

点击查看专栏目录 文章目录 6.3.2项目经理的影响力范围1.概述2.项目3.组织4.行业5.专业学科6.跨领域6.3.2项目经理的影响力范围 1.概述 项目经理在其影响力范围内可担任多种角色,这些角色反映了项目经理的能力,体现了项目经理的价值和作用,项目经理会涉及项日、组织、行业、…

爬虫案例:股吧,使用Selenium

爬虫案例:股吧,使用Selenium 实用工具关注公众号爬虫探索者获取。 发送对应关键词: 1.运行环境 pip install selenium==4.9.1 pip install pymysql1.1.高版本Chrome问题 针对于高版本的Chrome浏览器 1.2.最终效果 2.目标网站 3.参数介绍 BASE_URL = https://guba.eas…

ppp和ppp mp理论实验

ppp简介 PPP&#xff08;点对点协议&#xff09;为在点对点连接上传输多协议数据包提供了一个标准方法&#xff0c;是数据链路层封装协议的一种方法&#xff0c;支持同步和异步两种传输方式。&#xff08;除了PPP还有HDLC等&#xff0c;不过HDLC只支持同步方式&#xff09; P…

信号与进程(3):信号及其使用

信号及其使用 参考博客 Linux信号的产生和处理 信号及其使用 信号的产生 信号由内核产生&#xff0c;信号的生成与事件的发生相关&#xff0c;事件的发生源有3类&#xff1a; 1、用户 用户在终端上按下某些按键时会产生信号&#xff0c;如**CtrlC产生SIGINT信号&#xff0…

springboot+mp自动生成没有实体类

mybatisX版本冲突问题 一开始我的MyBatisX版本是1.6.1-3,使用mybatis-plus一直不能正常生成实体类 将MyBatisX的版本换成了1.5.7就可以了 MyBatisX版本更换 1.将原有的MyBatisX卸载后重新安装一个新的版本 2.选择一个合适的版本,这里我选的是1.5.7 下载完成后自己选择一个…

【隧道篇 / WAN优化】(7.4) ❀ 02. WAN优化的作用 ❀ FortiGate 防火墙

【简介】看了上一篇文章&#xff0c;相信大家都知道了在防火墙上启动WAN优化的方法&#xff0c;但是WAN优化到底能做什么&#xff1f;相信有很多人想了解。 什么是WAN优化 现在有许多企业通过集中应用程序或在云中提供应用程序来降低成本并整合资源。应用程序在本地局域网内都能…

python多标签图像分类的图片相册共享交流系统vue+django

建立图片共享系统&#xff0c;进一步提高用户对图片共享信息的查询。帮助用户和管理员提高工作效率&#xff0c;实现信息查询的自动化。使用本系统可以轻松快捷的为用户提供他们想要得到的图片共享信息。 根据本系统的基本设计思路&#xff0c;本系统在设计方面前台采用了pytho…

springboot 引入第三方bean

如何进行第三方bean的定义 参数进行自动装配

CH32V 系列 MCU IAP 使用函数形式通过传参形式灵活指定APP跳转地址

参考: CH32V 系列 MCU IAP 升级跳转方法 CH32V103 的 IAP 问题&#xff08;跳转及中断向量表重定位&#xff09; 1. 沁恒的RISC-V内核MCU的IAP跳转示例程序简要分析 沁恒的RISC-V内核的MCU比如CH32V203、CH32V307等系列的EVT包中IAP升级的示例程序中都是通过使能软件中断之后&…

先进制造业数字化转型,为什么基于传统存储无法完成?

本文是 XSKY 智能存储方案助力先进制造数字化转型系列文章中的第一篇&#xff0c;重点分享先进制造行业数字化转型过程中&#xff0c;对于数据存储的需求&#xff0c;以及为何传统存储架构无法很好满足这些需求。 随着智能制造的发展&#xff0c;自动化、信息化、智能化等技术…

linux命令——fdisk分区

在linux中&#xff0c;一切皆文件&#xff0c;硬盘设备在系统中也以文件形式存在&#xff0c;使用fdisk命令可以查看硬盘分区信息 并非硬盘转速越快&#xff0c;文件读取速率越高&#xff0c;有一个文件存储密度的概念

数据合规官认证证书CCRC-DCO使用设计和默认数据保护处理个人数据

快来了解隐私保护工程实践&#xff01;合法原则是关键&#xff0c;一起守护数据安全&#xff01; 隐私保护工程实践需要遵循合法原则&#xff0c;控制者必须确保处理个人数据有明确的法律依据。在设计和默认数据保护中&#xff0c;相关性、差异化、特定目的、必要性和自主权是合…

在ATECLOUD测试平台测试新能源车内连接器

在测试车内连接器的温度时&#xff0c;需要用到直流电源和温度巡检仪&#xff0c;通过温度巡检仪采集连接器工作时的温度。由于用户在测试时会用到多台直流电源和温度巡检仪&#xff0c;并且型号不一样。因此&#xff0c;在用ATECLOUD测试连接器温度时&#xff0c;技术工程师需…

框架漏洞RCE-1

一、前提 1、命令执行漏洞&#xff1a;直接调用操作系统命令。攻击者构造恶意命令&#xff0c;将命令拼接到正常的输入中&#xff0c;达到恶意攻击的目的。 (1)、常见命令执行函数 PHP&#xff1a;exec、shell_exec、system、passthru、popen、proc_open、反引号等 ASP.N…

拉普拉斯丨独家冠名2024年度ATPV技术分论坛,助力产业科技持续创新

为了进一步促进行业技术交流&#xff0c;推进光伏行业发展及标准建设的进程&#xff0c;针对高效电池&#xff0c;领跑组件&#xff0c;新产品认证及应用等技术专题及国内外光伏标准的最新进程&#xff0c;由中国绿色供应链联盟光伏专委会&#xff08;ECOPV&#xff09;指导的2…

Java 运行的底层原理

Java是一种跨平台的编程语言&#xff0c;其底层原理涉及到了多个方面&#xff0c;包括Java虚拟机&#xff08;JVM&#xff09;、字节码、类加载机制、垃圾回收器等。让我们逐一深入了解Java运行的底层原理。 1. Java虚拟机&#xff08;JVM&#xff09; Java虚拟机是Java程序运…

Java代码基础算法练习-年龄问题-2024.05.07

数学家维纳智力早熟&#xff0c;11岁就上了大学。一次&#xff0c;他参加某个重要会议&#xff0c;年轻的脸孔引人注目。于是有人询问他的年龄&#xff0c;他回答说&#xff1a;“我年龄的立方是个4位数。我年龄的4次方是个6位数。这10 个数字正好包含了从0到9这10个数字&#…
最新文章