QQmlEngine(QML引擎)

一、描述 

QQmlEngine 类为实例化 QML 组件提供了环境。

每个 QML 组件都在 QQmlContext 中实例化。在 QML 中,上下文是按层次排列的,这个层次由 QQmlEngine 管理。

在创建任何 QML 组件之前,应用程序必须创建一个 QQmlEngine 才能访问 QML 上下文。

以下示例展示了如何创建一个简单的 Text 项目:

    QQmlEngine engine;
    QQmlComponent component(&engine);
    component.setData(R"(import QtQuick 2.0
                         Text 
                         { 
                             text: "Hello world!" 
                         })", QUrl());
    QQuickItem *item = qobject_cast<QQuickItem *>(component.create());

...

文本项将在引擎的根上下文中创建。

二、属性成员

1、offlineStoragePath : QString

放置 SQL 和其他离线存储的目录。使用 openDatabase() 创建的 SQL 数据库存储在这里。

默认为平台标准用户应用程序数据目录中的 QML/OfflineStorage。

    QQmlEngine engine;
    qDebug()<<engine.offlineStoragePath();

QQmlEngine(QML引擎)

 三、成员函数

1、【信号】void exit(int retCode)

当引擎加载的 QML 想要退出具有指定返回码 retCode 的事件循环时,会发出此信号。

2、【信号】void quit()

当引擎加载的 QML 想要退出时,会发出此信号。

3、void retranslate()

刷新所有使用标记为翻译的字符串的绑定表达式。

在使用 QCoreApplication::installTranslator() 安装新的翻译器后调用此函数,以确保用户界面显示最新的翻译。

4、void warnings(const QList<QQmlError> &warnings)

当 QML 生成警告消息时会发出此信号。

5、void addImageProvider(const QString &providerId, QQmlImageProviderBase *provider)

      void removeImageProvider(const QString &providerId)

使用providerId 设置用于通过 image: url 方案请求的图像的提供程序。QQmlEngine 拥有 provider 的所有权。可参考

       QQmlImageProviderBase *imageProvider(const QString &providerId)

返回为 providerId 设置的图像提供程序。

6、void addImportPath(const QString &path)

添加path作为引擎在基于 URL 的目录结构中搜索已安装模块的目录。

路径可以是本地文件系统目录、Qt 资源路径 (:/imports)、Qt 资源 url (qrc:/imports) 或 URL。

在将路径添加到导入路径列表之前,路径将被转换为规范形式。

新添加的路径将在 importPathList() 中第一个出现。

       QStringList importPathList() 

返回引擎在基于 URL 的目录结构中搜索已安装模块的目录列表。

例如,如果 /opt/MyApp/lib/imports 在路径中,那么导入 com.mycompany.Feature 的 QML 将导致 QQmlEngine 在 /opt/MyApp/lib/imports/com/mycompany/Feature/ 中查找该模块提供的组件。 定义类型版本映射和可能的 QML 扩展插件需要一个 qmldir 文件。

默认情况下,该列表包含应用程序可执行文件的目录、在 QML_IMPORT_PATH 环境变量中指定的路径以及来自 QLibraryInfo 的内置 QmlImportsPath。

       void setImportPathList(const QStringList &paths)

将path设置为引擎在基于 URL 的目录结构中搜索已安装模块的目录列表。

7、void addPluginPath(const QString &path)

添加path作为引擎为导入的模块搜索本机插件的目录(在 qmldir 文件中引用)。

默认情况下,该列表仅包含 .,即引擎在 qmldir 文件本身的目录中搜索。

新添加的路径将在 pluginPathList() 中第一个出现。

       QStringList pluginPathList() 

返回引擎为导入的模块搜索本机插件的目录列表。

       void setPluginPathList(const QStringList &paths)

将引擎在其中搜索导入模块的本机插件的目录列表设置为路径。

8、void addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)

添加在 QML 中解析 URL 时要使用的拦截器。这也适用于用于加载脚本文件和 QML 类型的 URL。

      void removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)

移除之前使用 addUrlInterceptor() 添加的 urlInterceptor。

不会删除拦截器,而只是将其从引擎中移除。

9、QUrl baseUrl() / void setBaseUrl(const QUrl &url)

基本 URL 仅用于在将相对 URL 传递给 QQmlComponent 构造函数时解析组件。

如果未明确设置基本 URL,则此方法返回应用程序的当前工作目录。

10、void clearComponentCache()

清除引擎的内部组件缓存。

此函数会导致引擎先前加载的所有组件的属性元数据被销毁。所有先前加载的组件以及从这些组件创建的所有现存对象的属性绑定都将停止运行。

此函数将引擎返回到不包含任何加载的组件数据的状态。

清除组件缓存后,必须先加载组件,然后才能创建任何新对象。

