在 QML 中使用 C++-slot,它返回命名空间中的类型

Posted

技术标签:

【中文标题】在 QML 中使用 C++-slot,它返回命名空间中的类型【英文标题】:Use C++-slot in QML which returns type in namespace 【发布时间】:2012-12-29 21:58:06 【问题描述】:

我首先要提到的是,在 Qt 5.0.0 beta 1 之前,以下内容运行良好(可能还有 beta 2 和 RC,不知道),但在 Qt 5.0.0 最终版本中失败了。我只想参考在 Qt 5.0.0 最终发布版本中看到的结果。所以这很可能与 Qt5 最近的变化有关。

在 C++ 方面,我在命名空间中有一组类(QObject 派生)(可以使用编译器标志触发;这些类位于单独的库中,并且该库将命名空间的使用作为选项留给图书馆的用户)。一个类,这里​​是Game,可能看起来像这样(摘录):

OAE_BEGIN_NAMESPACE

// forward-declarations:
class Player;    // Player is just another class in the same library

class Game : public QObject

    Q_OBJECT

public:
    explicit Game(...);

public slots:
    Player *player() const;  // <-- the quesion is about such slots
;

OAE_END_NAMESPACE

OAE_BEGIN/END_NAMESPACE 扩展为 namespace OAE_NAMESPACE ... 或什么都没有,就像 Qt 在 &lt;qglobal.h&gt; 中所做的一样,只是在宏名称中将“QT”替换为“OAE”:

#ifndef OAE_NAMESPACE

# define OAE_PREPEND_NAMESPACE(name) ::name
# define OAE_USE_NAMESPACE
# define OAE_BEGIN_NAMESPACE
# define OAE_END_NAMESPACE
# define OAE_BEGIN_INCLUDE_NAMESPACE
# define OAE_END_INCLUDE_NAMESPACE
# define OAE_BEGIN_MOC_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) class name;
# define OAE_FORWARD_DECLARE_STRUCT(name) struct name;
# define OAE_MANGLE_NAMESPACE(name) name

#else /* user namespace */

# define OAE_PREPEND_NAMESPACE(name) ::OAE_NAMESPACE::name
# define OAE_USE_NAMESPACE using namespace ::OAE_NAMESPACE;
# define OAE_BEGIN_NAMESPACE namespace OAE_NAMESPACE 
# define OAE_END_NAMESPACE 
# define OAE_BEGIN_INCLUDE_NAMESPACE 
# define OAE_END_INCLUDE_NAMESPACE namespace OAE_NAMESPACE 
# define OAE_BEGIN_MOC_NAMESPACE OAE_USE_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) \
    OAE_BEGIN_NAMESPACE class name; OAE_END_NAMESPACE \
    using OAE_PREPEND_NAMESPACE(name);

# define OAE_FORWARD_DECLARE_STRUCT(name) \
    OAE_BEGIN_NAMESPACE struct name; OAE_END_NAMESPACE \
    using OAE_PREPEND_NAMESPACE(name);

# define OAE_MANGLE_NAMESPACE0(x) x
# define OAE_MANGLE_NAMESPACE1(a, b) a##_##b
# define OAE_MANGLE_NAMESPACE2(a, b) OAE_MANGLE_NAMESPACE1(a,b)
# define OAE_MANGLE_NAMESPACE(name) OAE_MANGLE_NAMESPACE2( \
        OAE_MANGLE_NAMESPACE0(name), OAE_MANGLE_NAMESPACE0(OAE_NAMESPACE))

namespace OAE_NAMESPACE 

# ifndef OAE_BOOTSTRAPPED
# ifndef OAE_NO_USING_NAMESPACE
   /*
    This expands to a "using OAE_NAMESPACE" also in _header files_.
    It is the only way the feature can be used without too much
    pain, but if people _really_ do not want it they can add
    DEFINES += OAE_NO_USING_NAMESPACE to their .pro files.
    */
   OAE_USE_NAMESPACE
# endif
# endif

#endif /* user namespace */

在下文中,当说“启用命名空间”时,我的意思是我声明了宏 OAE_NAMESPACE,在本例中为 oae

除其他外,我从 QML 中为我的应用程序的用户界面访问此类和 Player 类的实例,这些实例由 player() 返回。为此,我按如下方式注册类:

qmlRegisterType<Game>();
qmlRegisterType<Player>();

我为 QML 前端提供了一个指向 Game 实例的指针,在 QML 中称为 theGame

