[英]Using a macro to create QObject derived classes
我试图简化(即摆脱样板代码的负担)创建QObject
包装器类, QObject
转发其他QObject
派生类的属性访问。
首先,我只是尝试使用一种属性:
// 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
的类,该类携带Sy_test::prop
属性的副本以及仅转发请求/信号的实现。
而且几乎可以做到。 查看后预处理器的输出(我使用g ++,所以使用.ii文件),我可以看到Sy_testProxy
类已构建,并且其形式与Sy_test
类相同。 但是,我得到一个错误:
../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
的类),并且SY_PROXYPROPERTY
必须有效(因为moc
必须从中读取Q_PROPERTY
宏)。 谁能看到我哪里出问题了?
记录一下:我讨厌像其他所有人一样的宏,但是由于moc
对模板和QObject
虚拟继承的厌恶,我不得不使用它们。 之所以触发此调查,是因为我有一个在单独的线程中执行大量计算的实例的集合,但是它们驱动QML表示形式。 但是QML不允许连接/属性绑定到主线程之外的对象,因此我被迫创建驻留在主线程中的代理对象。 如果有人有更好的主意,我将非常欢迎他们!
moc
不太喜欢宏。 它可以在一定程度上扩展它们,但是当它们变得复杂¹时会失败。
您可以尝试将signals:
替换为public:
²(即,手动扩展signals
宏),并通过将Q_SIGNAL
放在函数声明的前面,告诉您希望该函数成为信号。
更换
signals:\
void Notifier( Type value );\
与
public:\
Q_SIGNAL void Notifier( Type value );\
¹:关于复杂的定义...我不知道什么时候失败,但是过去我遇到了一些不同的问题。 根据我的经验,当宏主体包含另一个宏时,moc会出现问题,例如您的示例中的signals
。 但这只是一个猜测-也许moc失败的那种宏是另外一回事。
²:在Qt 5之前,它曾经是protected
。
除了杂乱无章的moc
,您的包装也不是线程安全的。 没有从正确的线程调用属性getter。 所以我看不到包装的任何地方。 您最好直接从QML中使用包装的类,而不是包装器。
为了线程安全,包装程序应缓存已包装属性的值,以便始终从本地副本进行读取。
到那时,您还可以编写一个完全动态的包装器,该包装器线程安全地转发包装对象中的所有属性。 使用元对象系统,您可以即时生成所有内容-属性值的副本,等等。就属性而言,您可以复制整个二进制描述符,就像包装器假装具有相同的属性一样。
有一段很棒的代码,您可以在Google上搜索qmltricks,它提供了所需的一切作为一个好的开始。
您只需要一个标题。 有扩展空间以支持只读属性或自定义getter / setters ..但我建议您看看。 我现在找不到原始页面,在上一届Qt峰会上看到一个演讲,您可能可以在qt网站上查看动手材料。
在github上的链接下面,有几个可用的版本。
https://github.com/Cavewhere/lib-qt-qml-tricks/blob/master/include/QQmlHelpers
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.