注意:任何从 QML 组件创建的现有对象都会保留它们的类型,即使清除了组件缓存。这包括单例对象。如果在清除缓存后从相同的 QML 代码创建更多对象,则新对象的类型将与旧对象不同。将这样的新对象分配给属于在清除缓存之前创建的对象的其声明类型的属性将不起作用。

当清除组件缓存时,请确保没有从 QML 组件创建的对象处于活动状态。

11、void trimComponentCache()

修剪引擎的内部组件缓存。

此功能会导致销毁当前未使用的任何已加载组件的属性元数据。

如果存在组件本身的任何现存实例、使用该组件的其他组件的任何实例或由这些组件中的任何一个实例化的任何对象,则认为该组件正在使用中。

12、QQmlContext * contextForObject(const QObject *object)

      【static】void setContextForObject(QObject *object, QQmlContext *context)

返回对象的 QQmlContext,如果没有设置上下文,则返回 0。

当 QQmlEngine 实例化 QObject 时,上下文会自动设置。

13、bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)

使用提供的 uri 导入名为 filePath 的插件。返回导入结果。

失败时,如果 errors 非空,错误将添加到 errors。

该插件必须是实现 QQmlEngineExtensionPlugin 接口的 Qt 插件。

14、void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory)

设置用于创建 QNetworkAccessManager 的工厂。

QNetworkAccessManager 用于 QML 的所有网络访问。通过实现工厂,可以创建具有专门缓存、代理和 cookie 支持的自定义 QNetworkAccessManager。

在执行引擎之前必须设置工厂。QQmlEngine 不拥有工厂的所有权。

       QNetworkAccessManager * networkAccessManager() 

返回一个通用的 QNetworkAccessManager,可由该引擎实例化的任何 QML 类型使用。

15、void setOutputWarningsToStandardError(bool enabled)

       bool outputWarningsToStandardError() 

设置是否将警告消息输出到 stderr。默认值是true。

如果 enabled 为 true,则 QML 生成的任何警告消息都将输出到 stderr 并由 warnings() 信号发出。 如果 enabled 为 false,则只会发出 warnings() 信号。 这允许应用程序自己处理警告输出。

16、 QString offlineStorageDatabaseFilePath(const QString &databaseName) 

返回具有标识符 databaseName 的本地存储数据库所在(或将)所在的文件路径。

17、QQmlContext *rootContext() 

返回引擎的根上下文。

根上下文由 QQmlEngine 自动创建。应该对引擎实例化的所有 QML 组件实例可用的数据应该放在根上下文中。

应该只对组件实例的子集可用的附加数据应该添加到以根上下文为父级的子上下文中。

18、template <typename T> T singletonInstance(int qmlTypeId)

返回在 qmlTypeId 下注册的单例类型的实例。

模板参数 T 可以是 QJSValue 或指向 QObject 派生类型的指针,这取决于单例的注册方式。 如果尚未创建 T 的实例,则现在创建它。 如果 qmlTypeId 不代表有效的单例类型,则返回默认构造的 QJSValue 或 nullptr。

QObject示例:

class MySingleton : public QObject 
{
    Q_OBJECT

    // 注册为默认构造的单例。
    QML_ELEMENT
    QML_SINGLETON

    static int typeId;
    // ...
};

    MySingleton::typeId = qmlTypeId(...);

    // Retrieve as QObject*
    QQmlEngine engine;
    MySingleton* instance = engine.singletonInstance<MySingleton*>(MySingleton::typeId);

QJSValue示例:

    // 注册 QJSValue 回调
    int typeId = qmlRegisterSingletonType(...);

    // Retrieve as QJSValue
    QQmlEngine engine;
    QJSValue instance = engine.singletonInstance<QJSValue>(typeId);

四、相关非成员

1、template <typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create = true)

返回已通过附加类型 T 附加到指定附加对象的实例。

若 create 为 true 并且类型 T 是有效的附加类型,则将创建并返回一个新的附加对象实例。

若类型 T 不是有效的附加类型,或者如果 create 为 false 并且之前没有为附加对象创建任何附件对象实例,则返回 nullptr。

2、void qmlClearTypeRegistrations()

清除所有存储的类型注册数据,例如使用 qmlRegisterType() 生成的注册数据。

不要在 QQmlEngine 存在时调用此函数,否则行为将是未定义的。 在调用此函数之前,必须删除任何现有的 QQmlEngines。 此功能仅影响应用程序全局缓存。 删除 QQmlEngine 以清除与该引擎相关的所有缓存数据。

3、QQmlContext *qmlContext(const QObject *object)

返回与对象关联的 QQmlContext 。 相当于 QQmlEngine::contextForObject(object)。

4、QQmlInfo qmlDebug(const QObject *object)

打印包含指定 QML 对象的文件和行号的调试消息。

当 QML 类型生成日志消息时,如果它们包含 QML 文件和实例化特定实例的行号,则可以提高可追溯性。

