Qt 属性系统

一、属性系统有什么用

一般我们说一个类有什么属性,指的就是这个类有啥成员变量。比如 People?类中有个 int age的私有成员变量,我们就可以说这个 People类有个“年龄”属性可以更改读

Qt提供的属性系统,作用就是把类的信息暴露出来成为通用的、大家都认识的信息。比如用C+语言写的 People类中中有个 int age变量,但所如果用QML语言去读取就会出问题,因为QML有自己的规则,它不认识C规则。用Q的属性系统就可以解決这个问题,当一个类的成员变量或者成员函数用属性系统处理ー下,它们就从C++内部大家都认得.

 

二、属性详解

1、静态属性声明


要声明属性,必须继承 QObject 类并使用 Q_PROPERTY 宏。

 2、关键字含义


READ:如果未指定成员变量(通过 MEMBER ),则需要读取访问器函数。用于读取属性值。理想情况下,一个const函数可用于此目的,它必须返回属性的类型或对该类型的const引用。
WRITE:写访问器函数是可选的。用于设置属性值。它必须返回void,并且必须只接受一个参数,要么是属性的类型,要么是指向该类型的指针或引用。
MEMBER:如果未指定读取访问器函数,则需要成员变量关联。这使得给定的成员变量可读写,而无需创建读写访问器函数。如果需要控制变量访问,除了成员变量关联(但不是两者)之外,还可以使用读或写访问器函数。
RESET:复位功能是可选的。它用于将属性设置回其特定于上下文的默认值。
NOTIFY:通知信号是可选的。如果已定义,它应该指定该类中的一个现有信号,该信号在属性值更改时发出。成员变量的通知信号必须采用零个或一个参数,这些参数必须与属性的类型相同。参数将采用属性的新值。仅当属性确实发生更改时才应发出NOTIFY信号,以避免绑定在QML中被不必要地重新计算。
REVISION:修订号是可选的。如果包含,它将定义属性及其通知程序信号,以便在特定版本的API中使用(通常用于暴露于QML)。如果不包含,则默认为0。
DESIGNABLE:表示属性是否应该在GUI设计工具(例如Qt Designer)的属性编辑器中可见。大多数属性是可设计的(默认为true)。可以指定布尔成员函数,而不是true或false。
SCRIPTABLE:表示脚本引擎是否应该访问此属性(默认为true)。可以指定布尔成员函数,而不是true或false。
STORED:表示属性是应该被认为是独立存在还是依赖于其他值。它还指示在存储对象状态时是否必须保存属性值。
USER:表示是将属性指定为类的面向用户属性还是用户可编辑属性。通常,每个类只有一个用户属性(默认值为false)。
CONSTANT:表示属性值是常量。对于给定的对象实例,常量属性的READ方法每次调用时必须返回相同的值。对于对象的不同实例,此常量值可能不同。常量属性不能有写入方法或通知信号。
FINAL:表示派生类不会重写该属性。在某些情况下,这可以用于性能优化,但不是由moc强制执行的。
READ、WRITE、RESET 可以继承。当它们在使用多重继承的类中继承时,它们必须来自第一个继承的类。
 

3、Q_PROPERTY的常用格式

 

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 cursor WRITE setCursor RESET unsetCursor):定义了一个cursor属性,指明了它是QCursor 类型,需要使用自定的setCursor()函数来修改属性值,通过unsetCursor()函数来进行默认值设置。

我们声明的属性值,常用的操作无非就是读、写、将成员变量导出为属性值、关联信号等。

class Person : public QObject
{
    Q_OBJECT

    // alt+enter 会自动生成函数体
    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来指定信号。

下面的示例演示如何使用 MEMBER 关键字将成员变量导出为Qt属性。请注意,指定 NOTIFY 信号以允许QML属性绑定。 

如图,成员变量导出为Qt属性之后,变量改变后属性跟着变化。

 

属性类型可以是 QVariant 支持的任何类型,也可以是用户定义的类型。以下是自定义类型作为属性类型的例子:

struct ceshi
{
    ceshi() {}
    int a;
    bool operator!=(ceshi &c)//!=的重载
    {
        if(this->a != c.a)
            return true;
        return false;
    }
};
Q_DECLARE_METATYPE(ceshi)
 
class Widget : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(ceshi ccc MEMBER ccc NOTIFY cChanged)
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void onCChanged();
 
signals:
    void cChanged();
 
private slots:
    void on_pushButton_clicked();
 
private:
    Ui::Widget *ui;
    ceshi ccc;
};

