Hello Qt -- mixed programming of QML and C + +

1, Introduction to mixed programming of QML and C + +

QML and C + + mixed programming is to use QML to build UI efficiently and conveniently, while C + + is used to realize business logic and complex algorithms.

2, QML access C++

Qt integrates QML engine and Qt meta object system, which makes QML easy to be extended from C + +. Under certain conditions, QML can access the members of QObject derived classes, such as signals, slot functions, enumeration types, attributes, member functions, etc.

There are two ways for QML to access C + +: one is to register C + + classes in Qt meta object system, instantiate and access them in QML; Second, instantiate in C + + and set it as QML context attribute, which can be used directly in QML. The first method can make C + + class as a data type in QML, such as function parameter type or attribute type. It can also use its enumeration type, singleton, etc., which is more powerful.

3, Implementation of C + + classes

To be accessed by QML, C + + classes must first meet two conditions: one is derived from QObject class or subclass of QObject class, and the other is to use Q_OBJECT macro. QObject class is the base class of all Qt objects. As the core of Qt object model, QObject class provides many important features such as signal and slot mechanism. Q_ The object macro must be declared in the private area (c + + defaults to private), which is used to declare signals and slots. The content provided by the Qt meta object system is used, and the position is generally in the first line of the statement block. Projects select Qt Quick Application, and the project name is Hello.

1. Signal and slot implementation

A. C + + class implementation

#ifndef HELLO_H
#define HELLO_H

#include <QObject>
#include <QDebug>

class Hello: public QObject
{
    Q_OBJECT
public slots:
  void doSomething()
  {
    qDebug() << "Hello::dosomething() is called.";
  }
signals:
  void begin();
};

#endif // HELLO_H

Both Signal begin() and slot doSomething() in the Hello class can be accessed by QML. The slot must be declared as public or protected. The emit keyword is used when the Signal is used in C + +, but it is a common function in QML. The usage is the same as that of the function. The form of Signal processor is on and the initial letter of Signal is capitalized. Signals do not support overloading. Multiple signals have the same name but different parameters. Only the last Signal can be recognized, which is independent of the Signal parameters.

B. Register C + + types

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);
  //Register C + + types Hello
  qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

  QQmlApplicationEngine engine;
  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

  return app.exec();

}

Register the C + + class to the Qt meta object system.

C. Import C + + classes in QML file and use

import QtQuick 2.5
import QtQuick.Window 2.2
//Import registered C + + classes
import Hello.module 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello QML")
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin();//Click the mouse to call the begin signal function
        }
    }

    Hello{
        id:hello   //Instance of Hello class
        onBegin:doSomething()
    }
}

import the registered C + + class in the QML file, and the keyword Hello can be used as a QML type in the current QML file. MouseArea covers the interface. When you click the mouse, it will send a begin() signal, and then call the doSomething() slot function.

2. Enumeration type implementation

A. Definition of enumeration in C + + class

#ifndef HELLO_H
#define HELLO_H

#include <QObject>
#include <QDebug>

class Hello: public QObject
{
    Q_OBJECT
    Q_ENUMS(Color)
public:
  Hello():m_color(RED)
  {
    qDebug() << "Hello() is called.";
  }
  //enumeration
  enum Color
  {
    RED,
    BLUE,
    BLACK
  };
public slots:
  void doSomething(Color color)
  {
    qDebug() << "Hello::dosomething() is called " << color;
  }

signals:
  void begin();
private:
  Color m_color;
};

#endif // HELLO_H

The public Color enumeration type is added to the C + + class. To use the enumeration type in QML, you need to use Q_ENUMS() macro.

B. C + + enumeration types used in QML files

import QtQuick 2.5
import QtQuick.Window 2.2
//Import registered C + + classes
import Hello.module 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello QML")
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin();//Click the mouse to call the begin signal function
        }
    }
    Hello{
        id:hello   //Instance of Hello class
        onBegin:doSomething(Hello.RED)
    }
}

Enumeration types are used in QML by using "." through C + + type names Operator directly accesses enumeration members, such as hello RED.

3. Member function implementation

A. Member function definition

#ifndef HELLO_H
#define HELLO_H

#include <QObject>
#include <QDebug>