要包含文件和行号,必须传递一个对象。 如果该实例的文件和行号不可用(它没有被 QML 引擎实例化,或者位置信息被禁用),则将使用“未知位置”。 

qmlDebug(object) << "Internal state: 42";

//QML MyCustomType (unknown location): Internal state: 42

       QQmlInfo qmlInfo(const QObject *object)

打印包含指定 QML 对象的文件和行号的信息性消息。 

       QQmlInfo qmlWarning(const QObject *object)

打印包含指定 QML 对象的文件和行号的警告消息。 

5、QQmlEngine *qmlEngine(const QObject *object)

返回与对象关联的 QQmlEngine。 这等效于 QQmlEngine::contextForObject(object)->engine(),但效率更高。

6、bool qmlProtectModule(const char *uri, int majVersion)

此功能可保护模块不被进一步修改。这可用于防止其他插件将类型注入模块。它也可以是一种性能改进,因为它允许引擎在达到此导入时跳过检查新类型或插件的可能性。

一旦调用了此函数,QML 引擎将不再搜索新的 qmldir 文件来加载模块。不过,它将重新使用它之前加载的任何 qmldir 文件。因此,此时出现的类型继续工作。请注意,不同的 QML 引擎可能会加载不同的模块。但是,模块保护是全局的,会影响所有引擎。对于慢速文件系统,定位 qmldir 文件和加载插件的开销可能会很明显。因此,一旦您确定不再需要加载模块,就对其进行保护,这可能是一个很好的优化。还要注意,模块锁不仅会影响插件,还会影响任何其他 qmldir 指令,例如 import 或 prefer,以及在 qmldir 文件中声明的任何复合类型或脚本。

调用此函数后,任何试图将C++类型注册到这个uri,主要版本组合都会导致运行时错误。

如果找到并锁定具有 uri 作为模块标识符和 majVersion 作为主要版本号的模块,则返回 true,否则返回 false。模块必须包含导出类型才能被找到。

7、template <typename T> int qmlRegisterAnonymousType(const char *uri, int versionMajor)

该模板函数将 QML 系统中的 C++ 类型注册为匿名类型。 生成的 QML 类型没有名称。 因此,不能从 QML 系统创建这种类型的实例。 但是,当类型的实例作为其他类型的属性公开时,可以访问它们。

返回 QML 类型 ID。

当类型不会通过名称引用时使用此函数,特别是对于在属性绑定的左侧使用的 C++ 类型。 要指示该类型属于哪个模块,请使用 uri 和 versionMajor。

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged)

public:
    Bar() {}
    QString baz() const { return mBaz; }
    void setBaz(const QString &baz)
    {
        if (baz == mBaz)
            return;

        mBaz = baz;
        emit bazChanged();
    }

signals:
    void bazChanged();

private:
    QString mBaz;
};

class Foo : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL)

public:
    Foo() {}
    Bar *bar() { return &mBar; }

private:
    Bar mBar;
};

在 QML 中,为 bar 的 baz 属性分配一个字符串:

Foo {
    bar.baz: "abc"
    Component.onCompleted: print(bar.baz)
}

为了让 QML 引擎知道 Bar 类型有一个 baz 属性,必须让 Bar 知道:

qmlRegisterType<Foo>("App", 1, 0, "Foo");
qmlRegisterAnonymousType<Bar>("App", 1);

由于 Foo 类型在 QML 中实例化,因此必须采用元素名称的 qmlRegisterType() 版本进行注册。

8、template <typename T, typename E> int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

该模板函数在从 uri 导入的库中以名称 qmlName 在 QML 系统中注册 C++ 类型及其扩展对象,该库的版本号由 versionMajor 和 versionMinor 组成。 将在扩展对象中搜索主类型中不可用的属性。

返回 QML 类型 ID。

9、template <typename T, typename E> int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString &reason)

该模板函数在从 uri 导入的库中以名称 qmlName 在 QML 系统中注册 C++ 类型及其扩展名,该库的版本号由 versionMajor 和 versionMinor 组成。

虽然类型有名称和类型,但无法创建。 如果用户尝试创建此类型的实例,则会打印带有给定原因的错误消息。

当类型仅用于提供附加属性、枚举值或带有扩展的抽象基类时,这很有用。

返回 QML 类型 ID。

10、void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)

此函数在特定 uri 中注册具有 versionMajor 和 versionMinor 中指定版本的模块。

这可用于使某个模块版本可用,即使没有为该版本注册任何类型。 这对于保持相关模块的版本同步特别有用。

11、template <typename T, int metaObjectRevision> int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)

该模板函数使用从 uri 导入的库注册 QML 系统中 C++ 类型的指定修订版,该库的版本号由 versionMajor 和 versionMinor 组成。

返回 QML 类型 ID。

12、int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *cppObject)

此函数用于注册具有特定 uri 和 typeName 的单例对象 cppObject。它的版本是 versionMajor 和 versionMinor 的组合。

