转发 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
创建一个接受要计算的表达式的替换宏?
这里的问题是我们不能同时继承 QtWidget
和 QtQuickItem
因为它们都是 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_PROPERTY
s:
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)
我有一个使用 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
创建一个接受要计算的表达式的替换宏?
这里的问题是我们不能同时继承 QtWidget
和 QtQuickItem
因为它们都是 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 inint F(thing);
being equivalent toint 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_PROPERTY
s:
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)