本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!