class Hello: public QObject
{
    Q_OBJECT
    Q_ENUMS(Color)
public:
  Hello():m_color(RED)
  {
    qDebug() << "Hello() is called.";
  }

  //enumeration
  enum Color
  {
    RED,
    BLUE,
    BLACK
  };

  Q_INVOKABLE void show()
  {
    qDebug() << "show() is called.";
  }
public slots:
  void doSomething(Color color)
  {
    qDebug() << "Hello::dosomething() is called " << color;
  }

signals:
  void begin();
private:
  Color m_color;
};

#endif // HELLO_H

If the C + + member function is accessed in QML, the C + + member function must be a public or protected member function and use Q_INVOKABLE macro, located in front of the return type of the function.

Calling C++ class member functions in B and QML

import QtQuick 2.5
import QtQuick.Window 2.2
//Import registered C + + classes
import Hello.module 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello QML")
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin();//Click the mouse to call the begin signal function
            hello.show();
        }
    }
    Hello{
        id:hello   //Instance of Hello class
        onBegin:doSomething(Hello.RED)
    }
}

The form of accessing C + + member functions in QML is ".", Such as hello Show(), supports function overloading.

4. Properties of C + + classes

A. Definition of attributes in C + + classes

#ifndef HELLO_H
#define HELLO_H

#include <QObject>
#include <QDebug>

class Hello: public QObject
{
    Q_OBJECT
    Q_ENUMS(Color)
    //Attribute declaration
    Q_PROPERTY(Color color READ color WRITE setColor NOTIFY colorChanged)
public:
  Hello():m_color(RED)
  {
    qDebug() << "Hello() is called.";
  }

  //enumeration
  enum Color
  {
    RED,
    BLUE,
    BLACK
  };
  Q_INVOKABLE void show()
  {
    qDebug() << "show() is called.";
  }

  Color color() const
  {
    return m_color;
  }

  void setColor(const Color& color)
  {
    if(color != m_color)
    {
        m_color = color;
        emit colorChanged();
    }
  }
public slots:
  void doSomething(Color color)
  {
    qDebug() << "Hello::dosomething() is called " << color;
  }

signals:
  void begin();
  void colorChanged();
private:
  Color m_color;//attribute
};


#endif // HELLO_H

Q is added to the C + + class_ Property () macro is used to declare properties in QObject derived classes. Properties are the same as data members of the same kind, but some additional features can be accessed through Qt meta object system.

Q_PROPERTY()(type name
     (READ getFunction [WRITE setFunction] |
             MEMBER memberName [(READ getFunction | WRITE setFunction)])
            [RESET resetFunction]
            [NOTIFY notifySignal]
            [REVISION int]
            [DESIGNABLE bool]
            [SCRIPTABLE bool]
            [STORED bool]
            [USER bool]
            [CONSTANT]
            [FINAL])

The type and name of the attribute are required, and others are optional. The commonly used ones are READ, WRITE and NOTIFY. The type of attribute can be any type supported by QVariant or user-defined type, including user-defined class, list type, group attribute, etc. In addition, READ, WRITE and RESET of attributes can be inherited or virtual functions, which are not commonly used.

READ: READ the attribute value. It is required if MEMBER is not set. In general, a function is a const function, and the return value type must be the type of the attribute itself or the const reference of this type, without parameters.

WRITE: set attribute value; optional. The function must return void with one and only one parameter. The parameter type must be the type of the property itself or a pointer or reference to the type.

NOTIFY: optional signal associated with the attribute. The signal must be declared in the class. When the attribute value changes, the signal can be triggered. There can be no parameters. If there are parameters, it can only be a parameter of the same type as the attribute itself, which is used to record the value after the attribute changes.

B. Modifying attributes in QML

import QtQuick 2.5
import QtQuick.Window 2.2
//Import registered C + + classes
import Hello.module 1.0


Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello QML")
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin()//Click the mouse to call the begin signal function
            hello.show()
            hello.color = 2  //modify attribute
        }
    }
    Hello{
        id:hello   //Instance of Hello class
        onBegin:doSomething(Hello.RED)
        onColorChanged:console.log("color changed.")
    }
}

M in C + + classes_ The color attribute can be accessed and modified in QML. The color() function is called when accessing and the setColor() function is called when modifying. At the same time, a signal is sent to automatically update the color attribute value.