将单例类型安装到 URI 中允许向 QML 代码提供任意功能(方法和属性),而无需客户端实例化该类型的各个实例。

使用此函数将给定类型 T 的对象注册为单例类型。

QObject 单例类型可以通过注册它的类型名称来引用反过来,这个类型名称可以用作 Connections 类型中的目标,或者像任何其他类型 ID 一样。但是,有一个例外:QObject 单例类型属性不能被命名为别名,因为单例类型名称不标识与任何其他项位于同一组件中的对象

注意:cppObject 必须比使用它的 QML 引擎寿命更长。此外,cppObject 必须与引擎具有相同的线程关联。如果你想为多个引擎单独的单例实例,需要使用 qmlRegisterSingletonType()。

注意: qmlRegisterSingleton() 只能在该模块的所有类型都按程序注册时使用。

// 首先,定义提供功能的 QObject。
class SingletonTypeExample : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)

public:
    explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}

    Q_INVOKABLE int doSomething()
    {
        setSomeProperty(5);
        return m_someProperty;
    }

    int someProperty() const { return m_someProperty; }
    void setSomeProperty(int val) 
    {
        if (m_someProperty != val) 
        {
            m_someProperty = val;
            emit somePropertyChanged(val);
        }
    }

signals:
    void somePropertyChanged(int newValue);

private:
    int m_someProperty = 0;
};
// 二、创建对象的实例
// 在引擎之前分配示例以确保它比它更长寿
QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample);
QQmlEngine engine;

// 第三,通过调用此方法向 QML 注册单例类型提供程序
// 初始化函数中的函数。
qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());

为了在 QML 中使用注册的单例类型,必须导入具有相应版本的 URI。

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0
Item 
{
    id: root
    property int someValue: MyApi.someProperty

    Component.onCompleted: 
    {
        console.log(MyApi.doSomething())
    }
}

13、int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*)(QQmlEngine *, QJSEngine *) callback)

此函数可用于在具有 versionMajor 和 versionMinor 中指定的版本的特定 uri 和 typeName 中注册单例类型提供程序回调。

安装单例类型允许开发人员向客户端提供任意功能(方法和属性),而无需客户端实例化该类型的单个实例。

单例类型可以是 QObject 或 QJSValue。 此函数应该用于注册一个单例类型提供程序函数,该函数返回一个 QJSValue 作为单例类型。

// 首先,定义单例类型提供程序函数(回调)。
static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)

    static int seedValue = 5;
    QJSValue example = scriptEngine->newObject();
    example.setProperty("someProperty", seedValue++);
    return example;
}

// 其次,通过在初始化函数中调用此函数,向 QML 注册单例类型提供程序。
qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);

或者,可以使用 C++11 lambda:

qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue 
{
    Q_UNUSED(engine)

    static int seedValue = 5;
    QJSValue example = scriptEngine->newObject();
    example.setProperty("someProperty", seedValue++);
    return example;
});

为了在 QML 中使用注册的单例类型,必须导入单例类型。

import QtQuick 2.0
import Qt.example.qjsvalueApi 1.0 as ExampleApi
Item {
    id: root
    property int someValue: ExampleApi.MyApi.someProperty
}

       template <typename T> int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*)(QQmlEngine *, QJSEngine *) callback)

此函数可用于在具有 versionMajor 和 versionMinor 中指定的版本的特定 uri 和 typeName 中注册单例类型提供程序回调。

将单例类型安装到 uri 中允许开发人员向客户端提供任意功能(方法和属性),而无需客户端实例化该类型的单个实例。

单例类型可以是 QObject 或 QJSValue。此函数应用于注册一个单例类型提供程序函数,该函数返回给定类型 T 的 QObject 作为单例类型。

QObject 单例类型可以通过它注册的类型名称来引用,并且这个类型名称可以用作连接类型中的目标或以其他方式用作任何其他类型 id。对此的一个例外是 QObject 单例类型属性可能没有别名。

注意:从单例类型提供程序返回的 QObject 单例类型实例归 QML 引擎所有,除非该对象明确设置了 QQmlEngine::CppOwnership 标志。

// 首先,定义提供功能的 QObject。
class SingletonTypeExample : public QObject
{
    Q_OBJECT
    Q_PROPERTY (int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)

public:
    SingletonTypeExample(QObject* parent = 0)
        : QObject(parent), m_someProperty(0)
    {
    }

    ~SingletonTypeExample() {}

    Q_INVOKABLE int doSomething() { setSomeProperty(5); return m_someProperty; }

    int someProperty() const { return m_someProperty; }
    void setSomeProperty(int val) { m_someProperty = val; emit somePropertyChanged(val); }

signals:
    void somePropertyChanged(int newValue);

private:
    int m_someProperty;
};

// 其次,定义单例类型提供者函数(回调)。
static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    SingletonTypeExample *example = new SingletonTypeExample();
    return example;
}

