首页 > 解决方案 > 将 Q_PROPERTY 从一个类转发到另一个类

问题描述

我有一个使用 QtWidgets 编写的 GUI 组件,并且我通过继承为该组件创建了一个 QtQuick 包装器QQuickItem。原始组件公开了许多可以直接公开的属性,而某些属性需要引入从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接受要评估的表达式?

标签: c++macrosqmlqtquick2

解决方案


这里的问题是我们不能从两个QtWidgetAND继承,QtQuickItem因为它们都是子类,QObject而 Qt MOC 根本不支持这一点。它也不支持基于 QObject 的类的模板。

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

有继承

首先我想指出,没有理由将毫无意义QtWidget的 s 嵌入到QQuickItems 中。所以我建议将通用逻辑分成这样的基类:

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

解释:

虽然 moc 解析头文件而不是编译它,但从 Qt5 开始,我们显然可以在另一个宏中使用 Q_macros

此代码使用标记粘贴预处理器运算符 ##,它将标识符的两个部分粘合在一起,这包括宏参数。所以#define F(A) my ## A将导致int F(thing);等同于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
};

然后在两个继承者类中,您可以Q_PROPERTY通过以下方式将它们添加为 s:

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

由于 moc 不支持模板,因此必须将这段代码复制到两个继承类中。我认为它也不支持中产阶级#include,但我还没有测试过。

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

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

无继承

如果由于某种原因创建基类不可行,这里是您最初请求的代码,但要注意嵌入陈旧的 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)

推荐阅读