在注册的类本身中调用 qmlRegisterType()
Posted
技术标签:
【中文标题】在注册的类本身中调用 qmlRegisterType()【英文标题】:call qmlRegisterType() in the registered class itself 【发布时间】:2016-06-03 20:15:55 【问题描述】:在我的程序中,我有很多在 QML 中实例化的 QObject 子类。每次添加/删除一个新类,都需要在main.cpp中添加/删除qmlRegisterType()
对应的调用。我想知道是否可以将调用放在注册类本身的代码中。这使得可以通过删除其 cpp/header 文件来删除一个类,而无需更改任何其他 C++ 代码。另外,我可以清理我的 main.cpp 并且不需要包含已注册类的所有头文件。
一种方法似乎是这样的:
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”变量?
【问题讨论】:
对我来说正确的方法是在应用程序的一个地方注册所有类型。所以你可以很容易地检查它。 【参考方案1】:或者您可以使用静态对象为您完成这项工作。
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 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");
【讨论】:
很有趣,但使用起来仍然比宏复杂。与宏不同,它需要将代码添加到 .cpp 和头文件中。 当然,但是每个类多一行代码对于更多控制来说是一个公平的价格。由于几个原因,宏迟早会咬你一口。但这就是永恒的简单性与灵活性辩论的材料 为什么你认为你的代码比宏更灵活?顺便说一句,它需要一个头文件包含在另一个头文件中。另一个问题是它不那么容易解释。【参考方案2】:到目前为止,我发现的最简单和最干净的解决方案是制作这样的 C++ 宏:
#define QML_REGISTER(a) static int unused_val = qmlRegisterType<a>("my_uri", 1, 0, #a)
MyClass.cpp 然后在任何函数之外只需要这个简单的行:
QML_REGISTER(MyClass);
编辑:有时此代码会使应用程序在调试模式下崩溃。解决方案见this thread。
【讨论】:
【参考方案3】:以下解决方案扩展了 qCring 上面建议的方法。提供的解决方案遇到了“静态初始化顺序问题”,这对我来说在 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
构造期间调用的每个回调排队。
例如,要将一个类(例如:namespace Common class Result;
)注册为 QML 注册类型,只需在 Result
的源文件中添加以下行;
QML_REGISTER_TYPE(Common::Result)
同样,要将类注册为不可创建类,您只需在其各自的源文件中添加以下内容:
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
)) 以及实时由 QML 引擎管理的单例 QML 类型 (QML_REGISTER_JAVASCRIPT_SINGLETON_TYPE
)。
【讨论】:
以上是关于在注册的类本身中调用 qmlRegisterType()的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 qmlRegisterType 在 QML 中指定 c++ 类型?
我需要整合我的 c++ 代码和 qml。但是我在使用 qmlRegisterType 时遇到了许多错误“未定义对‘Middlemen::staticMetaObject’的引用”