【问题标题】:Qt proper usage of properties syntaxQt 正确使用属性语法
【发布时间】:2015-07-22 08:24:17
【问题描述】:

当我使用Q_PROPERTY 定义属性时,我确保从QObject 继承并在类定义的开头声明Q_OBJECT 宏。我这样声明属性:

Q_PROPERTY(QString MyProp READ getMyProp WRITE setMyProp)

然后我像这样使用它:

myObject->MyProp = QString("test");

它告诉我没有MyProp 这样的成员。有什么问题?

【问题讨论】:

  • myObject->setProperty("MyPro", QString("test")); 有效吗?
  • @t3ft3l--i 是的,但我认为它应该允许您像普通成员一样使用它们?不然有什么意义?
  • “否则还有什么意义?”其他 Qt 系统使用它们为所有 QObject 派生类型提供通用干扰,例如QML 绑定。
  • @ulakblade Q_PROPERTY 是在基于 QML 的应用程序中集成 C++ 逻辑代码的最佳方式。还有一点是在不知道它们出现的情况下使用类的方法
  • @t3ft3l--i property() 返回一个 QVariant,这是否意味着如果我有非 QVariant 默认类型的对象的属性,我必须对它们中的每一个使用 Q_DECLARE_METATYPE 或Q_PROPERTY 会处理这个吗?

标签: c++ qt


【解决方案1】:

当然没有这样的成员,因为您自己说过getter 称为getMyProp,而setter 称为setMyProp。名为MyProp 的成员的定义完全取决于您。下面,我将展示如何定义此类成员。

我们还应该注意,在 Qt 中,习惯上不要在 getter 前面加上get,而只是在属性之后命名它。这样你就有了

Q_PROPERTY(int foo READ foo WRITE setFoo)
// rather than
Q_PROPERTY)int foo READ getFoo WRITE setFoo)

可能的解决方案包括:

(1) 直接访问属性的数据成员 - 这会破坏属性系统授予的封装,因此不建议这样做。

class Direct : public QObject {
  Q_OBJECT
  Q_PROPERTY(int prop MEMBER prop)
public:
  int prop;
};

Direct d;
d.prop = 3;
Q_ASSERT(d.property("prop").toInt() == 3);

最后一行有效,因为 moc 生成了简单的 getter 和 setter 代码,然后可以通过元对象系统访问这些代码。此代码嵌入在元数据实现中,不会导致将任何方法添加到类本身。

(2) 有一个返回引用的 getter。当 getter 和 setter 很简单时,这是最简单、开销较低的方法。

class GetSet : public QObject {
  Q_OBJECT
  Q_PROPERTY(int prop READ prop WRITE setProp)
  int m_prop;
public:
  int & prop() { return m_prop; }
  int prop() const { return m_prop; }
  void setProp(int prop) { m_prop = prop; }
};

GetSet gs;
gs.prop() = 3; // assignment syntax through "getter"
Q_ASSERT(gs.property("prop").toInt() == 3);
gs.setProp(4); // setter syntax
Q_ASSERT(gs.property("prop").toInt() == 4);

(3) 公开充当属性代理的成员。这允许在没有调用语法的情况下使用代理:obj.prop 而不是 obj.prop()

(3a) 我们可以使用 Qt 属性系统来访问命名属性。 QMetaProperty 充当缓存以避免在每个属性访问时重复名称查找。对每个属性访问执行QVariant 转换。

template <typename B, typename T> class PProxy {
  Q_DISABLE_COPY(PProxy)
  B * obj;
  QMetaProperty prop;
public:
  PProxy(B & o, const char * p) : obj(&o), 
    prop(o.metaObject()->property(o.metaObject()->indexOfProperty(p))) {}
  T operator=(const T & val) { prop.write(obj, val); return val; }
  T operator=(T && val) { prop.write(obj, val); return val; }
  operator T() const { return prop.read(obj).value<T>(); }
};

class ViaPProxy : public QObject {
  Q_OBJECT
  Q_PROPERTY(int prop READ getProp WRITE setProp)
  int m_prop;
public:
  PProxy<Direct, int> prop;
  ViaPProxy() : prop(*this, "prop") {}
  int getProp() const { return m_prop; }
  void setProp(int val) { m_prop = val; }
}

ViaPProxy vpp;
vpp.prop = 3; // invokes the setter through a proxy
Q_ASSERT(vpp.prop == 3);

(3b) 代理可以直接使用getter 和setter,而不是通过Qt 属性系统和QVariant 转换。这比 (3a) 的开销低,但要求存在 getter 和可选的 setter。

template <typename B, typename T, 
          T (B::*get)() const, void (B::*set)(T) = 0> class DProxy {
  Q_DISABLE_COPY(DProxy)
  B & obj;
public:
  DProxy(B & o) : obj(o) {}
  T operator=(const T & val) { (obj.*set)(val); return val; }
  T operator=(T && val) { (obj.*set)(std::forward<T>(val)); return val; }
  operator T() const { return (obj.*get)(); }
};

class ViaDProxy : public QObject {
  Q_OBJECT
  Q_PROPERTY(int prop READ getProp WRITE setProp)
  int m_prop;
public:
  DProxy<Direct, int, &ViaDProxy::get, &ViaDProxy::set> prop;
  ViaDProxy() : prop(*this) {}
  int getProp() const { return m_prop; }
  void setProp(int val) { m_prop = val; }
}

ViaDProxy vdp;
vdp.prop = 3; // invokes the setter through a proxy
Q_ASSERT(vdp.prop == 3);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-27
    • 2016-10-10
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多