<qqmlintegration.h> - Registering C++ types to QML

Header: #include <QtQmlIntegration/qqmlintegration.h>

详细说明

此头提供了宏,可用于在 QML 中注册 C++ 类型。

另请参阅 qt_generate_foreign_qml_typesOverview - QML and C++ Integrationqmlperegistrar

宏文档

QML_ADDED_IN_VERSION(MAJOR, MINOR)

声明外层类型或命名空间是在指定的MAJOR.MINOR 版本中添加的。版本被假定为与Q_REVISION() 宏在方法、槽或信号上给出的修订,以及Q_PROPERTY() 在属性上声明的 REVISION() 属性一致。

QML_ADDED_IN_VERSION() 只有在 QML 中通过QML_ELEMENTQML_NAMED_ELEMENT( )、QML_ANONYMOUSQML_INTERFACE 宏,使类型或命名空间可用时才生效。

如果该类型所属的 QML 模块导入的版本比通过这种方式确定的版本低,则该 QML 类型是不可见的。

另请参阅 QML_ELEMENTQML_NAMED_ELEMENT

QML_ANONYMOUS

声明外层类型可用,但在 QML 中是匿名的。该类型不能在 QML 中创建或用于声明属性,但当从 C++ 传递时,它会被识别。在 QML 中,如果该类型的属性是在 C++ 中声明的,你就可以使用它们。

另请参阅 QML_ELEMENT,QML_NAMED_ELEMENT(),QML_UNCREATABLE(), 和QML_INTERFACE

QML_ATTACHED(ATTACHED_TYPE)

声明外层类型将ATTACHED_TYPE 作为附加属性附加到其他类型。如果使用QML_ELEMENTQML_NAMED_ELEMENT() 宏将该类型暴露给 QML,该声明就会生效。

注意: 类名需要完全限定,即使你已经在命名空间内。

另请参阅 QML_ELEMENT,QML_NAMED_ELEMENT(),qmlAttachedPropertiesObject() 和Providing Attached Properties(提供附加属性)。

[since 6.5] QML_CONSTRUCTIBLE_VALUE

将周围的值类型标记为可构造。也就是说,在为该类型的属性赋值时,可以使用该类型的任何Q_INVOKABLE 构造函数,这些构造函数需要一个参数。

可构造值类型的声明如下:

class MyValueType
{
    Q_GADGET
    QML_VALUE_TYPE(myValueType)
    QML_CONSTRUCTIBLE_VALUE
public:
    Q_INVOKABLE MyValueType(double d);

    // ...
};

有了上述类型,下面的 QML 代码将使用给定的构造函数生成一个MyValueType 值,并将其赋值给属性。

QtObject {
    property myValueType v: 5.4
}

你也可以用这种方法构造值列表:

QtObject {
    property list<myValueType> v: [5.4, 4.5, 3.3]
}

如果你让值类型可寻址,你可以在类型断言中使用这种类型来显式构造它:

pragma ValueTypeBehavior: Addressable

QtObject {
    function process(d: real) {
        let v = d as myValueType;
        // v is a myValueType now, not a number
    }
}

此宏在 Qt 6.5 中引入。

另请参见 QML_VALUE_TYPE

QML_ELEMENT

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

例如,这使得 C++ 类Slider 可作为名为Slider 的 QML 类型使用。它的所有属性、可调用方法和枚举都会公开。

class Slider : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
    // ...
public:
    enum Slippiness {
        Dry, Wet, Icy
    };
    Q_ENUM(Slippiness)

    Q_INVOKABLE void slide(Slippiness slippiness);

    // ...
}

你可以使用构建系统在类型命名空间com.mycompany.qmlcomponents中注册该类型,主要版本为1 。对于 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 {
    value: 12
    Component.onCompleted: slide(Slider.Icy)

    // ...
}

您还可以通过这种方式使标有Q_NAMESPACE 的命名空间可用,以便公开其中包含的标有Q_ENUM_NS 的枚举:

namespace MyNamespace {
  Q_NAMESPACE
  QML_ELEMENT

  enum MyEnum {
      Key1,
      Key2,
  };
  Q_ENUM_NS(MyEnum)
}

在 QML 中,您就可以使用这些枚举:

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

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

注意: 类名需要完全限定,即使你已经在命名空间内。

另请参阅 在 C++ 和 QML 之间选择正确的集成方法QML_NAMED_ELEMENT()、Q_REVISION() 和QML_ADDED_IN_VERSION()。

QML_EXTENDED(EXTENDED_TYPE)

声明外层类型使用EXTENDED_TYPE 作为扩展,在 QML 中提供更多属性、方法和枚举。如果使用QML_ELEMENTQML_NAMED_ELEMENT() 宏向 QML 公开该类型,该声明将生效。

警告: EXTENDED_TYPE 的成员被隐式地视为 FINAL。

注意: 类名需要完全限定,即使你已经在命名空间内。

另请参阅 QML_ELEMENT,QML_NAMED_ELEMENT(),QML_EXTENDED_NAMESPACE() 和Registering Extension Objects(注册扩展对象)。

QML_EXTENDED_NAMESPACE(EXTENSION_NAMESPACE)

声明外层类型使用EXTENSION_NAMESPACE 作为扩展,在 QML 中提供进一步的枚举。如果该类型使用QML_ELEMENTQML_NAMED_ELEMENT() 宏暴露在 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)
}

我们就能在 QML 中访问枚举:

QmlType {
    property int i: QmlType.MyEnumerator // i will be 10
}

注意: EXTENSION_NAMESPACE 也可以是QObject 或 QGadget;在这种情况下,与同样暴露方法和属性的QML_EXTENDED 相反,只暴露了它的枚举。

注: EXTENSION_NAMESPACE 必须有一个元对象;也就是说,它必须是一个包含Q_NAMESPACE 宏的命名空间或QObject/QGadget。

注意: 类名必须是完全限定的,即使你已经在命名空间内。

另请参阅 QML_NAMESPACE_EXTENDED(),QML_ELEMENT,QML_NAMED_ELEMENT(),QML_EXTENDED(),Registering Extension Objects,Q_ENUM, 和Q_ENUM_NS

QML_EXTRA_VERSION(MAJOR, MINOR)

声明该类型在MAJOR 版本中也可用。MINOR 。如果一个类型在多个主要版本中都可用,这将很有帮助。

类型会自动注册到

值得注意的是,它们不会自动注册到上述版本之间的任何PAST_MAJOR_VERSIONS。您可以使用 QML_EXTRA_VERSION,在更多主要版本中手动注册您的类型。

注意: 保留多个PAST_MAJOR_VERSIONS会耗费大量计算资源。

另请参阅 QML_ELEMENTQML_ADDED_IN_VERSION

QML_FOREIGN(FOREIGN_TYPE)

声明外层 C++ 类型中的QML_ELEMENT,QML_NAMED_ELEMENT(),QML_ANONYMOUS,QML_INTERFACE,QML_UNCREATABLE(),QML_SINGLETON,QML_ADDED_IN_VERSION(),QML_REMOVED_IN_VERSION(),QML_ADDED_IN_MINOR_VERSION(),QML_REMOVED_IN_MINOR_VERSION(),QML_EXTENDED(),QML_EXTENDED_NAMESPACE(), 或QML_NAMESPACE_EXTENDED() 宏不适用于外层类型,而适用于FOREIGN_TYPE 。外层类型仍然需要使用Q_GADGETQ_OBJECT 宏在元对象系统中注册。

这对于注册因属于第三方库等原因而无法添加宏的类型非常有用。要注册命名空间,请参阅QML_FOREIGN_NAMESPACE() 。

注意: 您可能希望使用QML_NAMED_ELEMENT() 而不是QML_ELEMENT 。使用QML_ELEMENT 时,元素以其包含的结构命名,而不是以外来类型命名。用 C++ 编写高级 QML 扩展中的 "外来对象集成"一章演示了这一点。

注意: QML_ATTACHED() 目前不能这样重定向。它必须特定于实现 qmlAttachedProperties() 的同一类型。

注意: 类名需要完全限定,即使您已经在命名空间内。

另请参见 QML_ELEMENTQML_NAMED_ELEMENT() 和QML_FOREIGN_NAMESPACE()。

QML_FOREIGN_NAMESPACE(FOREIGN_NAMESPACE)

声明外层 C++ 命名空间中的任何QML_ELEMENT,QML_NAMED_ELEMENT(),QML_ANONYMOUS,QML_INTERFACE,QML_UNCREATABLE(),QML_SINGLETON,QML_ADDED_IN_VERSION(),QML_REMOVED_IN_VERSION(),QML_ADDED_IN_MINOR_VERSION() 或QML_REMOVED_IN_MINOR_VERSION() 宏都不适用于外层类型,而适用于FOREIGN_NAMESPACE 。外层命名空间仍需使用Q_NAMESPACE 宏向元对象系统注册。

这对于注册因属于第三方库等原因而无法添加宏的命名空间非常有用。

另请参见 QML_ELEMENT,QML_NAMED_ELEMENT() 和QML_FOREIGN()。

QML_IMPLEMENTS_INTERFACES(interfaces)

此宏告诉 Qt 该类实现了哪个 QMLinterfaces 。该宏只能用于与使用QML_INTERFACE 的类连接,否则请使用Q_INTERFACES 。为了使通过QML_ELEMENT 进行的声明式注册正常运行,需要使用该宏。

另请参阅 QML_INTERFACEQ_INTERFACES

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

另请参阅 QML_IMPLEMENTS_INTERFACES(),QML_ELEMENT,QML_NAMED_ELEMENT(),QML_UNCREATABLE(), 和QML_ANONYMOUS

QML_NAMED_ELEMENT(name)

声明 QML 中可用的外层类型或命名空间,使用name 作为元素名。否则行为与QML_ELEMENT 相同。

class SqlEventDatabase : public QObject
{
    Q_OBJECT
    QML_NAMED_ELEMENT(EventDatabase)

    // ...
};

另请参阅 在 C++ 和 QML 之间选择正确的集成方法QML_ELEMENT

QML_REMOVED_IN_VERSION(MAJOR, MINOR)

声明外层类型或命名空间已在指定的MAJOR.MINOR 版本中移除。这主要用于替换 QML 类型的实现。如果相同 QML 名称的不同类型或命名空间上有相应的QML_ADDED_IN_VERSION() ,则在导入小于MAJOR.MINOR 的模块版本时使用移除的类型,在导入大于或等于MAJOR.MINOR 的模块版本时使用添加的类型。

QML_REMOVED_IN_VERSION() 只有在 QML 有QML_ELEMENT,QML_NAMED_ELEMENT() ,QML_ANONYMOUS, 或QML_INTERFACE 宏的情况下才会生效。

另请参阅 QML_ELEMENTQML_NAMED_ELEMENT

QML_SEQUENTIAL_CONTAINER(VALUE_TYPE)

VALUE_TYPE VALUE_TYPE 可以是实际类型,也可以是指向对象类型的指针。由于容器通常是模板,因此很少能将此宏添加到实际的容器声明中。您应该使用 将类型注册附加到模板实例化中。例如,使用这种技术,您可以像这样声明顺序容器:QML_FOREIGN

class IntDequeRegistration
{
  Q_GADGET
  QML_FOREIGN(std::deque<int>)
  QML_ANONYMOUS
  QML_SEQUENTIAL_CONTAINER(int)
};

之后,你就可以像在 QML 中使用 JavaScript 数组一样使用容器了。

class Maze
{
  Q_OBJECT
  Q_ELEMENT
  // 0: North, 1: East, 2: South, 3: West
  Q_PROPERTY(std::deque<int> solution READ solution CONSTANT FINAL)
  [...]
}
Item {
  Maze {
    id: maze
  }

  function showSolution() {
      maze.solution.forEach([...])
  }
}

注: 对于QML 值类型 QList 会自动注册为顺序容器。对于QML Object Types QQmlListProperty 则是。您不必添加这些注册。

注意: 目前您不能给容器自定义名称。传给QML_NAMED_ELEMENT 的任何参数都将被忽略。自动注册的顺序容器可用熟悉的list<...>名称,例如list<QtObject>list<font>。

注意: 类名需要完全限定,即使您已经在命名空间内。

另请参阅 QML_ANONYMOUSQML_FOREIGN()。

QML_SINGLETON

声明外层类型在 QML 中是单例。这只有当该类型是Q_OBJECT 并且在 QML 中可用(通过QML_ELEMENTQML_NAMED_ELEMENT() 宏)时才会生效。默认情况下,当首次访问该类型时,每个QQmlEngine 都会尝试使用该类型的默认构造函数或签名为T *create(QQmlEngine *, QJSEngine *) 的静态工厂函数创建一个单例。如果两者都存在且都可访问,则优先使用默认构造函数。如果没有默认构造函数和工厂函数,单例就无法访问。QML 引擎通常会承担单例的所有权,并在引擎本身销毁时删除它。你可以在单例上调用QJSEngine::setObjectOwnership() 来防止这种情况。

要把默认可构造类声明为单例,只需添加 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;
        // Create the object using some custom constructor or factory.
        // The QML engine will assume ownership and delete it, eventually.
        return result;
    }

    // members, Q_INVOKABLE functions, etc
};

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

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

    static MySingleton *create(QQmlEngine *, QJSEngine *engine)
    {
        MySingleton *result = nullptr;
        // Create the instance using some custom constructor or factory.
        // The QML engine will assume ownership and delete it, eventually.
        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:

    // Initialize this using myObject where you would previously
    // call qmlRegisterSingletonInstance().
    inline static MySingleton *s_singletonInstance = nullptr;

    static MySingleton *create(QQmlEngine *, QJSEngine *engine)
    {
        // The instance has to exist before it is used. We cannot replace it.
        Q_ASSERT(s_singletonInstance);

        // The engine has to have the same thread affinity as the singleton.
        Q_ASSERT(engine->thread() == s_singletonInstance->thread());

        // There can only be one engine accessing the singleton.
        if (s_engine)
            Q_ASSERT(engine == s_engine);
        else
            s_engine = engine;

        // Explicitly specify C++ ownership so that the engine doesn't delete
        // the instance.
        QJSEngine::setObjectOwnership(s_singletonInstance,
                                      QJSEngine::CppOwnership);
        return s_singletonInstance;
    }

private:
    inline static QJSEngine *s_engine = nullptr;
};

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

注意: 如果单例被多个 QML 引擎访问,或访问它的 QML 引擎的线程亲和性与单例对象本身不同,这种模式就不起作用。如上所示,你可以检查create() 方法的参数,以确定引擎的身份和线程亲和性。

另请参阅 QML_ELEMENT,QML_NAMED_ELEMENT(),qmlRegisterSingletonInstance(),QQmlEngine::singletonInstance() 和Singletons in QML

[since 6.5] QML_STRUCTURED_VALUE

将周围的值类型标记为结构化。结构化值类型可以也最好是从 JavaScript 对象中逐个属性构造出来的。不过,结构化值类型也总是QML_CONSTRUCTIBLE_VALUE 。这意味着,您仍然可以提供Q_INVOKABLE 构造函数,以便处理从原始类型构造的问题。

结构化值类型的声明方法如下:

class MyValueType
{
    Q_GADGET
    QML_VALUE_TYPE(myValueType)
    QML_STRUCTURED_VALUE
    Q_PROPERTY(double d READ d WRITE setD)
    Q_PROPERTY(string e READ e WRITE setE)

    // ...
};

然后可以按如下方式填充该类型的属性:

QtObject {
    property myValueType v: ({d: 4.4, e: "a string"})
}

为了将 JavaScript 对象与可能被理解为 JavaScript 代码块的内容区分开来,额外的括号是必要的。

您也可以用这种方法构建值列表:

QtObject {
    property list<myValueType> v: [
        {d: 4.4, e: "a string"},
        {d: 7.1, e: "another string"}
    ]
}

如果值类型是可寻址的,则可以在类型断言中使用这种类型来显式构造它:

pragma ValueTypeBehavior: Addressable

QtObject {
    function process(d: real) {
        let v = {d: d, e: objectName} as myValueType;
        // v is a myValueType now
    }
}

该宏在 Qt 6.5 中引入。

另请参见 QML_VALUE_TYPEQML_CONSTRUCTIBLE_VALUE

QML_UNAVAILABLE

该宏声明外层类型在 QML 中不可用。它注册了一个名为QQmlTypeNotAvailable 的内部虚拟类型,作为QML_FOREIGN() 类型,使用你指定的任何其他 QML 宏。

通常,模块导出的类型应该是固定的。但是,如果 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

这将导致任何试图使用 "Game "类型的 QML 产生错误信息:

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

使用这种技术,你只需要一个Q_GADGET struct 来定制错误信息,而不需要一个完整的QObject 。在没有QML_UNCREATABLE() 的情况下,QML_UNAVAILABLE 仍然会产生比通常的 "is not a type"(不是一个类型)更具体的错误信息。

注意: 类名需要完全限定,即使您已经在命名空间内。

另请参见 QML_ELEMENT,QML_NAMED_ELEMENT(),QML_UNCREATABLE() 和QML_FOREIGN()。

QML_UNCREATABLE(reason)

声明外层类型不能从 QML 创建。如果 QML 中有QML_ELEMENTQML_NAMED_ELEMENT() 宏,则此声明生效。如果检测到试图从 QML 创建类型,reason 将作为错误信息发出。

有些 QML 类型是隐式不可创建的,特别是用QML_ANONYMOUS 公开的类型或用QML_ELEMENTQML_NAMED_ELEMENT() 公开的命名空间。

自 Qt 6.0 起,您可以使用""来代替原因,从而使用标准信息。

另请参见 QML_ELEMENTQML_NAMED_ELEMENT() 和QML_ANONYMOUS

QML_VALUE_TYPE(name)

声明 QML 中可用的外层类型或命名空间,使用name 作为名称。类型必须是值类型,名称必须小写。

class MyValueType
{
    Q_GADGET
    QML_VALUE_TYPE(myValueType)

    // ...
};

另请参阅 在 C++ 和 QML 之间选择正确的集成方法QML_NAMED_ELEMENT

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.