Qt之属性系统
一、属性的定义
1.赋予属性读写操作
Qt提供了一个Q_PROPERTY()宏可以定义属性,它也是基于元对象系统实现的,在QObject的子类中,用Q_PROPERTY()定义属性。
QWidget类兴义属性的一些例子:
1 2 3 | Q_PROPERTY( bool focus READ hasFocus) Q_PROPERTY( bool enable READ isEnable WRITE setEnable) Q_PROPERTY(QCursor cursor READ WRITE setCursor RESET unsetCursor) |
Q_PROPERTY(bool focus READ hasFocus):定义了一个focurs属性,指明了它是bool类型,而且需要用自定义的hasFocus()函数来读取这个属性值。
Q_PROPERTY(bool enable READ isEnable WRITE setEnable):定义了一个enable属性,指明了它是bool类型,需要使用自定的isEnable()函数读取这个属性值,通过setEnable()函数来修改属性值。
Q_PROPERTY(QCursor cursor READ WRITE setCursor RESET unsetCursor):定义了一个cursor属性,指明了它是QCursor 类型,需要使用自定的setCursor()函数来修改属性值,通过unsetCursor()函数来进行默认值设置。
2.将类的成员变量导出为一个属性值
上述所说的属性值并不是QWidget的成员变量,如果想要将QWidget类中的某个变量导出一个属性值,需要使用关键字MEMBER将变量注册到Qt的属性系统中。
如下所示:
1 2 3 4 5 6 | class Person : public QObject <br>{ Q_OBJECT Q_PROPERTY(QString name MEMBER m_name) private : QString m_name; }; |
上述代码中把Person类中的私有成员变量m_name导出为一个属性值,并且命名为name,那么从外界访问时能识别到的的属性值只有name而不是m_name。
3.Q_PROPERTY的常用格式
我们声明的属性值,常用的操作无非就是读、写、将成员变量导出为属性值、关联信号等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Person : public QObject { Q_OBJECT Q_PROPERTY( int age READ age WRITE setAge NOTIFY ageChaned) Q_PROPERTY(QString name MEMBER m_name) public : explicit Person(QString name, QObject *parent = nullptr ); int age(); void setAge( int value);<br> signals: void ageChaned( int value);<br> private : int m_age; QString m_name; }; |
例如上述代码中:
- 使用age这个属性值,用READ关键字指定获取属性值的函数为age(),用WRITE关键指定修改属性值的函数为setAge()。
- 上述的age这个属性值并不是类中的成员变量,是凭空声明出来的一个属性值,如果想要将类中已有的成员变量设置为属性值,需要使用关键字MEMBER,如代码中的属性name关联着m_name,修改属性值name时,m_name的值也会随之改变。
- 给属性值设置关联信号,如果我们希望某个属性值变化时能够发出信号,Qt的属性系统是用关键字NOTIFY来指定信号。
二、属性的使用
不管是否使用了READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject:property()读取属性值,并通过QObject::setProperty()设置属性值
例如:
1 2 3 | QPushButton *pButton = new QPushButton( this ); pButton->setProperty( "flat" , QVariant( true )); bool bFlat = pButton->property( "flat" ).toBool(); |
1.动态属性
- 动态属性是在运行时动态添加到对象上的属性,而不需要在编译时声明或定义。
- 你可以使用
QObject
类的setProperty
方法来为对象添加动态属性,这个方法接受属性名和属性值作为参数。 - 动态属性对于自定义属性非常有用,因为它们允许你在不修改类定义的情况下为对象添加自定义信息。
- 动态属性通常用于个别对象实例,而不是整个类。
2.静态属性:
- 静态属性是在编译时静态定义的属性,它们通常在类定义中使用
Q_PROPERTY
宏来声明。 - 静态属性的声明通常伴随着属性的读取和写入方法的定义,以及属性的默认值等信息。
- 静态属性通常用于整个类,而不是单个对象实例。这意味着所有该类的对象共享相同的属性。
下面示例演示了动态属性和静态属性的区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class MyObject : public QObject { Q_OBJECT Q_PROPERTY(QString staticProperty READ getStaticProperty) public : QString getStaticProperty() const { return m_staticProperty; } private : QString m_staticProperty = "Static Property Value" ; }; int main( int argc, char *argv[]) { QApplication app(argc, argv); MyObject object1; object1.setProperty( "dynamicProperty" , "Dynamic Property Value" ); // 添加动态属性 QString dynamicValue = object1.property( "dynamicProperty" ).toString(); // 读取动态属性值 QString staticValue = object1.getStaticProperty(); // 读取静态属性值 qDebug() << "Dynamic Property:" << dynamicValue; // 输出动态属性值 qDebug() << "Static Property:" << staticValue; // 输出静态属性值 return app.exec(); } |
在这个示例中,dynamicProperty
是一个动态属性,可以在运行时添加。staticProperty
是一个静态属性,通过 Q_PROPERTY
宏在类定义中声明。
总的来说,动态属性用于为对象添加自定义属性,而静态属性用于在类级别定义属性,通常伴随着属性的读取和写入方法。
3.类的附加信息
属性系统还有一个宏Q_CLASSINFO(),可以为类的元对象定义"名称-值"信息,如下所示:
1 2 3 4 5 6 7 8 9 | class Person : public QObject { Q_OBJECT Q_CLASSINFO( "author" , "Wang" ) Q_CLASSINFO( "company" , "UPC" ) Q_CLASSINFO( "version" , "1.0.0" ) public : ... }; |
用Q_CLASSINFO()宏定义附加类信息后,可以通过元对象的一些函数获取类的附件信息,如calssInfo(int)获取某个附加信息。
函数原型定义如下:
1 | QMetaClassInfo QMteaObject::classInfo( int index) const |
返回值是QMetaClassInfo类型,有name()和value两个函数,可获得类附加信息的名称和值。
示例代码如下:
1 2 3 4 5 6 7 8 | Person *pBoy = new Person(); const QMetaObject* metaObject = pBoy->metaObject(); int infoCount = metaObject->classInfoCount(); for ( int i = 0; i < infoCount; ++i) { QMetaClassInfo info = metaObject->classInfo(i); qDebug() << "Key:" << info.name() << ", Value:" << info.value(); } |
打印信息:
4.在不知道类的属性情况下获取属性的值
1 2 3 4 5 6 7 8 9 | Person *pBoy = new Person( "王小明" ); const QMetaObject *metaobject = pBoy->metaObject(); int count = metaobject->propertyCount(); for ( int i = 0; i < count; ++i) { QMetaProperty metaproperty = metaobject->property(i); const char *name = metaproperty.name(); QVariant value = pBoy->property(name); } |
三、完整代码示例
头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #ifndef PERSON_H #define PERSON_H #include <QObject> class Person : 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 ageChaned) Q_PROPERTY(QString name MEMBER m_name) Q_PROPERTY( int score MEMBER m_score) public : explicit Person(QString name, QObject *parent = nullptr ); int age(); void setAge( int value); void incAge(); signals: void ageChaned( int value); private : int m_age; QString m_name; int m_score; }; #endif // PERSON_H |
源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include "person.h" Person::Person(QString name, QObject *parent) : QObject(parent) { m_name = name; } int Person::age() { return m_age; } void Person::setAge( int value) { m_age = value; emit ageChaned(m_age); } void Person::incAge() { m_age++; emit ageChaned(m_age); } |
属性调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { m_pBoy = new Person( "王小明" ); connect(m_pBoy, &Person::ageChaned, this , &MainWindow::on_ageChanged); m_pBoy->setProperty( "socre" , QVariant(95)); m_pBoy->setProperty( "sex" , QVariant( "Boy" )); //动态属性 m_pBoy->setProperty( "age" , QVariant(10)); } MainWindow::~MainWindow() { } void MainWindow::on_ageChanged( int value) { Q_UNUSED(value); Person *pPserson = qobject_cast<Person*>(sender()); //类型投射 QString name = pPserson->property( "name" ).toString(); QString sex = pPserson->property( "sex" ).toString(); //int age = pPserson->property("age").toInt();//通过属性获取年龄 int age = pPserson->age(); //通过接口函数获取年龄 qDebug() << name + "," + sex + "," + QString::asprintf( "年龄=%d" , age); } |
打印信息:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?