4, Register C + + classes as QML types

QObject derived classes can be registered in Qt meta object system, so that the class can be used as a data type in QML like other built-in types. The QML engine allows the registration of either instantiatable or non instantiatable types. The common registration functions are:

qmlRegisterInterface()
qmlRegisterRevision()
qmlRegisterSingletonType()
qmlRegisterType()
qmlRegisterTypeNotAvailable()
qmlRegisterUncreatableType()


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

The template function registers C + + classes into the Qt meta object system. uri is the library name that needs to be imported into QML, versionMajor and versionMinor are their version numbers, and qmlName is the type name that can be used in QML.

qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

main.cpp registers the Hello class as a GHello type that can be used in QML. The major version is 1, the minor version is 0, and the name of the library is hello module. main. The C + + library is imported into QML, and an object is constructed with Hello. The id is hello. You can use id to access C + +.

The registration action must be before the QML context is created, otherwise it is invalid.

QQuickView provides a window for QtQuickUI, which can easily load QML files and display its interface. QApplication is derived from QGuiApplication, and QGuiApplication is derived from QCoreApplication. These three classes are common classes for managing Qt applications. QQmlApplicationEngine can easily load applications from a single QML file, which is derived from QQmlEngine, and QQmlEngine provides loading QML assembly Can be used with QQmlComponent, QQmlContext, etc.

5, QML context attribute settings

When a C + + application loads a QML object, it can directly embed some C + + data for QML use. You need to use QQmlContext::setContextProperty() to set the QML context property. The context property can be a simple type or any custom class object.

1. C + + setting context properties

#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include "hello.h"

int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);


  QQuickView view;
  Hello hello;
  view.rootContext()->setContextProperty("hello", &hello);
  view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));
  view.show();

  return app.exec();
}

The Hello class is instantiated as a hello object and then registered as a QML context attribute.

B. QML usage

import QtQuick 2.5

Item {
    width: 640
    height: 480
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin()//Click the mouse to call the begin signal function
            hello.show()
        }
    }

    Connections{
       target:hello
       onBegin:console.log("hello")
    }
}

In main In QML, the Hello type cannot be used for instantiation, and the doSomething() slot function cannot be called, because the enumeration type in doSomething() function cannot be accessed in QML. The correct usage is to access C + + through the QML context attribute "hello" that has been set. You can access the signal begin() and member function show(). At this time, the signal processor needs to use Connections to process.

6, C + + accessing QML

Properties, functions and signals in QML can also be accessed in C + +.

When loading QML files in C + +, you can use QQmlComponent or qqquickview, and then you can access QML objects in C + +. Qqquickview provides a window to display the user interface, while QQmlComponent does not.

1. C + + uses QQmlComponent

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "hello.h"

int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);

  //Register C + + types Hello
  qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

  QQmlApplicationEngine engine;
  QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
  component.create();

  return app.exec();
}

2. C + + uses the properties of QML

After loading the QML file and instantiating the component in C + +, you can access and modify the attribute value of the instance in C + +, which can be either QML built-in attribute or user-defined attribute.

A. Attributes of some elements defined in QML

import QtQuick 2.5
import QtQuick.Window 2.0
import Hello.module 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: "Hello QML"
    color: "white"
    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin()//Click the mouse to call the begin signal function
            hello.doSomething(2)
        }
    }

    Rectangle{
        objectName: "rect"
        anchors.fill: parent
        color:"red"
        }
    Hello{
       id:hello
       onBegin:console.log("hello")
       onColorChanged:hello.show()
    }
}

QML adds a Rectangle and sets the objectName attribute value to "rect". objectName is used to find the Rectangle in C + +.

B. Attributes of using QML elements in C + +

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>

#include "hello.h"


int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);
  //Register C + + types Hello
  qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

  QQmlApplicationEngine engine;
  QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
  QObject* object = component.create();
  qDebug() << "width value is" << object->property("width").toInt();
  object->setProperty("width", 500);//Set the width of the window
  qDebug() << "width value is" << object->property("width").toInt();
  qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
  QQmlProperty::write(object, "height", 300);//Set the height of window
  qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
  QObject* rect = object->findChild<QObject*>("rect");//Find an element named "rect"
  if(rect)
  {
      rect->setProperty("color", "blue");//Sets the value of the color attribute of the element
      qDebug() << "color is " << object->property("color").toString();
  }

  return app.exec();
}

QObject::property()/setProperty() to read and modify the width property value.

QQmlProperty::read()/write() to read and modify the height property value.

If the type of an object is QQuickItem, such as the return value of QQuickView::rootObject(), you can use QQuickItem::width/setWidth() to access and modify the width property value.

QML components are a complex tree structure, including sibling components and child components. You can use QObject:: findchild() / findchild() to find them.

3. C + + uses signals and functions in QML

In C + +, QMetaObject::invokeMethod() can be used to call functions in QML. The function parameters and return values passed from QML will be converted to QVariant type in C + +. If successful, true will be returned. If the parameters are incorrect or the name of the called function is wrong, false will be returned. invokeMethod() has four overloaded functions. Q must be used_ Arg () macro to declare function parameters, using Q_RETURN_ARG() macro to declare the return value of the function. Its prototype is as follows:

QGenericArgument           Q_ARG(Type, const Type & value)
QGenericReturnArgument     Q_RETURN_ARG(Type, Type & value)

Use QObject::connect() to connect signals in QML. Connect() has four overloaded functions, all of which are static functions. The SIGNAL() macro must be used to declare the signal, and the SLOT() macro declares the slot function.

Use QObject::disconnect() to disconnect the signal from the slot function.

A. Signals and functions defined in QML

import QtQuick 2.5
import QtQuick.Window 2.0
import Hello.module 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: "Hello QML"
    color: "white"
    //Define signal
    signal qmlSignal(string message)
    //Define function
    function qmlFunction(parameter) {
        console.log("qml function parameter is", parameter)
        return "function from qml"
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            hello.begin()//Click the mouse to call the begin signal function
            hello.doSomething(2)
            qmlSignal("This is an qml signal.")//Send signal
        }
    }

    Rectangle{
        objectName: "rect"
        anchors.fill: parent
        color:"red"
        }

    Hello{
       id:hello
       onBegin:console.log("hello")
       onColorChanged:hello.show()
    }
}

The qmlSignal() signal and qmlFunction() function are added to the QML, the signal is sent in QML, and the function is called in C++.

Call in B and C++

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>

#include "hello.h"


int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);
  //Register C + + types Hello
  qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

  QQmlApplicationEngine engine;
  QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
  QObject* object = component.create();
  qDebug() << "width value is" << object->property("width").toInt();
  object->setProperty("width", 500);//Set the width of the window
  qDebug() << "width value is" << object->property("width").toInt();
  qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
  QQmlProperty::write(object, "height", 300);//Set the height of window
  qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
  QObject* rect = object->findChild<QObject*>("rect");//Find an element named "rect"
  if(rect)
  {
      rect->setProperty("color", "blue");//Sets the value of the color attribute of the element
      qDebug() << "color is " << object->property("color").toString();
  }

  //Call functions in QML
  QVariant returnedValue;
  QVariant message = "Hello from C++";
  QMetaObject::invokeMethod(object, "qmlFunction",
                            Q_RETURN_ARG(QVariant, returnedValue),
                            Q_ARG(QVariant, message));

  qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml
  Hello hello;
  //Connect signals in QML elements to C + + slot functions
  QObject::connect(object, SIGNAL(qmlSignal(QString)),
                   &hello, SLOT(qmlSlot(QString)));

  return app.exec();

}

Slot functions in C + + classes:

public slots:
  void doSomething(Color color)
  {
    qDebug() << "Hello::dosomething() is called " << color;
  }

  void qmlSlot(const QString& message)
  {
    qDebug() << "C++ called: " << message;
  }

7, Considerations for mixed programming of QML and C + +

Notes for QML and mixed programming:

A. Custom C + + classes must be derived from QObject class or its subclasses, and use Q_OBJECT macro.

B. It is necessary to register custom C + + classes to Qt meta object system or set custom class object instance as QML context attribute.

C. When the two interact to transfer data, they should comply with the conversion rules of data types between QML and C + +.

Keywords: C++ Qt qml

Added by sabbagh on Sun, 06 Mar 2022 18:55:19 +0200