使用宏创建派生的 QObject 类
Using a macro to create QObject derived classes
我正在尝试简化(即摆脱大量样板代码)创建 QObject
包装器 classes 转发 属性 访问其他 QObject
派生 classes.
从小处着手,我只是尝试用一个 属性:
// Sy_test.h - The wrapped class
class Sy_test : public QObject
{
Q_OBJECT
Q_PROPERTY( bool prop READ getProp WRITE setProp NOTIFY propChanged )
public:
Sy_test( QObject* parent = nullptr ) :
QObject{ parent },
prop_{ false } {}
bool getProp() const { return prop_; }
public slots:
void setProp( bool value )
{
if ( value != prop_ ) {
prop_ = value;
emit propChanged( prop_ );
}
}
signals:
void propChanged( bool value );
private:
bool prop_;
};
// Sy_proxy.h - The wrapper generator
#define SY_PROXYPROPERTY( Type, Name, Getter, Setter, Notifier )\
private:\
Q_PROPERTY( Type Name READ Getter WRITE Setter NOTIFY Notifier )\
\
public:\
Type Getter() const { return target_->Getter(); }\
\
public slots:\
void Setter( Type value ) { target_->Setter( value ); }\
\
signals:\
void Notifier( Type value );\
\
private:\
void setConnection()\
{\
connect( target_, &std::remove_pointer< decltype( target_ ) >::type::Notifier,\
this, &std::remove_pointer< decltype( this ) >::type::Notifier );\
}
#define SY_PROXY( ProxyName, TargetType, Prop1 )\
class ProxyName : public QObject\
{\
Q_OBJECT \
Prop1 \
\
public:\
ProxyName( TargetType* target ) :\
target_{ target }\
{\
setConnection();\
}\
\
virtual ~ProxyName() {}\
\
private:\
TargetType* target_;\
};
// This should create a Sy_test wrapper class called Sy_testProxy
SY_PROXY( Sy_testProxy,
Sy_test,
SY_PROXYPROPERTY( bool, prop, getProp, setProp, propChanged ) )
因此 SY_PROXY
宏应该创建一个名为 Sy_testProxy
的 class,它带有 Sy_test::prop
属性 的副本,其实现仅转发 requests/signals.
而且 几乎 做到了。查看 post-预处理器输出(我使用的是 g++,所以是 .ii 文件),我可以看到 Sy_testProxy
class 已构建并且其形式与 Sy_test
class。但是,我得到一个错误:
../CppTest/Sy_proxy.h:47: Error: NOTIFY signal 'propChanged' of property 'prop' does not exist in class Sy_testProxy.
make: *** [moc_Sy_proxy.cpp] Error 1
看来 moc
无法解析我的宏魔法;尽管我不确定 SY_PROXY
宏在哪里出现(错误来自一个名为 Sy_testProxy
的 class),并且 SY_PROXYPROPERTY
也必须有效(因为moc
必须从中读取 Q_PROPERTY
宏)。谁能看出我哪里出错了?
郑重声明:我和其他人一样讨厌宏,但由于 moc
对模板和 QObject
虚拟继承的厌恶,我开始使用它们。触发此调查是因为我有一组实例在单独的线程中执行繁重的计算,但它们驱动 QML 表示。但是 QML 不允许 connections/property 绑定到主线程之外的对象,所以我被迫创建位于主线程中的代理对象。如果有人有更好的想法,我很乐意接受!
抛开 moc
的变幻莫测,您的包装器不是线程安全的。 属性 getter 未从正确的线程调用。所以我看不到包装的任何意义。您不妨直接从 QML 使用包装的 class,而不是包装器。
为了线程安全,您的包装器应该缓存包装的 属性 的值,以便始终从本地副本进行读取。
到那时,您还不如编写一个完全动态的包装器,线程安全地转发来自包装对象的所有 属性。使用元对象系统,您可以即时生成所有内容 - 属性 值的副本等。就属性而言,您可以复制整个二进制描述符,因为您的包装器假装具有相同的属性。
moc
不太喜欢宏。它在某种程度上扩展了它们,但是当它们变得复杂时它就失败了¹。
您可以尝试用 public:
² 替换 signals:
(即手动扩展 signals
宏),并通过将 Q_SIGNAL
在函数声明前。
替换
signals:\
void Notifier( Type value );\
和
public:\
Q_SIGNAL void Notifier( Type value );\
¹: 对于 complicated 的某些定义...我不知道它什么时候失败了,但我 运行 过去遇到了一些不同的问题。根据我的经验,我的猜测是当一个宏体包含另一个宏时,moc 会出现问题,例如您示例中的 signals
。但这只是一个猜测——也许 moc 失败的宏类型是另外一回事。
²:在 Qt 5 之前,它曾经是 protected
。
有一段很棒的代码,您可以 google for qmltricks,它拥有您需要的一切作为一个良好的开端。
您只需要一个 header。有扩展空间以支持只读属性或自定义 getters/setters.. 但我建议您看一下。我现在找不到 original.page,在上次 Qt 峰会上看到了一个演示文稿,您可能可以查看 qt 站点以获取 hands-on 资料。
在 github 上的 link 下面,有几个可用的版本。
https://github.com/Cavewhere/lib-qt-qml-tricks/blob/master/include/QQmlHelpers
我正在尝试简化(即摆脱大量样板代码)创建 QObject
包装器 classes 转发 属性 访问其他 QObject
派生 classes.
从小处着手,我只是尝试用一个 属性:
// Sy_test.h - The wrapped class
class Sy_test : public QObject
{
Q_OBJECT
Q_PROPERTY( bool prop READ getProp WRITE setProp NOTIFY propChanged )
public:
Sy_test( QObject* parent = nullptr ) :
QObject{ parent },
prop_{ false } {}
bool getProp() const { return prop_; }
public slots:
void setProp( bool value )
{
if ( value != prop_ ) {
prop_ = value;
emit propChanged( prop_ );
}
}
signals:
void propChanged( bool value );
private:
bool prop_;
};
// Sy_proxy.h - The wrapper generator
#define SY_PROXYPROPERTY( Type, Name, Getter, Setter, Notifier )\
private:\
Q_PROPERTY( Type Name READ Getter WRITE Setter NOTIFY Notifier )\
\
public:\
Type Getter() const { return target_->Getter(); }\
\
public slots:\
void Setter( Type value ) { target_->Setter( value ); }\
\
signals:\
void Notifier( Type value );\
\
private:\
void setConnection()\
{\
connect( target_, &std::remove_pointer< decltype( target_ ) >::type::Notifier,\
this, &std::remove_pointer< decltype( this ) >::type::Notifier );\
}
#define SY_PROXY( ProxyName, TargetType, Prop1 )\
class ProxyName : public QObject\
{\
Q_OBJECT \
Prop1 \
\
public:\
ProxyName( TargetType* target ) :\
target_{ target }\
{\
setConnection();\
}\
\
virtual ~ProxyName() {}\
\
private:\
TargetType* target_;\
};
// This should create a Sy_test wrapper class called Sy_testProxy
SY_PROXY( Sy_testProxy,
Sy_test,
SY_PROXYPROPERTY( bool, prop, getProp, setProp, propChanged ) )
因此 SY_PROXY
宏应该创建一个名为 Sy_testProxy
的 class,它带有 Sy_test::prop
属性 的副本,其实现仅转发 requests/signals.
而且 几乎 做到了。查看 post-预处理器输出(我使用的是 g++,所以是 .ii 文件),我可以看到 Sy_testProxy
class 已构建并且其形式与 Sy_test
class。但是,我得到一个错误:
../CppTest/Sy_proxy.h:47: Error: NOTIFY signal 'propChanged' of property 'prop' does not exist in class Sy_testProxy.
make: *** [moc_Sy_proxy.cpp] Error 1
看来 moc
无法解析我的宏魔法;尽管我不确定 SY_PROXY
宏在哪里出现(错误来自一个名为 Sy_testProxy
的 class),并且 SY_PROXYPROPERTY
也必须有效(因为moc
必须从中读取 Q_PROPERTY
宏)。谁能看出我哪里出错了?
郑重声明:我和其他人一样讨厌宏,但由于 moc
对模板和 QObject
虚拟继承的厌恶,我开始使用它们。触发此调查是因为我有一组实例在单独的线程中执行繁重的计算,但它们驱动 QML 表示。但是 QML 不允许 connections/property 绑定到主线程之外的对象,所以我被迫创建位于主线程中的代理对象。如果有人有更好的想法,我很乐意接受!
抛开 moc
的变幻莫测,您的包装器不是线程安全的。 属性 getter 未从正确的线程调用。所以我看不到包装的任何意义。您不妨直接从 QML 使用包装的 class,而不是包装器。
为了线程安全,您的包装器应该缓存包装的 属性 的值,以便始终从本地副本进行读取。
到那时,您还不如编写一个完全动态的包装器,线程安全地转发来自包装对象的所有 属性。使用元对象系统,您可以即时生成所有内容 - 属性 值的副本等。就属性而言,您可以复制整个二进制描述符,因为您的包装器假装具有相同的属性。
moc
不太喜欢宏。它在某种程度上扩展了它们,但是当它们变得复杂时它就失败了¹。
您可以尝试用 public:
² 替换 signals:
(即手动扩展 signals
宏),并通过将 Q_SIGNAL
在函数声明前。
替换
signals:\
void Notifier( Type value );\
和
public:\
Q_SIGNAL void Notifier( Type value );\
¹: 对于 complicated 的某些定义...我不知道它什么时候失败了,但我 运行 过去遇到了一些不同的问题。根据我的经验,我的猜测是当一个宏体包含另一个宏时,moc 会出现问题,例如您示例中的 signals
。但这只是一个猜测——也许 moc 失败的宏类型是另外一回事。
²:在 Qt 5 之前,它曾经是 protected
。
有一段很棒的代码,您可以 google for qmltricks,它拥有您需要的一切作为一个良好的开端。
您只需要一个 header。有扩展空间以支持只读属性或自定义 getters/setters.. 但我建议您看一下。我现在找不到 original.page,在上次 Qt 峰会上看到了一个演示文稿,您可能可以查看 qt 站点以获取 hands-on 资料。
在 github 上的 link 下面,有几个可用的版本。
https://github.com/Cavewhere/lib-qt-qml-tricks/blob/master/include/QQmlHelpers