// 第三,通过在初始化函数中调用此函数,向 QML 注册单例类型提供程序。
qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);

或者,可以使用 C++11 lambda:

qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * 
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    SingletonTypeExample *example = new SingletonTypeExample();
    return example;
});

为了在 QML 中使用注册的单例类型,必须导入单例类型。

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0
Item {
    id: root
    property int someValue: MyApi.someProperty

    Component.onCompleted: {
        someValue = MyApi.doSomething()
    }
}

        template <typename T> int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject *(QQmlEngine *, QJSEngine *)> callback)

重载函数。

       int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)

此函数可用于在从 uri 导入的库中注册名为 qmlName 的单例类型,其版本号由 versionMajor 和 versionMinor 组成。 类型由位于 url 的 QML 文件定义。 url 必须是绝对 URL,即 url.isRelative() == false。

此外,该类型的 QML 文件必须在其导入语句中包含 pragma Singleton 语句。

可以通过注册它的类型名称来引用单例类型,并且该类型名称可以用作 Connections 类型中的目标或以其他方式用作任何其他类型 id。 对此的一个例外是单例类型属性可能没有别名(因为单例类型名称不标识与任何其他项位于同一组件中的对象)。

// 首先,定义提供功能的 QML 单例类型。pragma Singleton
import QtQuick 2.0
Item {
    property int testProp1: 125
}
// 其次,通过在初始化函数中调用此函数来注册 QML 单例类型。
qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton");

为了在 QML 中使用注册的单例类型,必须导入单例类型。

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0
Item {
    id: root
    property int someValue: RegisteredSingleton.testProp1
}

也可以在不使用 qmlRegisterSingletonType() 函数的情况下注册 QML 单例类型。 这可以通过在类型的 QML 文件的导入中添加 pragma Singleton 语句来完成。 此外,类型必须在带有单例关键字的 qmldir 文件中定义,并且 qmldir 必须由使用单例的 QML 文件导入。

14、template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

该模板函数在从 uri 导入的库中注册 QML 系统中名为 qmlName 的 C++ 类型,其版本号由 versionMajor 和 versionMinor 组成。

返回 QML 类型 ID。

这个模板函数有两种形式:

template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

template<typename T, int metaObjectRevision>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

前者是将类型 T 注册为新类型的标准形式。 后者允许在指定版本中注册类的特定修订。

例如,这将 C++ 类 MySliderItem 注册为名为 Slider 的 QML 类型,用于名为“com.mycompany.qmlcomponents”的类型命名空间的 1.0 版:

qmlRegisterType<MySliderItem>("com.mycompany.qmlcomponents", 1, 0, "Slider");

注册后,可以通过导入指定的类型命名空间和版本号在 QML 中使用该类型:

import com.mycompany.qmlcomponents 1.0

Slider {
    // ...
}

       int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)

该函数在 QML 系统中注册一个名为 qmlName 的类型,在从 uri 导入的库中,其版本号由 versionMajor 和 versionMinor 组成。 类型由位于 url 的 QML 文件定义。 url 必须是绝对 URL,即 url.isRelative() == false。

通常 QML 文件可以作为类型直接从其他 QML 文件加载,或者使用 qmldir 文件。 此函数允许从 C++ 代码将文件注册为类型,例如需要在启动时按程序确定类型映射时。

如果注册不成功,则返回 -1。

15、int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString &message)

该函数在 QML 系统中注册一个名为 qmlName 的类型,在从 uri 导入的类型命名空间中,其版本号由 versionMajor 和 versionMinor 组成,但任何实例化类型的尝试都会产生给定的错误消息。

返回 QML 类型 ID。

#ifdef NO_GAMES_ALLOWED
qmlRegisterTypeNotAvailable("MinehuntCore", 0, 1, "Game", "Get back to work, slacker!");
#else
qmlRegisterType<MinehuntGame>("MinehuntCore", 0, 1, "Game");
#endif

这将导致任何导入“MinehuntCore”类型命名空间的 QML 并尝试使用该类型产生错误消息。

16、int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString &reason)

该函数在从 uri 导入的库中以名称 qmlName 在 QML 系统中注册 staticMetaObject 及其扩展,该库的版本号由 versionMajor 和 versionMinor 组成。

无法创建元对象的实例。 如果用户尝试创建它,则会打印带有给定原因的错误消息。

这个函数对于注册 Q_NAMESPACE 命名空间很有用。

返回 QML 类型 ID。

namespace MyNamespace {
  Q_NAMESPACE
  enum MyEnum {
      Key1,
      Key2,
  };
  Q_ENUMS(MyEnum)
}

//...
qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "io.qt", 1, 0, "MyNamespace", "Access to enums & flags only");

在 QML 中即可使用已注册的枚举:

Component.onCompleted: console.log(MyNamespace.Key2)

