在注册的 class 本身中调用 qmlRegisterType()

call qmlRegisterType() in the registered class itself

在我的程序中,我有相当多的 QObject 子class在 QML 中实例化。每次我add/remove一个新的class,我都需要add/remove在main.cpp中相应的调用qmlRegisterType()。我想知道我是否可以将调用放在注册的 class 本身的代码中。这使得通过删除 cpp/header 文件而不更改任何其他 C++ 代码来删除 class 成为可能。另外,我可以让我的 main.cpp 干净,不需要包含已注册 classes 的所有头文件。

一种方法似乎是这样的:

MyClass.h:

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(QObject *parent = 0);

private:
    static int unused_val;
};

MyClass.cpp:

#include "MyClass.h"
#include <QtQml>

int MyClass::unused_val = qmlRegisterType<MyClass>("my_company", 1, 0, "MyClass");

// some other code

有更好的方法吗?例如,一个不需要 "unused_val" 变量的?

到目前为止,我找到的最简单、最干净的解决方案是制作一个像这样的 C++ 宏:

#define QML_REGISTER(a) static int unused_val = qmlRegisterType<a>("my_uri", 1, 0, #a)

MyClass.cpp 然后在任何函数之外只需要这一行:

QML_REGISTER(MyClass);

编辑:有时这段代码会使应用程序在调试模式下崩溃。参见 解决方案。

或者您可以使用静态对象为您完成这项工作。

namespace Register {
    template <class T>
    struct Type {
        Type() {
            qmlRegisterType<T>();
        }
    };

不同类型的注册,取决于你想要什么:

    template <class T>
    struct CreatableType {
        CreatableType() = delete;
        CreatableType(const QString& name) {
            qmlRegisterType<T>("my_uri", 1, 0, name.toStdString().c_str());
        }
    };

    template <class T>
    struct UncreatableType {
        UncreatableType() = delete;
        UncreatableType(const QString& name) {
            qmlRegisterUncreatableType<T>("my_uri", 1, 0, name.toStdString().c_str(), name + " is not available.");
        }
    };
}

在我看来,它比使用宏更灵活、更简洁。用法很简单:首先,在 class:

中声明一个静态成员
class A : public QObject {
    Q_OBJECT
    // this one to simply expose the class to QML
    static Register::Type<A> Register;

    // or this one to expose and also allow creation from QML
    static Register::CreatableType<A> Register;

    // or this one to expose the class but disallow creation from QML
    static Register::UncreatableType<A> Register;
};

最后,在 .cpp 文件中定义成员,就像您对宏所做的那样:

// either one of these

Register::Type<A> A::Register;

//or
Register::CreatableType<A> A::Register("MyClass");

// or
Register::UncreatableType<A> A::Register("MyClass");

以下解决方案扩展了 qCring 上面建议的方法。提供的解决方案受到 "static initialization order problem" 的影响,这对我来说是可重现的,而 运行 VS2017 中的代码。下面是使用的头文件:

#pragma once

#include <QCoreApplication>
#include <QQmlEngine>

#define QML_REGISTER_TYPE(T) \
    static QmlRegister::Type<T> s_qmlRegister;

#define QML_REGISTER_CREATABLE_TYPE(T, N) \
    template<> \
    const char * const QmlRegister::CreatableType<T>::NAME = #N; \
    static QmlRegister::CreatableType<T> s_qmlRegister;

#define QML_REGISTER_UNCREATABLE_TYPE(T, N) \
    template<> \
    const char * const QmlRegister::UncreatableType<T>::NAME = #N; \
    static QmlRegister::UncreatableType<T> s_qmlRegister;

#define QML_REGISTER_CPP_SINGLETON_TYPE(T, N) \
    template<> \
    const char * const QmlRegister::CppSingletonType<T>::NAME = #N; \
    static QmlRegister::CppSingletonType<T> s_qmlRegister;

#define QML_REGISTER_JAVASCRIPT_SINGLETON_TYPE(T, N) \
    template<> \
    const char * const QmlRegister::JavaScriptSingletonType<T>::NAME = #N; \
    static QmlRegister::JavaScriptSingletonType<T> s_qmlRegister;

namespace QmlRegister
{
    static constexpr const char * const URI = "my_uri";
    static constexpr int MAJOR = 1;
    static constexpr int MINOR = 0;

    template <class T>
    struct Type
    {
        Type()
        {
            qAddPreRoutine(call);
        }

        static void call()
        {
            qmlRegisterType<T>();
        }
    };

    template <class T>
    struct CreatableType
    {
        static const char * const NAME;

        CreatableType()
        {
            qAddPreRoutine(call);
        }

        static void call()
        {
            qmlRegisterType<T>(URI, MAJOR, MINOR, NAME);
        }
    };

    template <class T>
    struct UncreatableType
    {
        static const char * const NAME;

        UncreatableType()
        {
            qAddPreRoutine(call);
        }

        static void call()
        {
            qmlRegisterUncreatableType<T>(URI, MAJOR, MINOR, NAME, QStringLiteral("%1 is not available.").arg(NAME));
        }
    };

    template <class T>
    struct CppSingletonType
    {
        static const char * const NAME;

        CppSingletonType()
        {
            qAddPreRoutine(call);
        }

        static void call()
        {
            static auto callback = [](QQmlEngine* engine, QJSEngine* scriptEngine) -> QObject*
            {
                QObject* ptr = &T::instance();
                engine->setObjectOwnership(ptr, QQmlEngine::ObjectOwnership::CppOwnership);
                return ptr;
            };

            qmlRegisterSingletonType<T>(URI, MAJOR, MINOR, NAME, callback);
        }
    };

    template <class T>
    struct JavaScriptSingletonType
    {
        static const char * const NAME;

        JavaScriptSingletonType()
        {
            qAddPreRoutine(call);
        }

        static void call()
        {
            static auto callback = [](QQmlEngine* engine, QJSEngine* scriptEngine) -> QObject*
            {
                QObject* ptr = new T();
                engine->setObjectOwnership(ptr, QQmlEngine::ObjectOwnership::JavaScriptOwnership);
                return ptr;
            };

            qmlRegisterSingletonType<T>(URI, MAJOR, MINOR, NAME, callback);
        }
    };
}

主要区别在于上述解决方案调用了 qAddPreRoutine 函数,该函数将每个回调排队,以便在 QCoreApplications 的构造过程中调用。

例如,要将 class(例如:namespace Common { class Result; })注册为 QML 注册类型,只需在 Result 的源文件中添加以下行;

QML_REGISTER_TYPE(Common::Result)

同样,要将 class 注册为 none-creatable,您只需在各自的源文件中添加以下内容:

QML_REGISTER_UNCREATABLE_TYPE(Company::Outcome, Outcome)  // class Company::Outcome is none-creatable and referred in QML as "Outcome"

相同的结构适用于可创建的 QML 类型 (QML_REGISTER_CREATABLE_TYPE)、生命由 C++ 代码管理的单例 QML 类型 (QML_REGISTER_CPP_SINGLETON_TYPE)),以及生命由 C++ 代码管理的单例 QML 类型QML 引擎 (QML_REGISTER_JAVASCRIPT_SINGLETON_TYPE).