属性系统 Qt Creator 帮助文档为《The Property System》
属性系统是基于元对象系统实现的,Qt 的属性系统与C++编译器无关,任何标准的C++编译器都可以编译定义了Qt属性的C++程序。我的理解:C++ 类提供属性(成员变量)和方法,方法操作内部属性,Qt基于元对象定义一种操作类内部属性和方法的方式,即按照Qt属性系统定义属性,方法,就可以按照Qt的方式操作对应的内部属性和方法。
Qt 提供一个 Q_PROPERTY() 宏可以定义属性,在QObject的子类中用宏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])
具体含义: Qt 使用Q_PROPERTY 宏定义一个返回值类型为 type,名称为 name 的属性,用 READ、WRITE 关键字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是 QVariant 支持的任何类型,也可以用户自定义类型。
Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:
1)READ 指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
2)WRITE 指定一个设定属性值的函数,只读属性没有 WRITE 设置。
3)MEMBER 指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
4)RESET 是可选的,用于指定一个设置属性缺省值的函数。
5)NOTIFY 是可选的,用于设置一个信号,当属性值变化时发射此信号。
6)DESIGNABLE 表示属性是否在 Qt Designer 里可见,缺省为 true。
7)CONSTANT 表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
8)FINAL 表示所定义的属性不能被子类重载。
例如下面QWeidgt 的属性定义:
Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
通过QObject::property() 和 QObject::setProperty() 就可以操作对应的属性,无论是否使用READ 或者WRITE 定义方法接口,只要知道属性名称都可以操作,例如:
QPushButton *button = new QPushButton;
QObject *object = button;
button->setDown(true);
object->setProperty("down", true); //等同setDown 为true
bool down = object->Property("down");
同时QObject::setProperty() 函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的,动态属性可以使用 QObject::property() 查询,就如在类定义里用 Q_PROPERTY 宏定义的属性一样。
属性系统还有一个宏Q_CLASSINFO() 可以为类的元对象定义 名称 和 值的信息,如:
Q_CLASSINFO("Version", "3.0.0") //定义
QMetaClassInfo QMetaObject::classInfo(int index) const //调用该函数就可以获取类的附加信息
const char *QMetaClassInfo::name() const //获取附加信息的名称
const char *QMetaClassInfo::value() const //获取附加信息的值
来一个完整的实例,该实例来自《Qt 5.9 c++ 开发指南》这本书:
//qperson.h
#ifndef QPERSON_H
#define QPERSON_H
#include <QObject>
class QPerson : public QObject
{
Q_OBJECT
Q_CLASSINFO("author","Wang")
Q_CLASSINFO("company","UPC")
Q_CLASSINFO("version","1.0.0")
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
Q_PROPERTY(QString name MEMBER m_name)
Q_PROPERTY(int score MEMBER m_score)
private:
int m_age=10;
QString m_name;
int m_score=79;
public:
explicit QPerson(QString fName, QObject *parent = nullptr);
int age();
void setAge(int value);
void incAge();
signals:
void ageChanged( int value);
public slots:
};
#endif // QPERSON_H
//qperson.cpp
#include "qperson.h"
QPerson::QPerson(QString fName,QObject *parent) : QObject(parent)
{ //构造函数
m_name=fName;
}
int QPerson::age()
{ //返回age
return m_age;
}
void QPerson::setAge(int value)
{//设置age
m_age=value;
emit ageChanged(m_age); //发射信号
}
void QPerson::incAge()
{
m_age++;
emit ageChanged(m_age);//发射信号
}
//qmywidget.h
#ifndef QMYWIDGET_H
#define QMYWIDGET_H
#include <QWidget>
#include "qperson.h"
namespace Ui {
class QmyWidget;
}
class QmyWidget : public QWidget
{
Q_OBJECT
private:
QPerson *boy;
QPerson *girl;
public:
explicit QmyWidget(QWidget *parent = 0);
~QmyWidget();
private:
Ui::QmyWidget *ui;
signals:
private slots:
//自定义槽函数
void on_ageChanged(int value);
void on_spin_valueChanged(int arg1);
//界面按钮的槽函数
void on_btnClear_clicked();
void on_btnBoyInc_clicked();
void on_btnGirlInc_clicked();
void on_btnClassInfo_clicked();
};
#endif // QMYWIDGET_H
//qmywidget.cpp
#include "qmywidget.h"
#include "ui_qmywidget.h"
#include <QMetaProperty>
QmyWidget::QmyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::QmyWidget)
{//构造函数
ui->setupUi(this);
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);
girl=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);
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();
}
void QmyWidget::on_spin_valueChanged(int arg1)
{//响应界面上spinBox的valueChanged(int)信号
Q_UNUSED(arg1);
QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());
if (spinBox->property("isBoy").toBool())
boy->setAge(spinBox->value());
else
girl->setAge(spinBox->value());
}
void QmyWidget::on_btnClassInfo_clicked()
{//"类的元对象信息"按钮
// const QMetaObject *meta=boy->metaObject();
const QMetaObject *meta=girl->metaObject();
// const QMetaObject *meta=ui->spinBoy->metaObject();
ui->textEdit->clear();
ui->textEdit->appendPlainText("==元对象信息==\n");
ui->textEdit->appendPlainText(QString("类名称:%1\n").arg(meta->className()));
ui->textEdit->appendPlainText("property");
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(boy->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()));
}
}