c++ - 将 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
接受要评估的表达式?
解决方案
这里的问题是我们不能从两个QtWidget
AND继承,QtQuickItem
因为它们都是子类,QObject
而 Qt MOC 根本不支持这一点。它也不支持基于 QObject 的类的模板。
所以不幸的是,我们必须编写额外的代码来通过一种或另一种方式传递属性。
有继承
首先我想指出,没有理由将毫无意义QtWidget
的 s 嵌入到QQuickItem
s 中。所以我建议将通用逻辑分成这样的基类:
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)
推荐阅读
- python - 有什么方法可以缩短类属性字符串吗?
- c# - 具有依赖关系的工厂方法模式的实现
- javascript - React - 创建和更新数据库模型、继承或组合的组件?
- google-apps-script - 仅从一张纸复制值并粘贴到另一张纸上
- c - 为什么 argv 的第一个参数是 1 而不是 0
- ruby-on-rails - Capistrano/Passenger 错误@nginx 使用 nokogiri - 库版本不兼容
- java - 需要帮助计算驾驶汽油成本
- vue.js - 发生父级更新时,子级发出事件会冻结 UI
- javascript - 登录到 Chrome 控制台的对象具有不存在的属性
- xml - PowerShell XML 导出到 CSV - 无法获得一些子值