繁体   English   中英

使用宏创建QObject派生类

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM