Qt类的元对象MetaObject

2023-12-17 15:38
文章标签 qt 对象 metaobject

本文主要是介绍Qt类的元对象MetaObject,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Qt类的元对象MetaObject

  • 核心特点
    • 元对象系统
      • 属性系统
      • 信息与槽
      • 实际使用Qt的元对象

今天又重新看了一下这里的内容,总结一下:
也就是说,这个类继承到最后一定是一个QObject类才行,才能调用元对象系统。在调用元对象之前可以对自己设置的类进行类的属性的设置,是通过宏定义Q_PROPERTY的方式来进行的,比如:
QWidget类定义属性的一些例子如下:

Q_PROPERTY(bool  focus  READ  hasFocus)
Q_PROPERTY(bool  enabled  READ  isEnabled  WRITE  setEnabled)
Q_PROPERTY(QCursor  cursor  READ cursor WRITE setCursor RESET unsetCursor)

这样我们就定义的属性的一些信息。然后我们就可以调用到元系统的一些信息,首先我们要声明一个元对象,然后就可以使用父类的指针指向子类的对象,来调用自己想修改的属性信息。比如:

QPushButton *button = new QPushButton;
QObject *object = button;
object->setProperty("flat", true);
bool  isFlat= object->property("flat");

这样我们就可以修改到当前的元对象的一些信息了。
动态增加属性信息。如果我们需要对这个类增加一些新的属性的信息,我们是可以通过动态来增加的,不一定需要通过重新构造一个类来,可以通过QObject::setProperty()函数,是在运行的时候来增加的,因此也称为动态属性。
动态属性可以使用QObject::property()查询,就如在类定义里用Q_PROPERTY宏定义的属性一样。如:

editName->setProperty("required", "true");//增加了required属性
comboSex-> setProperty("required", "true");//增加了required属性
checkAgree-> setProperty("required", "true");//增加了required属性

还有一点是,我们可以对一个类增加附加信息,也就是相当于备注一样,要使用到宏Q_CLASSINFO()。
如:

class QMyClass : public QObject{Q_OBJECTQ_CLASSINFO("author", "Wang")//备注了作者的信息Q_CLASSINFO("company", "UPC")//备注了公司的信息Q_CLASSINFO("version ", "3.0.1")//备注了版本的信息public:...};

怎么拿到自己的备注的信息呢?
可以通过:

QMetaClassInfo QMetaObject::classInfo(int index) const

返回值是QMetaClassInfo类型,有name()和value()两个函数,可获得类附加信息的名称和值。
这样我们就完全知道了元系统的作用,就是对所有的类,如果终其父类是QObject,那么我们就可以使用这个元系统,元系统中包括我们使用的信息和槽,这也就是为什么,我们的信息和槽的时候继承的类一定要来自QObject类。

核心特点

元对象系统

Qt的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
QObject类是所有使用元对象系统的类的基类。
1.QObject类是所有使用元对象系统的类的基类。
2.在一个类的private部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
3.MOC(元对象编译器)为每个QObject的子类提供必要的代码来实现元对象系统的特性。

使用:
QObject::metaObject()函数返回类关联的元对象,元对象类QMetaObject包含了访问元对象的一些接口函数,例如QMetaObject::className()函数可在运行时返回类的名称字符串。

QObject *obj = new QPushButton;
obj->metaObject()->className();    // 返回 "QPushButton"
//元对象的函数的测试
void test01()
{QObject *obj=new QPushButton;qDebug()<<obj->metaObject()->className();//打印类信息的名字qDebug()<<obj->metaObject()->classInfoCount();qDebug()<<obj->metaObject()->classInfoOffset();QTimer *timer=new QTimer;QObject *obj1=obj->metaObject()->newInstance();//创建一个新得实例QString str="hello";qDebug()<<QObject::tr(str.toStdString().c_str());qDebug()<<timer->inherits("QTimer");//判断一个对象实例是否是类或者子类qDebug()<<timer->inherits("QObject");qDebug()<<timer->inherits("QAbstractButton");char* s="dffdf";qDebug()<<QObject::tr(str.toStdString().c_str());qDebug()<<QObject::trUtf8(s);//动态的投射QObject *obj2=new QWidget;QWidget *widget=qobject_cast<QWidget*>(obj2);}

属性系统

//属性系统
void test02()
{
//1.定义属性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] )
//            READ指定一个读取属性值的函数,没有MEMBER关键字时必须设置READ。
//            WRITE指定一个设定属性值的函数,只读属性没有WRITE设置。
//            MEMBER指定一个成员变量与属性关联,成为可读可写的属性,无需再设置READ和WRITE。
//            RESET是可选的,用于指定一个设置属性缺省值的函数。
//            NOTIFY是可选的,用于设置一个信号,当属性值变化时发射此信号。
//            DESIGNABLE表示属性是否在Qt Designer里可见,缺省为true。
//            CONSTANT表示属性值是一个常数,对于一个对象实例,READ指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有CONSTANT关键字的属性不能有WRITE和NOTIFY关键字。
//            FINAL表示所定义的属性不能被子类重载。//如 QWidget的
//            Q_PROPERTY(bool  focus  READ  hasFocus)
//            Q_PROPERTY(bool  enabled  READ  isEnabled  WRITE  setEnabled)
//            Q_PROPERTY(QCursor  cursor  READ cursor WRITE setCursor RESET unsetCursor)//2.属性的使用:QPushButton *button = new QPushButton;QObject *object = button;object->setProperty("flat", true);//设置属性值QVariant  isFlat= object->property("flat");//读取属性值qDebug()<<isFlat;//3.动态属性:使用setPropertry()函数在类运行的时候定义一个新的属性//定义一个必填的字段,定义一个新的属性required属性,并设置为true//editName->setProperty("required",true);//comboSex->setProperty("required",true);//checkAgree-> setProperty("required", "true");//*[required="true"] {background-color: lime}  样式设置,将背景颜色设置为亮绿色//4.类的附加信息:属性系统还有一个宏Q_CLASSINFO(),可以为类的元对象定义“名称——值”信息//Q_CLASSINFO("author","DOU")//Q_CLASSINFO("version","3.0.1")
}

信息与槽

//信息与槽
void test03()
{
//1.connect函数形式
//    1.1connect(发送信号者的指针,信号的指针,接收者的指针的信息,处理的槽函数的指针);
//    1.2使用了宏SIGNAL()和SLOT()指定信号和槽函数,而且如果信号和槽函数带有参数,还需注明参数类型
//    没有参数的时候connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
//    有参数的时候connect(spinNum, SIGNAL(valueChanged (int)), this, SLOT(updateStatus(int));
//    1.3connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)//    connect()函数,最后都有一个参数Qt::ConnectionType type,缺省值为Qt::AutoConnection。枚举类型Qt::ConnectionType表示了信号与槽之间的关联方式,有以下几种取值。    
//    Qt::AutoConnection(缺省值):如果信号的接收者与发射者在同一个线程,就使用Qt::Direct Connection方式;否则使用Qt::QueuedConnection方式,在信号发射时自动确定关联方式。
//    Qt::DirectConnection:信号被发射时槽函数立即执行,槽函数与信号在同一个线程。
//    Qt::QueuedConnection:在事件循环回到接收者线程后执行槽函数,槽函数与信号在不同的线程。
//    Qt::BlockingQueuedConnection:与Qt::QueuedConnection相似,只是信号线程会阻塞直到槽函数执行完毕。当信号与槽函数在同一个线程时绝对不能使用这种方式,否则会造成死锁。//2.使用sender()获取信号发射者//在槽函数里,使用QObject::sender()可以获取信号发射者的指针。如果知道信号发射者的类型,可以将指针投射为确定的类型,然后使用这个确定类的接口函数。//QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());//3.自定义信号//在类中信号中定义(不需要实现,但一定要声明)//signals:void    ageChanged( int  value);
}

实际使用Qt的元对象

创建一个元类(person)

person.h

class Person:public QObject//一定要继承于QObject
{//宏,使用信息与槽必须使用Q_OBJECT//设置类的附加信息Q_CLASSINFO("author","DouDou")Q_CLASSINFO("version","1.0.0")//设置类的属性值Q_PROPERTY(int age READ age WRITE setAge NOTUFY ageChanged)Q_PROPERTY(QString name MEMBER m_name)Q_PROPERTY(int score MEMBER m_score)
private://成员int m_age=20;//年龄QString m_name;//名字int m_score=60;//分数
public:explicit Person(QString fName,QObject* parent=nullptr);//构造函数int age();//获取年龄void setAge(int value);//设置年龄void incAge();//修改年龄
signals:void    ageChanged( int  value);//修改年龄的信号函数public slots:
};

person.cpp

#include "qperson.h"QPerson::QPerson(QString fName,QObject *parent) : QObject(parent)
{ //构造函数m_name=fName;
}int QPerson::age()
{ //返回agereturn  m_age;
}void QPerson::setAge(int value)
{//设置agem_age=value;emit ageChanged(m_age); //发射信号
}void QPerson::incAge()
{m_age++;emit ageChanged(m_age);//发射信号
}

//主界面的窗口.h

#ifndef QMYWIDGET_H
#define QMYWIDGET_H#include <QWidget>
#include "qperson.h"namespace Ui {
class QmyWidget;
}class QmyWidget : public QWidget
{Q_OBJECTprivate:QPerson *boy;//创建了一个Person类指针 boyQPerson *girl;//创建了一个Person类指针 girlpublic:explicit QmyWidget(QWidget *parent = 0);~QmyWidget();private:Ui::QmyWidget *ui;signals:private slots:
//自定义槽函数void   on_ageChanged(int  value);           //年龄改变处理函数void   on_spin_valueChanged(int arg1);      //spin输入得时候改变处理函数//界面按钮的槽函数void on_btnClear_clicked();         //清除信息按键void on_btnBoyInc_clicked();        //boy年龄增加按键void on_btnGirlInc_clicked();       //girl年龄增加按键void on_btnClassInfo_clicked();     //类信息显示
};#endif // QMYWIDGET_H

//函数的实现.cpp

#include "qmywidget.h"
#include "ui_qmywidget.h"
#include    <QMetaProperty>
#include <QDebug>QmyWidget::QmyWidget(QWidget *parent) :QWidget(parent),ui(new Ui::QmyWidget)
{//构造函数ui->setupUi(this);//h文件中只是维护了指针,但是没有实际的对象,所以new一个对象boy=new QPerson("王小明");boy->setProperty("score",95);boy->setProperty("age",10);boy->setProperty("sex","Boy");//动态属性,新增加属性
//    connect(boy,SIGNAL(ageChanged(int)),this,SLOT(on_ageChanged(int)));connect(boy,&QPerson::ageChanged,this,&QmyWidget::on_ageChanged);//如果boy的年龄已经改变了,就调用函数on_ageChangedgirl=new QPerson("张小丽");girl->setProperty("score",81);girl->setProperty("age",20);girl->setProperty("sex","Girl");//动态属性connect(girl,&QPerson::ageChanged,this,&QmyWidget::on_ageChanged);ui->spinBoy->setProperty("isBoy",true); //动态属性,给控件增加了属性ui->spinGirl->setProperty("isBoy",false);//  不能使用此形式,因为QSpinBox有两种参数形式的valueChanged()信号
//    connect(ui->spinGirl,&QSpinBox::valueChanged,
//            this,&QmyWidget::on_spinBoy_valueChanged);connect(ui->spinGirl,SIGNAL(valueChanged(int)),this,SLOT(on_spin_valueChanged(int)));connect(ui->spinBoy,SIGNAL(valueChanged(int)),this,SLOT(on_spin_valueChanged(int)));
}QmyWidget::~QmyWidget()
{delete ui;
}//功能,获取的是信号发送者的类的信息
void QmyWidget::on_ageChanged( int value)
{//响应QPerson的ageChanged()信号Q_UNUSED(value);//对没有使用value不告警QPerson *aPerson = qobject_cast<QPerson *>(sender()); //类型投射QString hisName=aPerson->property("name").toString(); //姓名
//    QString hisName=aPerson->name(); //获取姓名,错误,私有属性,没不办法获取到名字,只能通过接口或者属性去获取QString hisSex=aPerson->property("sex").toString(); //动态属性int hisAge=aPerson->age();//通过接口函数获取年龄
//    int hisAge=aPerson->property("age").toInt();//通过属性获得年龄ui->textEdit->appendPlainText(hisName+","+hisSex+QString::asprintf(",年龄=%d",hisAge));
}void QmyWidget::on_btnClear_clicked()
{//"清空文本框"按钮ui->textEdit->clear();
}void QmyWidget::on_btnBoyInc_clicked()
{//"boy长大一岁"按钮boy->incAge();
}void QmyWidget::on_btnGirlInc_clicked()
{//"girl长大一岁"按钮girl->incAge();
}//spin的数值改变之后处理函数
//是spingoy和spingirl的处理函数是同一个,但是我们想他们的处理的函数的时候能够不一样
//所以在spinBox的时候根据属性值再去做处理
void QmyWidget::on_spin_valueChanged(int arg1)
{//响应界面上spinBox的valueChanged(int)信号Q_UNUSED(arg1);QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());//如果发送信号者的属性”isBoy“是真,则去处理不同的逻辑if (spinBox->property("isBoy").toBool())boy->setAge(spinBox->value());elsegirl->setAge(spinBox->value());
}//获取元对象的信息
void QmyWidget::on_btnClassInfo_clicked()
{//"类的元对象信息"按钮
//    const QMetaObject *meta=boy->metaObject();const QMetaObject *meta=girl->metaObject();
//    const QMetaObject *meta=ui->spinBoy->metaObject();//对象转化为元对象QObject *obj=girl;ui->textEdit->clear();ui->textEdit->appendPlainText("==元对象信息==\n");ui->textEdit->appendPlainText(QString("类名称:%1\n").arg(meta->className()));ui->textEdit->appendPlainText("property");qDebug()<<"meta->propertyOffset():"<<meta->propertyOffset();qDebug()<<"meta->propertyCount():"<<meta->propertyCount();//属性信息for (int i=meta->propertyOffset();i<meta->propertyCount();i++){const char* propName=meta->property(i).name();ui->textEdit->appendPlainText(QString("属性名称=%1,属性值=%2").arg(propName).arg(obj->property(propName).toString()));}ui->textEdit->appendPlainText("");ui->textEdit->appendPlainText("classInfo");//类的信息for (int i=meta->classInfoOffset();i<meta->classInfoCount();++i){QMetaClassInfo classInfo=meta->classInfo(i);ui->textEdit->appendPlainText(QString("Name=%1; Value=%2").arg(classInfo.name()).arg(classInfo.value()));}}

效果截图:
在这里插入图片描述

说明:
程序参考于《Qt 5.9 C++开发指南》
程序中增加注释,方便理解和学习。

这篇关于Qt类的元对象MetaObject的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/504908

相关文章

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

Qt QCustomPlot库简介(最新推荐)

《QtQCustomPlot库简介(最新推荐)》QCustomPlot是一款基于Qt的高性能C++绘图库,专为二维数据可视化设计,它具有轻量级、实时处理百万级数据和多图层支持等特点,适用于科学计算、... 目录核心特性概览核心组件解析1.绘图核心 (QCustomPlot类)2.数据容器 (QCPDataC

Qt如何实现文本编辑器光标高亮技术

《Qt如何实现文本编辑器光标高亮技术》这篇文章主要为大家详细介绍了Qt如何实现文本编辑器光标高亮技术,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录实现代码函数作用概述代码详解 + 注释使用 QTextEdit 的高亮技术(重点)总结用到的关键技术点应用场景举例示例优化建议

Qt 设置软件版本信息的实现

《Qt设置软件版本信息的实现》本文介绍了Qt项目中设置版本信息的三种常用方法,包括.pro文件和version.rc配置、CMakeLists.txt与version.h.in结合,具有一定的参考... 目录在运行程序期间设置版本信息可以参考VS在 QT 中设置软件版本信息的几种方法方法一:通过 .pro

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以