转发 Q_PROPERTY 从一个 class 到另一个

Forwarding Q_PROPERTY from one class to another

我有一个使用 QtWidgets 编写的 GUI 组件,我通过继承 QQuickItem 为该组件创建了一个 QtQuick 包装器。原始组件暴露了一些可以直接暴露的属性,而一些属性需要引入新的 类 派生自 QQuickItem。有没有更优雅的方法来做到这一点。

class MyQuickItem : public QQuickItem {
  Q_OBJECT
 public:
  ...
  Q_PROPERTY(int someProperty READ getSomeProperty WRITE setSomeProperty);
  int getSomeProperty() const {
    return m_myQtWidget->getSomeProperty();
    // return m_myQtWidget->property("someProperty").toInt();
  }
  void setSomeProperty(int val) {
    m_myQtWidget->setSomeProperty(val);
  }
 private:
  MyQtWidget* m_myQtWidget;
};

class MyQtWidget : public QWidget {
 Q_OBJECT
 ...
};

有没有办法绕过这种愚蠢的样板编码。我在想是否可以为 Q_PROPERTY 创建一个接受要计算的表达式的替换宏?

这里的问题是我们不能同时继承 QtWidgetQtQuickItem 因为它们都是 QObject 的子 class 而 Qt MOC 根本不支持这个。它也不支持基于 QObject 的模板 classes.

所以不幸的是,我们必须编写额外的代码来通过一种或另一种方式传递属性。

有继承

首先,我想指出没有理由将无意义的 QtWidget 嵌入到 QQuickItem 中。所以我建议将公共逻辑分成这样的基础 class:

class MyBaseItem {...};
//Please note that QObject-based class MUST always be first in inheritance list!
class MyQuickItem : public QQuickItem, public MyBaseItem {...};
class MyQtWidget : public QWidget, public MyBaseItem {...};

为了减少直通代码的数量,我们需要定义如下宏:

#define PASSTHROUGH_Q_PROPERTY_GETSET(VALTYPE,PROPNAME) \
  Q_PROPERTY( VALTYPE PROPNAME READ get_ ## PROPNAME WRITE set_ ## PROPNAME )

#define PASSTHROUGH_Q_PROPERTY_VALUE(VALTYPE,PROPNAME) \
  PASSTHROUGH_Q_PROPERTY_GETSET(VALTYPE, PROPNAME) \
  VALTYPE get ## PROPNAME () const { return m_ ## PROPNAME (); } \
  void set ## PROPNAME (VALTYPE val) { m_ ## PROPNAME = val; } 

解释:

Although moc parses the header instead of compiling it, starting with Qt5 we can apparently use Q_macros in another macros

This code makes use of token-pasting preprocessor operator ## which glues two parts of identifier together, this includes macro parameters. So #define F(A) my ## A will result in int F(thing); being equivalent to int mything;

您需要以这种方式创建基础属性:

class MyBaseItem 
{
public:
  int get_someProperty() { return 42; } // obtain value
  void set_someProperty(int val) {} // set data to val

protected:
  float m_myValueProperty; //a second property, this is a simple number
};

然后在两个继承人 classes 中,您可以这样将它们添加为 Q_PROPERTYs:

class MyQuickItem : public QQuickItem, public MyBaseItem 
{
  Q_OBJECT
public:
  PASSTHROUGH_Q_PROPERTY_GETSET(int, someProperty)
  PASSTHROUGH_Q_PROPERTY_VALUE(float, myValueProperty)
};

必须将此代码复制到两个继承人 classes 中,因为 moc 不支持模板。我认为它也不支持 mid-class #include 但我还没有测试过。

如何添加对信号和只读属性等的支持应该很容易理解。

不幸的是,C/C++ 预处理器不允许更改标识符大小写,因此您最终会得到难看的 set_myProperty 而不是正确的驼峰式 setMyProperty。如果你非常想要后者,你需要将一个额外的宏参数 UPCASE_PROPNAME 设置为大写 MyProperty - 除了仍然需要的 myProperty 之外。请参阅下一节中的此方法示例。

无继承

如果创建基础 class 由于某种原因不可行,这里是您最初请求的代码,但要注意嵌入陈旧的 QWidgets 可能导致的问题

#define PASSTHROUGH_Q_PROPERTY(VALTYPE,PROPNAME,UNAME) \
  Q_PROPERTY(VALTYPE PROPNAME READ get ## UNAME WRITE set ## UNAME ); \
  VALTYPE get ## UNAME () const { return m_myQtWidget->get ## UNAME (); } \
  void set ## UNAME (VALTYPE val) { m_myQtWidget->set ## UNAME (val); } 

用法:

PASSTHROUGH_Q_PROPERTY(int, someProperty, SomeProperty)