17、template <typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString &message)

该模板函数在从 uri 导入的库中注册 QML 系统中名为 qmlName 的 C++ 类型,其版本号由 versionMajor 和 versionMinor 组成。

虽然类型有名称和类型,但无法创建,如果尝试创建,将导致给定的错误消息。

这在类型仅用于提供附加属性或枚举值的情况下很有用。

返回 QML 类型 ID。

18、int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

返回在特定 uri 中使用名称 qmlName 注册的类型的 QML 类型 ID,以及在 versionMajor 和 versionMinor 中指定的版本。

该函数返回与 qmlRegisterType() 和 qmlRegisterSingletonType() 等 QML 类型注册函数相同的值。

如果 qmlName、uri 和 versionMajor 匹配注册类型,但 versionMinor 中指定的次要版本更高,则返回最接近次要版本的类型的 id。

如果未找到匹配类型或给定参数之一无效,则返回 -1。

五、宏成员

1、QML_ADDED_IN_MINOR_VERSION(VERSION)

略。

2、QML_ANONYMOUS

声明类型可用,但在 QML 中是匿名的。该类型不能在 QML 中创建或用于声明属性,但当从 C++ 传递时,它被识别。在 QML 中,可以使用这种在 C++ 中声明的属性。

3、QML_ATTACHED(ATTACHED_TYPE)

略。

4、QML_DECLARE_TYPE

略。

5、QML_DECLARE_TYPEINFO

略。

6、QML_ELEMENT

声明类型或命名空间在 QML 中可用,使用其类或命名空间名称作为 QML 元素名称。

例如,这使得 C++ 类 Slider 可用作名为 Slider 的 QML 类型:

class Slider : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    ...
}

可以使用构建系统在主版本 1 的类型命名空间 com.mycompany.qmlcomponents 中注册类型。对于 qmake,在项目文件中指定以下内容:

CONFIG += qmltypes
QML_IMPORT_NAME = com.mycompany.qmlcomponents
QML_IMPORT_MAJOR_VERSION = 1

使用 CMake,将 URI 和版本传递给 qt_add_qml_module

qt6_add_qml_module(myapp
  URI com.mycompany.qmlcomponents
  VERSION 1.0
)

注册后,该类型可以通过导入相同的类型命名空间和版本号在 QML 中使用:

import com.mycompany.qmlcomponents 1.0

Slider {
    // ...
}

还可以通过这种方式使带有 Q_NAMESPACE 标记的命名空间可用,以便公开它们包含的带有 Q_ENUM_NS 标记的枚举。

注意:当类具有相同的名称但位于不同的命名空间时,在它们两个上使用 QML_ELEMENT 将导致冲突。 请确保对其中之一使用 QML_NAMED_ELEMENT() 。

7、QML_EXTENDED(EXTENDED_TYPE)

略。

8、QML_EXTENDED_NAMESPACE(EXTENDED_NAMESPACE)

在QML中拓展枚举。

例如,给出以下 C++ 代码:

namespace MyNamespace
{
    Q_NAMESPACE
    enum MyEnum { MyEnumerator = 10 };
    Q_ENUM_NS(MyEnum)
}

class QmlType : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    QML_EXTENDED_NAMESPACE(MyNamespace)    //拓展MyNamespace为QmlType的枚举
}

可在 QML 中访问枚举:

QmlType 
{
    property int i: QmlType.MyEnumerator // i = 10
}

注意:EXTENDED_NAMESPACE 也可以是 QObject Q_GADGET; 在这种情况下 - 与 QML_EXTENDED 相比,QML_EXTENDED 也公开了方法和属性 - 只公开了它的枚举。

注意:EXTENDED_NAMESPACE 必须有一个元对象; 即它必须是包含 Q_NAMESPACE 宏或 QObject/Q_GADGET 的命名空间。

8、QML_FOREIGN(FOREIGN_TYPE)

给无法修改代码的类添加拓展,如第三方库。 

示例,Qt官方demo:Extending QML - Extension Objects Example。

演示如何使用 QML_EXTENDED QLineEdit 提供扩展对象,而无需对其进行修改或子类化。

首先定义一个 LineEditExtension 类,定义四个属性,当属性值改变时设置 QLineEdit 相应的边距改变:

class LineEditExtension : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin NOTIFY marginsChanged)
    Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin NOTIFY marginsChanged)
    Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin NOTIFY marginsChanged)
    Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY marginsChanged)
public:
    LineEditExtension(QObject *);

    int leftMargin() const;
    void setLeftMargin(int);

    int rightMargin() const;
    void setRightMargin(int);

    int topMargin() const;
    void setTopMargin(int);

    int bottomMargin() const;
    void setBottomMargin(int);
signals:
    void marginsChanged();

private:
    QLineEdit *m_lineedit;
};

LineEditExtension::LineEditExtension(QObject *object)
: QObject(object), m_lineedit(qobject_cast<QLineEdit *>(object))
{
}

