在注册的 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
).
在我的程序中,我有相当多的 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
).