#include "widget.h"
#include "ui_widget.h"
#include <QRandomGenerator>
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::cChanged,this,&Widget::onCChanged);
}
 
void Widget::onCChanged()
{
    qDebug()<<"成员:"<<ccc.a;
    qDebug()<<"属性:"<<this->property("ccc").value<ceshi>().a;
}
 
void Widget::on_pushButton_clicked()
{
    ccc.a = QRandomGenerator::global()->bounded(255);
    emit cChanged();
}

 

 

三、元对象系统的读写特性

可以使用泛型函数 QObject::property() 和 QObject::setProperty() 读取和写入属性,而不必知道所属类的任何信息(属性名称除外)。

在运行时读取对象的属性信息而不必知道类的任何信息:

  QObject *object = ...
  const QMetaObject *metaobject = object->metaObject();
  int count = metaobject->propertyCount();
  for (int i=0; i<count; ++i) 
  {
      QMetaProperty metaproperty = metaobject->property(i);
      const char *name = metaproperty.name();
      QVariant value = object->property(name);
      ...
  }

1、一个简单的例子,枚举作为属性类型

  class MyClass : public QObject
  {
      Q_OBJECT
      Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
  public:
      MyClass(QObject *parent = 0);
      ~MyClass();
 
      enum Priority { High, Low, VeryHigh, VeryLow };
      Q_ENUM(Priority)
 
      void setPriority(Priority priority)
      {
          m_priority = priority;
          emit priorityChanged(priority);
      }
      Priority priority() const
      { return m_priority; }
 
  signals:
      void priorityChanged(Priority);
 
  private:
      Priority m_priority;
  };
 
 
  MyClass *myinstance = new MyClass;
  QObject *object = myinstance;
 
  myinstance->setPriority(MyClass::VeryHigh);
  object->setProperty("priority", "VeryHigh");

 

四、动态属性


可以使用 QObject::setProperty() 在运行时向对象添加新属性。如果此时中存在具有给定名称的属性,并且给定值与该属性的类型兼容,则该值存储在该属性中,并返回true(也就是重设值)。如果值与属性的类型不兼容,则不会更改属性,并返回false。但是,如果QObject中不存在具有给定名称的属性(即,如果没有使用 Q_PROPERTY()声明该属性),则会自动向QObject添加具有给定名称和值的新属性,但仍会返回false。这意味着返回false不能用于确定是否实际设置了特定属性。

不管是否使用了READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject:property()读取属性值,并通过QObject::setProperty()设置属性值

例如:

QPushButton *pButton = new QPushButton(this);
pButton->setProperty("flat", QVariant(true));
bool bFlat = pButton->property("flat").toBool();

1.动态属性

  • 动态属性是在运行时动态添加到对象上的属性,而不需要在编译时声明或定义。
  • 你可以使用 QObject 类的 setProperty 方法来为对象添加动态属性,这个方法接受属性名和属性值作为参数。
  • 动态属性对于自定义属性非常有用,因为它们允许你在不修改类定义的情况下为对象添加自定义信息。
  • 动态属性通常用于个别对象实例,而不是整个类。

2.静态属性:

  • 静态属性是在编译时静态定义的属性,它们通常在类定义中使用 Q_PROPERTY 宏来声明。
  • 静态属性的声明通常伴随着属性的读取和写入方法的定义,以及属性的默认值等信息。
  • 静态属性通常用于整个类,而不是单个对象实例。这意味着所有该类的对象共享相同的属性。

下面示例演示了动态属性和静态属性的区别:

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 宏在类定义中声明。

总的来说,动态属性用于为对象添加自定义属性,而静态属性用于在类级别定义属性,通常伴随着属性的读取和写入方法。

五、属性和自定义类型


使用Q_DECLARE_METATYPE宏注册属性的自定义类型,以便它们的值可以存储在QVariant对象中。这使它们既适用于在类定义中使用Q_PROPERTY()宏声明的静态属性,也适用于在运行时创建的动态属性。
 

六、类的附加信息

属性系统还有一个宏Q_CLASSINFO(),可以为类的元对象定义"名称-值"信息,如下所示:

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)获取某个附加信息。

函数原型定义如下:  

QMetaClassInfo QMteaObject::classInfo(int index) const

返回值是QMetaClassInfo类型,有name()和value两个函数,可获得类附加信息的名称和值。

示例代码如下:

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();
}

打印信息:

七、在不知道类的属性情况下获取属性的值

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);
} 

八、完整代码示例

头文件:

#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

 源文件:

#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);
}

 属性调用:

ainWindow::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);
}

打印信息: 

  

 

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高亚奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值