在注册的类本身中调用 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()的主要内容,如果未能解决你的问题,请参考以下文章

qml 怎样修改window 窗口样式

如何使用 qmlRegisterType 在 QML 中指定 c++ 类型?

QML 相机尺寸问题

如何在 QML 中注册 QSerialPort?

我需要整合我的 c++ 代码和 qml。但是我在使用 qmlRegisterType 时遇到了许多错误“未定义对‘Middlemen::staticMetaObject’的引用”

Qml 注册类型的构造函数中的发射信号不起作用