int LineEditExtension::leftMargin() const
{
    return m_lineedit->textMargins().left();
}

void LineEditExtension::setLeftMargin(int l)
{
    QMargins m = m_lineedit->textMargins();
    m.setLeft(l);
    m_lineedit->setTextMargins(m);
}

int LineEditExtension::rightMargin() const
{
    return m_lineedit->textMargins().right();
}

void LineEditExtension::setRightMargin(int r)
{
    QMargins m = m_lineedit->textMargins();
    m.setRight(r);
    m_lineedit->setTextMargins(m);
}

int LineEditExtension::topMargin() const
{
    return m_lineedit->textMargins().top();
}

void LineEditExtension::setTopMargin(int t)
{
    QMargins m = m_lineedit->textMargins();
    m.setTop(t);
    m_lineedit->setTextMargins(m);
}

int LineEditExtension::bottomMargin() const
{
    return m_lineedit->textMargins().bottom();
}

void LineEditExtension::setBottomMargin(int b)
{
    QMargins m = m_lineedit->textMargins();
    m.setBottom(b);
    m_lineedit->setTextMargins(m);
}

然后将 LineEditExtension 类注册到 QML 系统作为 QLineEdit 的扩展。 使用 QML_FOREIGN 声明一个外部类型来执行此操作,因为无法修改 Qt 的内部 QLineEdit 类。

struct QLineEditForeign
{
    Q_GADGET
    QML_FOREIGN(QLineEdit)
    QML_NAMED_ELEMENT(QLineEdit)
    QML_EXTENDED(LineEditExtension)
};

在pro文件中设置模块:

CONFIG += qmltypes
QML_IMPORT_NAME = People
QML_IMPORT_MAJOR_VERSION = 1

QML 引擎实例化一个 QLineEdit:

int main(int argc, char ** argv)
{
    QApplication app(argc, argv);

    QQmlEngine engine;
    QQmlComponent component(&engine, QUrl("qrc:example.qml"));
    auto *edit = qobject_cast<QLineEdit *>(component.create());

    if (edit) 
    {
        edit->show();
        return QApplication::exec();
    }

    qWarning() << component.errors();
    return EXIT_FAILURE;
}

在QML中即可使用拓展属性:

QLineEdit {
    leftMargin: 20
}

QQmlEngine(QML引擎)

9、QML_FOREIGN_NAMESPACE(FOREIGN_NAMESPACE)

用于向命名空间中的类添加拓展。

10、QML_IMPLEMENTS_INTERFACES(interfaces)

这个宏告诉 Qt 类实现了哪些 QML 接口。 这个宏应该只用于与使用 QML_INTERFACE 的类进行交互。通过 QML_ELEMENT 进行声明式注册才能正常运行。

11、QML_INTERFACE

该宏将 QML 系统中的 C++ 类型注册为接口。

在 QML 中注册为接口的类型也应该将自己声明为与元对象系统的接口。 例如:

struct FooInterface
{
    QML_INTERFACE
public:
    virtual ~FooInterface();
    virtual void doSomething() = 0;
};

Q_DECLARE_INTERFACE(FooInterface, "org.foo.FooInterface")

当以这种方式向 QML 注册时,它们可以用作属性类型:

Q_PROPERTY(FooInterface *foo READ foo WRITE setFoo)

当将 QObject 子类分配给此属性时,QML 引擎会自动将接口转换为 FooInterface*。

接口类型在 QML 中是隐式匿名且不可创建的。

注意:当使用 QML_INTERFACE 从类型继承时,使用 QML_IMPLEMENTS_INTERFACES 而不是 Q_INTERFACES

12、QML_NAMED_ELEMENT(name)

声明类型或命名空间在 QML 中可用,使用 name 作为元素名称。 其他方面的行为与 QML_ELEMENT 相同。

class SqlEventDatabase : public QObject
{
    Q_OBJECT
    QML_NAMED_ELEMENT(EventDatabase)

    // ...
};

13、QML_REMOVED_IN_MINOR_VERSION(VERSION)

略。

14、QML_SINGLETON

将类型声明为 QML 中的单例。 这仅在类型是 Q_OBJECT 并且在 QML 中可用(通过具有 QML_ELEMENT QML_NAMED_ELEMENT() 宏)时才有效。 默认情况下,当第一次访问类型时,每个 QQmlEngine 将尝试使用类型的默认构造函数或签名 T *create(QQmlEngine *, QJSEngine *) 的静态工厂函数来创建单例实例。如果两者都存在且可访问,则首选默认构造函数。 如果没有默认构造函数和工厂函数,则无法访问单例。

为了将默认可构造类声明为单例,所要做的就是添加 QML_SINGLETON

class MySingleton : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    QML_SINGLETON
    // Q_PROPERTY( ... )
public:
    // members, Q_INVOKABLE functions, etc.
};

