Defining QML Types from C++▲
When extending QML with C++ code, a C++ class can be registered with the QML type system to enable the class to be used as a data type within QML code. While the properties, methods and signals of any QObject-derived class are accessible from QML, as discussed in Exposing Attributes of C++ Types to QML, such a class cannot be used as a data type from QML until it is registered with the type system. Additionally registration can provide other features, such as allowing a class to be used as an instantiable QML object type from QML, or enabling a singleton instance of the class to be imported and used from QML.
Additionally, the Qt QML module provides mechanisms for implementing QML-specific features such as attached properties and default properties in C++.
(Note that a number of the important concepts covered in this document are demonstrated in the Writing QML Extensions with C++ tutorial.)
NOTE: All headers that declare QML types need to be accessible without any prefix from the project's include path.
For more information about C++ and the different QML integration methods, see the C++ and QML integration overview page.
Registering C++ Types with the QML Type System▲
A QObject-derived class can be registered with the QML type system to enable the type to be used as a data type from within QML code.
The engine allows the registration of both instantiable and non-instantiable types. Registering an instantiable type enables a C++ class to be used as the definition of a QML object type, allowing it to be used in object declarations from QML code to create objects of this type. Registration also provides the engine with additional type metadata, enabling the type (and any enums declared by the class) to be used as a data type for property values, method parameters and return values, and signal parameters that are exchanged between QML and C++.
Registering a non-instantiable type also registers the class as a data type in this manner, but the type cannot be used instantiated as a QML object type from QML. This is useful, for example, if a type has enums that should be exposed to QML but the type itself should not be instantiable.
For a quick guide to choosing the correct approach to expose C++ types to QML, see Choosing the Correct Integration Method Between C++ and QML.
Preconditions▲
All the macros mentioned below are available from the qqmlregistration.h header. You need to add the following code to the files using them in order to make the macros available:
#include <QtQml/qqmlregistration.h>
Furthermore, your class declarations have to live in headers reachable via your project's include path. The declarations are used to generate registration code at compile time, and the registration code needs to include the headers that contain the declarations.
Registering an Instantiable Object Type▲
Any QObject-derived C++ class can be registered as the definition of a QML object type. Once a class is registered with the QML type system, the class can be declared and instantiated like any other object type from QML code. Once created, a class instance can be manipulated from QML; as Exposing Attributes of C++ Types to QML explains, the properties, methods and signals of any QObject-derived class are accessible from QML code.
To register a QObject-derived class as an instantiable QML object type, add QML_ELEMENT or QML_NAMED_ELEMENT(<name>) to the class declaration and CONFIG += qmltypes, a QML_IMPORT_NAME, and a QML_IMPORT_MAJOR_VERSION to your project file. This will register the class into the type namespace under the given major version, using either the class name or an explicitly given name as QML type name. The minor version(s) will be derived from any revisions attached to properties, methods, or signals. The default minor version is 0. You can explicitly restrict the type to be available only from specific minor versions by adding the QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can import suitable versions of the namespace in order to use the type.
For example, suppose there is a Message class with author and creationDate properties:
class
Message : public
QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
QML_ELEMENT
public
:
// ...
}
;
This type can be registered by adding an appropriate type namespace and version number to the project file. For example, to make the type available in the com.mycompany.messaging namespace with version 1.0:
CONFIG +=
qmltypes
QML_IMPORT_NAME =
com.mycompany.messaging
QML_IMPORT_MAJOR_VERSION =
1
If the header the class is declared in is not accessible from your project's include path, you may have to amend the include path so that the generated registration code can be compiled:
INCLUDEPATH +=
com/
mycompany/
messaging
The type can be used in an object declaration from QML, and its properties can be read and written to, as per the example below:
import
com.mycompany.messaging 1.0
Message {
author
:
"Amelie"
creationDate
:
new Date
()
}
Registering Non-Instantiable Types▲
Sometimes a QObject-derived class may need to be registered with the QML type system but not as an instantiable type. For example, this is the case if a C++ class:
-
is an interface type that should not be instantiable
-
is a base class type that does not need to be exposed to QML
-
declares some enum that should be accessible from QML, but otherwise should not be instantiable
-
is a type that should be provided to QML through a singleton instance, and should not be instantiable from QML
The Qt QML module provides several macros for registering non-instantiable types:
-
QML_ANONYMOUS registers a C++ type that is not instantiable and cannot be referred to from QML. This enables the engine to coerce any inherited types that are instantiable from QML.
-
QML_INTERFACE registers an existing Qt interface type. The type is not instantiable from QML, and you cannot declare QML properties with it. Using C++ properties of this type from QML will do the expected interface casts, though.
-
QML_UNCREATABLE(reason) combined with with QML_ELEMENT or QML_NAMED_ELEMENT registers a named C++ type that is not instantiable but should be identifiable as a type to the QML type system. This is useful if a type's enums or attached properties should be accessible from QML but the type itself should not be instantiable. The parameter should be an error message to be emitted if an attempt at creating an instance of the type is detected.
-
QML_SINGLETON combined with QML_ELEMENT or QML_NAMED_ELEMENT registers a singleton type that can be imported from QML, as discussed below.
Note that all C++ types registered with the QML type system must be QObject-derived, even if they are non-instantiable.
Registering Singleton Objects with a Singleton Type▲
A singleton type enables properties, signals and methods to be exposed in a namespace without requiring the client to manually instantiate an object instance. QObject singleton types in particular are an efficient and convenient way to provide functionality or global property values.
Note that singleton types do not have an associated QQmlContext as they are shared across all contexts in an engine. QObject singleton type instances are constructed and owned by the QQmlEngine, and will be destroyed when the engine is destroyed.
A QObject singleton type can be interacted with in a manner similar to any other QObject or instantiated type, except that only one (engine constructed and owned) instance will exist, and it must be referenced by type name rather than id. Q_PROPERTYs of QObject singleton types may be bound to, and Q_INVOKABLE functions of QObject module APIs may be used in signal handler expressions. This makes singleton types an ideal way to implement styling or theming, and they can also be used instead of ".pragma library" script imports to store global state or to provide global functionality.
Once registered, a QObject singleton type may be imported and used like any other QObject instance exposed to QML. The following example assumes that a QObject singleton type was registered into the "MyThemeModule" namespace with version 1.0, where that QObject has a QColor "color" Q_PROPERTY:
import
MyThemeModule 1.0 as
Theme
Rectangle
{
color
:
Theme.color // binding.
}
A QJSValue may also be exposed as a singleton type, however clients should be aware that properties of such a singleton type cannot be bound to.
See QML_SINGLETON for more information on how implement and register a new singleton type, and how to use an existing singleton type.
Enum values for registered types in QML should start with a capital.
Final properties▲
Properties declared final using the FINAL modifier to Q_PROPERTY cannot be overridden. This means that any properties or functions of the same name, declared either in QML or in C++ on derived types, are ignored by the QML engine. You should declare properties FINAL when possible, in order to avoid accidental overrides. An override of a property is visible not only in derived classes, but also to QML code executing the context of the base class. Such QML code, typically expects the original property, though. This is a frequent source of mistakes.
Properties declared FINAL can also not be overridden by functions in QML, or by Q_INVOKABLE methods in C++.
Type Revisions and Versions▲
Many of the type registration functions require versions to be specified for the registered type. Type revisions and versions allow new properties or methods to exist in the new version while remaining compatible with previous versions.
Consider these two QML files:
// main.qml
import
QtQuick 1.0
Item {
id
:
root
MyType {}
}
// MyType.qml
import
MyTypes 1.0
CppType {
value
:
root.x
}
where CppType maps to the C++ class CppType.
If the author of CppType adds a root property to CppType in a new version of their type definition, root.x now resolves to a different value because root is also the id of the top level component. The author could specify that the new root property is available from a specific minor version. This permits new properties and features to be added to existing types without breaking existing programs.
The REVISION tag is used to mark the root property as added in revision 1 of the type. Methods such as Q_INVOKABLE's, signals and slots can also be tagged for a revision using the Q_REVISION(x) macro:
class
CppType : public
BaseType
{
Q_OBJECT
Q_PROPERTY(int
root READ root WRITE setRoot NOTIFY rootChanged REVISION 1
)
QML_ELEMENT
signals
:
Q_REVISION(1
) void
rootChanged();
}
;
The revisions given this way are automatically interpreted as minor versions to the major version given in the project file. In this case, root is only available when MyTypes version 1.1 or higher is imported. Imports of MyTypes version 1.0 remain unaffected.
For the same reason, new types introduced in later versions should be tagged with the QML_ADDED_IN_MINOR_VERSION macro.
This feature of the language allows for behavioural changes to be made without breaking existing applications. Consequently QML module authors should always remember to document what changed between minor versions, and QML module users should check that their application still runs correctly before deploying an updated import statement.
Revisions of a base class that your type depends upon are automatically registered when registering the type itself. This is useful when deriving from base classes provided by other authors, e.g. when extending classes from the Qt Quick module.
The QML engine does not support revisions for properties or signals of grouped and attached property objects.
Registering Extension Objects▲
When integrating existing classes and technology into QML, APIs will often need tweaking to fit better into the declarative environment. Although the best results are usually obtained by modifying the original classes directly, if this is either not possible or is complicated by some other concerns, extension objects allow limited extension possibilities without direct modifications.
Extension objects add additional properties to an existing type. An extended type definition allows the programmer to supply an additional type, known as the extension type, when registering the class. Its members are transparently merged with the original target class when used from within QML. For example:
QLineEdit {
leftMargin
:
20
}
The leftMargin property is a new property added to an existing C++ type, QLineEdit, without modifying its source code.
The QML_EXTENDED(extension) macro is for registering extended types. The argument is the name of another class to be used as extension.
You can also use QML_EXTENDED_NAMESPACE(namespace) to register a namespace, and especially the enumerations declared within, as an extension to a type. If the type you are extending is itself a namespace, you need to use QML_NAMESPACE_EXTENDED(namespace) instead.
An extension class is a regular QObject, with a constructor that takes a QObject pointer. However, the extension class creation is delayed until the first extended property is accessed. The extension class is created and the target object is passed in as the parent. When the property on the original is accessed, the corresponding property on the extension object is used instead.
The Extension Objects Example demonstrates a usage of extension objects.
Registering Foreign Types▲
There may be C++ types that cannot be modified to hold the above mentioned macros. Those may be types from 3rdparty libraries, or types that need to fulfill some contract that contradicts the presence of those macros. You can still expose those types to QML, though, using the QML_FOREIGN macro. In order to do this, create a separate struct that consists entirely of the registration macros, like this:
// Contains class Immutable3rdParty
#include <3rdpartyheader.h>
struct
Foreign
{
Q_GADGET
QML_FOREIGN(Immutable3rdParty)
QML_NAMED_ELEMENT(Accessible3rdParty)
QML_ADDED_IN_VERSION(2
, 4
)
// QML_EXTENDED, QML_SINGLETON ...
}
;
From this code, you get a QML type with the methods and properties of Immutable3rdParty, and the QML traits (e.g.: singleton, extended) specified in Foreign.
Defining QML-Specific Types and Attributes▲
Providing Attached Properties▲
In the QML language syntax, there is a notion of attached properties and attached signal handlers, which are additional attributes that are attached to an object. Essentially, such attributes are implemented and provided by an attaching type, and these attributes may be attached to an object of another type. This contrasts with ordinary object properties which are provided by the object type itself (or the object's inherited type).
For example, the Item below uses attached properties and attached handlers:
import
QtQuick 2.0
Item
{
width
:
100
; height
:
100
focus
:
true
Keys.enabled
:
false
Keys.onReturnPressed
:
console.log("Return key was pressed"
)
}
Here, the Item object is able to access and set the values of Keys.enabled and Keys.onReturnPressed. This allows the Item object to access these extra attributes as an extension to its own existing attributes.
Steps for Implementing Attached Objects▲
When considering the above example, there are several parties involved:
-
There is an instance of an anonymous attached object type, with an enabled and a returnPressed signal, that has been attached to the Item object to enable it to access and set these attributes.
-
The Item object is the attachee, to which the instance of the attached object type has been attached.
-
Keys is the attaching type, which provides the attachee with a named qualifier, "Keys", through which it may access the attributes of the attached object type.
When the QML engine processes this code, it creates a single instance of the attached object type and attaches this instance to the Item object, thereby providing it with access to the enabled and returnPressed attributes of the instance.
The mechanisms for providing attached objects can be implemented from C++ by providing classes for the attached object type and attaching type. For the attached object type, provide a QObject-derived class that defines the attributes to be made accessible to attachee objects. For the attaching type, provide a QObject-derived class that:
-
implements a static qmlAttachedProperties() with the following signature:
Sélectionnezstatic
&
lt;AttachedPropertiesType&
gt;*
qmlAttachedProperties(QObject*
object);This method should return an instance of the attached object type.
The QML engine invokes this method in order to attach an instance of the attached object type to the attachee specified by the object parameter. It is customary, though not strictly required, for this method implementation to parent the returned instance to object in order to prevent memory leaks.
This method is called at most once by the engine for each attachee object instance, as the engine caches the returned instance pointer for subsequent attached property accesses. Consequently the attachment object may not be deleted until the attachee object is destroyed.
-
is declared as an attaching type, by adding the QML_ATTACHED(attached) macro to the class declaration. The argument is the name of the attached object type
Implementing Attached Objects: An Example▲
For example, take the Message type described in an earlier example:
class
Message : public
QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
QML_ELEMENT
public
:
// ...
}
;
Suppose it is necessary to trigger a signal on a Message when it is published to a message board, and also track when the message has expired on the message board. Since it doesn't make sense to add these attributes directly to a Message, as the attributes are more relevant to the message board context, they could be implemented as attached attributes on a Message object that are provided through a "MessageBoard" qualifier. In terms of the concepts described earlier, the parties involved here are:
-
An instance of an anonymous attached object type, which provides a published signal and an expired property. This type is implemented by MessageBoardAttachedType below
-
A Message object, which will be the attachee
-
The MessageBoard type, which will be the attaching type that is used by Message objects to access the attached attributes
Following is an example implementation. First, there needs to be an attached object type with the necessary properties and signals that will be accessible to the attachee: