Qt 属性系统

Qt提供了一个复杂的属性系统,它基于元对象系统,也通过信号槽提供对象间通信。

一、静态属性声明

要声明属性,必须继承 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])

 这是 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)

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

      Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
      Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
      Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
      ...
  signals:
      void colorChanged();
      void spacingChanged();
      void textChanged(const QString &newText);

  private:
      QColor  m_color;
      qreal   m_spacing;
      QString m_text;

例:

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

二、关键字含义

  • 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强制执行的。

READWRITERESET 可以继承。当它们在使用多重继承的类中继承时,它们必须来自第一个继承的类。

属性类型可以是 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();
}

自定义了一个结构体作为属性的类型,必须重载!=运算符。使用 Q_DECLARE_METATYPE 宏将此结构体注册为 QVariant 的类型。

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

可以使用泛型函数 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);
      ...
  }

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

  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不能用于确定是否实际设置了特定属性。

六、属性和自定义类型

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

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值