当单例类不是默认可构造时,可以向其添加工厂函数,以使其可访问:

class MySingleton : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    QML_SINGLETON
    // Q_PROPERTY( ... )

public:
    static MySingleton *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
    {
        MySingleton *result = nullptr;
         // 使用一些自定义构造函数或工厂创建对象,QML 引擎将承担所有权并最终将其删除。
        return result;
    }

    // members, Q_INVOKABLE functions, etc
};

如果无法修改该类并且它没有默认构造函数或合适的工厂函数,可以提供一个 QML_FOREIGN 包装器来定义工厂函数:

struct SingletonForeign
{
    Q_GADGET
    QML_FOREIGN(MySingleton)
    QML_SINGLETON
    QML_NAMED_ELEMENT(MySingleton)
public:

    static MySingleton *create(QQmlEngine *, QJSEngine *engine)
    {
        MySingleton *result = nullptr;
        // 使用一些自定义构造函数或工厂创建实例,QML 引擎将承担所有权并最终将其删除。
        return result;
    }
};

最后,如果你想提供一个你无法控制的特定单例对象的创建,可以从工厂函数中返回它。 这是 qmlRegisterSingletonInstance() 函数的替代。如果调用:

qmlRegisterSingletonInstance("MyModule", 1, 0, "MySingleton", myObject);

由于 myObject 的类型为 MySingleton *,可以改为执行以下操作:

struct SingletonForeign
{
    Q_GADGET
    QML_FOREIGN(MySingleton)
    QML_SINGLETON
    QML_NAMED_ELEMENT(MySingleton)
public:

    // 使用之前的 myObject 对其进行初始化
    // 调用qmlRegisterSingletonInstance().
    static MySingleton *s_singletonInstance = nullptr;

    static MySingleton *create(QQmlEngine *, QJSEngine *engine)
    {
        // 实例在使用前必须存在。
        Q_ASSERT(s_singletonInstance);

        // 引擎必须与单例具有相同的线程关联。
        Q_ASSERT(engine->thread() == s_singletonInstance->thread());

        // 只能有一个引擎访问单例。
        if (s_engine)
            Q_ASSERT(engine == s_engine);
        else
            s_engine = engine;

        // 显式指定 C++ 所有权,以便引擎不会删除实例。
        QJSEngine::setObjectOwnership(s_singletonInstance, QJSEngine::CppOwnership);
        return s_singletonInstance;
    }

private:
    static QJSEngine *s_engine = nullptr;
};

这样,预先存在的类 MySingleton 就被声明为一个 QML 单例,称为 MySingleton。 通过设置 s_singletonInstance 成员,可以在使用它之前的任何时间为其指定一个实例。这些都不需要修改 MySingleton 本身。

注意:如果单例被多个 QML 引擎访问,或者访问它的 QML 引擎与单例对象本身具有不同的线程关联,则此模式不起作用。 如上所示,可以检查 create() 方法的参数以获取引擎的身份和线程关联性,以便对其进行断言。

15、QML_UNAVAILABLE

此宏声明类型在 QML 中不可用。 它使用指定的任何其他 QML 宏将称为 QQmlTypeNotAvailable 的内部虚拟类型注册为 QML_FOREIGN() 类型。

通常,模块导出的类型应该是固定的。 但是,如果 C++ 类型不可用,至少应该“保留”QML 类型名称,并为不可用类型的用户提供有意义的错误消息。

#ifdef NO_GAMES_ALLOWED
struct MinehuntGame
{
    Q_GADGET
    QML_NAMED_ELEMENT(Game)
    QML_UNAVAILABLE
    QML_UNCREATABLE("Get back to work, slacker!");
};
#else

class MinehuntGame : public QObject
{
    Q_OBJECT
    QML_NAMED_ELEMENT(Game)
    // ...
};
#endif

这将导致任何尝试使用“游戏”类型的 QML 产生错误消息:

fun.qml: Get back to work, slacker!
   Game {
   ^

使用这种技术,只需要一个 Q_GADGET 结构来自定义错误消息,而不是一个完整的 QObject。 如果没有 QML_UNCREATABLE(),QML_UNAVAILABLE 仍然会提供更具体的错误消息。

16、QML_UNCREATABLE(reason)

声明不能从 QML 创建类型。 如果该类型在 QML 中可用,这将生效,通过 QML_ELEMENT QML_NAMED_ELEMENT() 宏。 如果检测到尝试从 QML 创建类型,则 reason 将作为错误消息发出。

一些 QML 类型是隐式不可创建的,特别是用 QML_ANONYMOUS 公开的类型或用 QML_ELEMENT QML_NAMED_ELEMENT() 公开的命名空间。 对于此类类型,QML_UNCREATABLE() 可用于提供自定义错误消息。

上一篇:Qt6 QML Book/前言/欢迎加入


下一篇:excel数据导入数据库