view.engine()->rootContext()->setContextProperty("theGame",
        QVariant::fromValue<Game*>(game));

在 QML 中,我照常使用它。一个小例子应该打印player()的指针地址:

Rectangle 
    width: 100; height: 100
    Component.onCompleted: console.log(theGame.player())

我得到以下结果,具体取决于我是否设置了OAE_NAMESPACE(顺便说一句:我对库和使用它的应用程序使用相同的设置):

禁用命名空间时,一切都按预期工作,并且 QML 打印出指针:

Player(0x10b4ae0)

启用命名空间(以及using 在 C++ 代码中使用 库,所以我根本不更改代码),QML 无法 了解Game::player()的返回类型:

Error: Unknown method return type: Player*

Game::player() 的返回类型更改为 oae::Player*,一切正常:

oae::Player(0x10b4ae0)

到目前为止,我的结论是moc 没有考虑我在类周围放置的命名空间。我的第一个猜测是:嘿,moc 不知道我在调用g++ 时定义了命名空间,这是我在 .pro 文件中所做的:

DEFINES += OAE_NAMESPACE=oae

但是,当将返回类型更改为 OAE_NAMESPACE::Player* 时,它仍然有效,因此 moc 确实知道 OAE_NAMESPACE 宏,但它既不扩展 OAE_BEGIN/END_NAMESPACE 宏,也不它根本不再解析命名空间。

mocPlayer * Game::player() const 生成以下“字符串数据”,其中包含方法的返回类型:

禁用命名空间并使用返回类型Player*时:

"player\0Player*\0"

启用命名空间并使用返回类型Player*时:

"player\0Player*\0"

启用命名空间并使用返回类型OAE_NAMESPACE::Player*

"player\0oae::Player*\0"

另一方面,moc 会在 QMetaObject::className() 返回的类名前面加上命名空间(如果已启用)。

我现在的结论是,当在 QObject 元方法的签名中使用这些类型时,我可以通过编写 OAE_NAMESPACE::ClassName 而不是 ClassName 来解决此问题。 (嗯,有更好的宏OAE_PREPEND_NAMESPACE)。由于这在代码中看起来很糟糕,而且在我看来甚至是错误的,因为方法已经在命名空间中有更好的解决方案吗?

现在还有OAE_BEGIN/END_MOC_NAMESPACE(类似于QT_BEGIN/END_MOC_NAMESPACE),所以也许我在任何地方都需要这些?我不知道它们在 Qt 中的何处/如何使用,因此我应该在我的库中相应地使用它们,因为我想使用与 Qt 相同的可选命名空间功能。

【问题讨论】:

我将添加一些有关 moc 生成的代码的信息(例如,如何解析插槽的签名)。 使用带有命名空间限定类型名的 qmlRegisterType 会改变什么吗? @mlvjr 感谢您的提示,但我已经尝试过了。实际上,这不应该改变任何东西,因为这是一个 C++ 类型说明符,但这是关于 moc 如何解析头文件的。它没有检测到命名空间内的Player 在命名空间外使用时实际上引用了类oae::Player。我也相信这种行为在 Qt 5.0.0 alpha 和 beta1 中是不同的。 【参考方案1】:

它在 5.0.0a 中真的有效吗?

我浏览了 Qt 5.0.0 的源代码并查看了解析方法的位置,尤其是返回类型(仅供参考,5.0.0\qtbase\src\tools\moc\moc.cpp:L160)并且没有命名空间检查(在论点上都没有,所以player(Player* p) 也不起作用)。 然而,它是为类 def (5.0.0\qtbase\src\tools\moc\moc.cpp:L620 & L635)

我认为“我们”可以称之为错误(或疏忽)

【讨论】:

感谢您的努力。我明天再检查一下。当然错误可能在我这边,我不想排除这种可能性;) 好吧,似乎也可以在 Qt 5.0.0 beta1 中工作。我刚刚重试了。奇怪的是我记得它有效。感谢您花时间在 moc 代码中找到这些东西,并祝贺您获得赏金!

以上是关于在 QML 中使用 C++-slot,它返回命名空间中的类型的主要内容,如果未能解决你的问题,请参考以下文章

Qt Quick基础用法

Vue——自定义组件 & 自定义事件$emit & 插槽slot

将 QML 信号连接到 PySide2 插槽

大前端之vue插槽slot

C++ 和 QML:将 QML 信号连接到 C++ 插槽

Qt - 启用 Qml 调试。仅在安全的环境中使用