Qt defines QML type from C++ (2)

foreword

In the previous article, I introduced how to register a C++ instantiable object type for use in QML, so today I will continue with the introduction of the previous article.

text

Register a non-instantiable type

Sometimes a QObject-derived class may need to be registered with the QML type system, but not as an instantiable type. For example, if this is the case for a C++ class:

1. is an interface type that should not be instantiated

2. It is a base class type that does not need to be exposed to QML

3. declares some enums that should be accessible from QML, but otherwise should not be instantiable

4. Types provided to QML through singleton instances should not be instantiated from QML

The QML module in Qt provides several methods for registering non-instance types:

qmlRegisterType() (without parameters) registers a C++ type that is not instantiable and cannot be referenced from QML. This enables the engine to enforce any inherited types instantiated from QML.

qmlRegisterInterface() registers a Qt interface type with a specific QML type name. The type is not instantiated from QML, but can be referenced by its type name.

qmlRegisterUncreatableType() registers a named C++ type that is not instantiable, but is recognized as a type by the QML type system. This method can be used if the type’s enumeration or attached properties are accessible from QML, but the type itself should not be instantiable.

qmlRegisterSingletonType() registers a singleton type that can be imported from QML.

Note that all C++ types registered with the QML type system must be derived from QObject, even non-instantiable classes.

Register a singleton object with a singleton type

Singleton types enable properties, signals, and methods to be exposed in namespaces without requiring clients to manually instantiate object instances. In particular the QObject singleton type is 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 the engine. The QObject singleton type instance is constructed and owned by the QQmlEngine, and will be destroyed when the engine is destroyed.

A QObject singleton type can interact in a manner similar to any other QObject or instantiated type, except that only one (engine-constructed and owned) instance exists, and it must be referenced by type name rather than id. The Q_PROPERTY of the QObject singleton type can be bound, and the Q_INVOKABLE function of the QObject module API can be used in signal handler expressions. This makes singleton types ideal for implementing styles or themes, and they can also be used instead of storing global state or providing global functionality.

Once registered, a QObject singleton type can be imported and used just like any other QObject instance exposed to QML.

For example:

If the Theme here has been registered to the MyThemeModule 1.0 system space. And the color attribute is defined, then it can be directly referenced in QML.

import MyThemeModule 1.0 as Theme
Rectangle {
color: Theme.color // binding.

Note: Enum values of registered types in QML should start with a capital letter.

OK, let’s look at qmlRegisterSingletonType to register a singleton object that can be imported from QML.

qmlRegisterSingletonType function description

qmlRegisterSingletonType has a total of three refactoring functions. Let’s take a look at the function declaration first.

int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const QUrl & url, const char *uri, int versionMajor, int versionMinor, const char
1.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QJSValue( ) ( QQmlEngine , QJSEngine ) callback)

This function can be used to register a singleton type provider callback at a specific uri and typeName, whose version is specified in versionMajor and versionMinor.

A singleton type can be QObject or QJSValue. This function should be used to register a singleton type provider function that returns a QJSValue as the singleton type.

Note: The QJSValue singleton type property does not trigger binding if changed.

Let’s see an example:

//First, define the singleton type provider callback function
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++);
    qDebug() << __FUNCTION__ << seedValue;
    return example;
}


int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    //Second, register the singleton type provider with QML by calling this function in the initialization function
    qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);

    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine. rootObjects(). isEmpty())
        return -1;

    return

Here, it can also be written as a Lambda function when registering: as follows:

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;

Called in QML:

log in to copy

import QtQuick 2.0
import Qt.example.qjsvalueApi 1.0 as ExampleApi
Item {
id: root
property int someValue: ExampleApi.MyApi.someProperty
Component.onCompleted: {
console.log("someValue = ",someValue)
}
}

Program output:

example_qjsvalue_singletontype_provider 6
qml: someValue = 5

Note that someValue here still keeps the previous value when it is called again, that is to say, it will not be repeatedly entered into the callback function. This is true after several tests.

I personally think that the above usage should not be used much in actual scenarios, so let’s take a look at the second one.

2.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QObject (* ) ( QQmlEngine , QJSEngine ) callback)

Different from the registration function above, here is a callback that returns a QObject object type.

A QObject singleton type can be referenced by its registered type name, and this type name can be used as a target in Connections type, or as an id for any other type. One exception is that QObject singleton type properties cannot be aliased (because the singleton type name does not identify the object in the same component as any other item).

Note: A QObject instance of a single type returned from a single type provider is owned by the QML engine unless the object has the explicit QQmlEngine::CppOwnership flag set.

Let’s see an example:

//Define a function class inherited from QObject

class Student : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY sigNameChanged)

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


    void setName(const QString & amp; name){
        if(name != m_name){
            m_name = name;
            emit sigNameChanged(m_name);
        }
    }
    QString getName() const {return m_name;}

    Q_INVOKABLE QString doSomething(){
        qDebug(__FUNCTION__);
        setName("xiaoming");
        return m_name;
    }

signals:
    void sigNameChanged(QString name);


private:
    QString m_name = "zhangsan";

};

//Second, define the singleton type provider function (callback)
static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)
    qDebug()<<__FUNCTION__;
    Student *student = new Student();
    return

Call function registration:

qmlRegisterSingletonType<Student>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);

In QML use:

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0

Item {
    id: root
    property string someValue: MyApi.name

    Component.onCompleted: {
        console.log("someValue = ",someValue)
        someValue = MyApi. doSomething()
        console.log("someValue = ",someValue)
    }
}

Run the code, the output is as follows:

example_qobject_singletontype_provider
qml: someValue = zhangsan
doSomething
qml:

Note that since the singleton type does not have an associated QQmlContext object, QML context and engine information is not available inside functions registered as QObject-derived types implemented by the singleton type.

3.int qmlRegisterSingletonType(const QUrl & amp;url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)

This function can be used to register a singleton type named qmlName with a version number consisting of versionMajor and versionMinor in the library imported from uri. The type is defined by the QML file at url. The URL must be absolute.

In addition, this type of QML file must contain the compilation instruction Singleton statement in its import statement.

A singleton type can be referenced by the type name it is registered with, and this type name can be used as a target in a Connections type, or as an id for any other type. Singleton type properties cannot be aliased.

Take a look at the example:

//First, define the QML singleton type that provides this function.
pragma Singleton
import QtQuick 2.0
Item {
    property int testProp1: 125
//Secondly, register the QML singleton type by calling this function in the initialization function.
qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton");

In order to use a registered singleton type in QML, the singleton type must be imported.

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

The import of singleton type is introduced here first.