Qt Property System

在这里咱们来介绍一下Qt的属性系统。

目录

1.总体介绍

2.声明属性

3.用元对象系统读取和设置属性

4.动态属性

5.属性和自定义类型

6.为类添加其他信息


1.总体介绍

Qt提供了一套比较复杂的属性系统,类似于一些编译器提供商提供的系统。然而作为一个与编译器、平台无关的库,Qt并不依赖非标准的编译器特性,比如_property或者[property]。Qt的属性系统可以在Qt支持的每一个平台上的任何标准C++编译器上工作,都没问题。因为它是基于Qt的元对象系统的,元对象系统也提供了通过信号与槽实现内部对象通信的方法。

2.声明属性

想要声明一个属性,在继承自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])

其中type name是必须的,它就是你准备使用的属性变量,可以使用QVariant支持的类型,也可以使用自己创建的类型,自己创建的类型需要使用qRegisterMetaType进行注册到元对象系统中,否则发送信号的时候会识别不到。而后面的成员函数就是对此变量进行操作。

下面是几个简单例子:

Q_PROPERTY(bool focus READ hasFocus)

  Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
  Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

READ表示了一个获取属性的函数,WRITE表示的是设置属性的函数,RESET表示的是重置属性的函数,后面的其它字段稍后介绍。

这里是一个更为详细全面的例子,下面的信号和私有变量可以按住Alt+Enter选择Generate Missing Q_PROPERTY Members自动生成,也可以自己一个一个写,当然是自动生成啦。

             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;

其中MEMBER关键字是用来关联成员变量的,就是前面的属性变量。注意,如果想在QML中绑定信号的话,NOTIFY函数是必须要有的,每当属性值变化时都会自动发出这个信号,用过QML的应该会比较熟悉。

属性的行为很像类的数据成员,不过通过元对象系统新增一些了额外的特性。

当MEMBER函数未指定时,READ函数是必须要的。它是用来读取属性值的。理想情况下,它是一个const函数,并且必须返回属性的类型或者属性类型的const引用。比如QWidget的focus属性,它是一个只读属性,因为Qt只为它只设置了一个READ字段

    Q_PROPERTY(bool focus READ hasFocus)

WRITE函数是可选的。它是用来设置属性值的。必须返回void并且函数只能有一个参数。该参数可以是属性的类型,也可以是指向该类型的一个指针或者引用,自动生成的都是属性值的属性类型。比如QWidget::enabled有个WRITE函数QWidget:::setEnabled()。只读属性不需要WRITE函数,比如上面说的QWidget::focus就没有WRITE函数。

MEMBER函数在READ函数未指定的时候是需要的,它将属性关联到一个成员变量。这个字段可以让属性变得可读和可写,即使没有指定READ和WRITE函数。当然,你最好只用MEMBER、READ|WRITE其中一种就好了。

RESET函数也是可选的。它把属性值重置为指定的默认值。比如QWidget::cursor既有READ函数又有WRITE函数,分别是QWidget::curosr()和QWidget::setCursor(),并且它还有一个RESET函数,是QWidget::unsetCursor,因为它不是调用QWidget::setCursor()来设定默认值的,所以RESET函数必须返回void并且不接受任何参数。

NOTIFY信号也是可选的。如果定义了它,那么就应该在类中声明一个一样的信号,在属性值改变的时候发射它,一般直接自动生成就都有了。对于MEMBER变量,NOTIFY信号只能有0个或一个参数,并且参数类型必须和属性一样。该参数将会江永属性的心智。NOTIFY信号应该在属性真正改变的时候才发送,这样可以避免在QML中进行不必要的绑定的重新评估,比如,Qt会自动发射信号,当MEMBER值没有显示设置的时候。

REVISION修订号是可选的。 如果包含,则它定义将在API的特定版本中使用的属性及其通知程序信号(通常用于暴露于QML)。 如果不包括,则默认为0。

DESIGNABLE属性指示该属性在GUI设计工具(例如Qt Designer)的属性编辑器中是否应可见。 大多数属性是DESIGNABLE(默认为true)。 可以给它一个指定布尔成员函数,而不是true或false。

SCRIPTABLE属性指示脚本引擎是否可以访问此属性(默认为true)。 可以指定一个布尔成员函数,而不是true或false。

STORED属性指示该属性应被视为本身存在还是取决于其他值。 它还指示存储对象状态时是否必须保存属性值。 大多数属性都是STORED(默认为true),但是例如QWidget :: minimumWidth()的STORED为false,因为它的值仅取自QWidget :: minimumSize()属性的宽度,minimumSize是个QSize,有width和height。

USER属性指示该属性是被指定为该类的面向用户还是可编辑的属性。 通常,每个类只有一个USER属性(默认为false)。 例如,对于checkable按钮来说QAbstractButton :: checked是用户可编辑属性。 注意QItemDelegate可以获取并设置widget的USER属性。

CONSTANT属性的存在指示该属性值是恒定的。 对于给定的对象实例,常量属性的READ方法每次调用都必须返回相同的值。 对于对象的不同实例,此常数值可能会有所不同。 常量属性不能具有WRITE方法或NOTIFY信号。

FINAL属性的存在指示该属性将不会被派生类覆盖。 在某些情况下,可以将其用于性能优化,但moc不会强制执行。 绝对不要覆盖FINAL属性。

READ、WRITE和RESET函数可以被继承。也可以是虚函数。在使用多重继承的类中继承它们时,它们必须来自第一个继承的类。

3.用元对象系统读取和设置属性

一个属性可以使用通用的函数QObject::property()和QObject::setProperty(),并且不必知道除了属性名之外的任何有关类的信息。下面的例子展示了如何分别调用QAbstractButton::setDown()和QObject::setProperty来设置"down"这个属性。

QPushButton *button = new QPushButton;

QObject *object = button;
button->setDown(true);
object->setProperty("down", true);

通过WRITE函数来访问属性是二者中更好的一种,因为可以更快,并且在编译时可以获得更好的诊断,但是用这个方法设置属性需要你在编译时知道类的相关信息。通过名字访问属性可以让你在编译时不用知道类的信息。你可以在运行时通过查询类的QObject,QMeatObject和QMetaProperties找到一个类的属性,下面的例子展示了如何这么干。

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

上面的例子中,QMetaObject::property()用来获取定义在未知类中每个属性的metadata。从元数据中获取属性名称,并将其传递给QObject :: property()以获取当前对象中属性的值。

下面是一个完整的例子来展示上面讲的东西。首先假定我们有一个类MyClass,它继承自QObject并且在private使用了了Q_OBJECT宏。我们想要在类中声明一个属性来跟踪优先级值。属性就取名叫priority,类型就是枚举型了,是定义在类中的Priority的枚举。

我们在private区域用Q_PROPERTY()宏来声明一个属性。它需要一个READ函数叫做priority吧,还要一个WRITE函数叫setPriority吧。然后枚举类型必须用Q_ENUM()宏在元对象系统中注册。注册枚举型让我们可以在QObject::setProperty中使用该枚举。我们还必须提供我们的READ和WRITE函数,像下面这样写就行了。

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

READ函数是一个const函数并且返回属性的类型。WRITE函数返回void并且有一个属性类型的参数。元对象编译器需要这些东西。

定义一个指向我们的MyClass实例的指针或者指向MyClass实例的QObject指针,我们有两种方法来设置priority属性,分别是

MyClass *myinstance = new MyClass;

  QObject *object = myinstance;
 
  myinstance->setPriority(MyClass::VeryHigh);
  object->setProperty("priority", "VeryHigh");

在该示例中,作为属性类型的枚举类型在MyClass中声明,并使用Q_ENUM()宏在元对象系统中注册。这使得枚举值可以作为字符串使用,以便在setProperty()的调用中使用。 如果枚举类型在另一个类中声明,则将需要其完全限定名称(即OtherClass :: Priority),并且该其他类还必须继承QObject并使用Q_ENUM()宏在此处注册枚举类型。

一个类似的宏,Q_FLAG()也是可以用的。就像Q_ENUM()一样,它注册一个枚举类型,但是标记该类型为一系列flags,即可以进行“或”运算的值。一个I / O类可能具有枚举值Read和Write,然后QObject :: setProperty()可以接受Read | Write。Q_FLAG()应该用于注册此枚举类型。

4.动态属性

QObject :: setProperty()也可以在运行时用于向类的实例添加新属性。 当使用名称和值调用它时,如果QObject中存在具有给定名称的属性,并且给定值与该属性的类型兼容,则该值将存储在该属性中,并返回true。 如果该值与属性的类型不兼容,则不更改属性,并返回false。 但是,如果QObject中不存在具有给定名称的属性(即,如果未使用Q_PROPERTY()进行声明),则会将具有给定名称和值的新属性自动添加到QObject中,但仍然为false 回来。 这意味着不能使用返回false来确定是否实际设置了特定的属性,除非事先知道该属性已存在于QObject中。

请注意,动态属性是按实例添加的,即它们是添加到QObject而不是QMetaObject的。 通过将属性名称和无效的QVariant值传递给QObject :: setProperty(),可以从实例中删除属性。 QVariant的默认构造函数构造一个无效的QVariant。
可以使用QObject :: property()查询动态属性,就像在编译时使用Q_PROPERTY()声明的属性一样。

5.属性和自定义类型

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

6.为类添加其他信息

连接到属性系统的是附加宏Q_CLASSINFO(),可用于将附加名称/值对附加到类的元对象上,例如:

   Q_CLASSINFO(“版本”,“ 3.0.0”)

像其他元数据一样,类信息可以在运行时通过元对象QMetaObject :: classInfo()进行访问。

上面的内容都是我根据文档边学边翻译的,有些是机翻,其他大部分都是结合自己的理解翻译的,有不对的地方请不要吝惜批评的话。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值