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

相关文章

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

QT实现TCP客户端自动连接

《QT实现TCP客户端自动连接》这篇文章主要为大家详细介绍了QT中一个TCP客户端自动连接的测试模型,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录版本 1:没有取消按钮 测试效果测试代码版本 2:有取消按钮测试效果测试代码版本 1:没有取消按钮 测试效果缺陷:无法手动停

基于Qt实现系统主题感知功能

《基于Qt实现系统主题感知功能》在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观,Qt作为一个跨平台的C++图形用... 目录【正文开始】一、使用效果二、系统主题感知助手类(SystemThemeHelper)三、实现细节

Qt实现文件的压缩和解压缩操作

《Qt实现文件的压缩和解压缩操作》这篇文章主要为大家详细介绍了如何使用Qt库中的QZipReader和QZipWriter实现文件的压缩和解压缩功能,文中的示例代码简洁易懂,需要的可以参考一下... 目录一、实现方式二、具体步骤1、在.pro文件中添加模块gui-private2、通过QObject方式创建

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设