In this article, I describe how to use QML to call C++ to extend our application. We know that QML is powerful and UI savvy in many scenarios, but there are times when we might want to apply our C++ code. There are several reasons for this:

1) we already have mature C++ engine design or protocol. For example, we have designed our Fetion protocol code in C++. We don’t have to rewrite one side in another low-performing language

2) Some functions cannot be completed using Javascript, we must use the system or some special function packs to complete. At this point we can use the C++ language to accomplish the required functionality

3) some code is very computationally demanding and requires a lot of CPU time. In this case, we can consider using C++ to complete this part of the work

\

In fact, on Ubuntu OS, we can use QML/Javascript to create cool UI, and we can also use C++ for some of our unique features (such as computations). The union between the two is very natural and intimate. You can imagine C++ being used to extend the capabilities of QML.

\

Here, let’s create an application that reads some Linux environment variables. Through this exercise, we can learn how to call C++ code in QML files.

1) Create a basic application

  • Start the Ubuntu IDE and select the “App with QML Extension Library” template application

\

\

\

  • Set the Settings for the application

\

\

  • Select the architecture you want to run. Here we choose “Desktop” and the “ARMHF” architecture that works on mobile phones

\

\

At this point, we’ve basically created an application that runs on both phones and computers. Select the appropriate architecture and click the Run key in the lower left corner of the IDE

\

\

\

If you still have any problems with running here, please refer to my previous article to install your environment correctly.

\

2) add C++ functional modules

\

First of all, we find the project subdirectories “ReadEnv/backend/modules/ReadEnv”, here we create two files “ReadEnv. CPP” and “ReadEnv. H”. They read as follows:

\

\

#ifndef READ_ENV_H
#define READ_ENV_H

#include <QObject>

class ReadEnv : public QObject
{
    Q_OBJECT
public:
    explicit ReadEnv(QObject *parent = 0);
    Q_INVOKABLE QString getenv(const QString envVarName) const;

private:};#endif // READ_ENV_H
Copy the code

\

#include "readenv.h"
#include <QDebug>

ReadEnv::ReadEnv(QObject *parent) : QObject(parent)
{
}

QString ReadEnv::getenv(const QString envVarName) const
{
    QByteArray result = qgetenv(envVarName.toStdString().c_str());
    QString output = QString::fromLocal8Bit(result);
    qDebug() << envVarName << " value is: "  << output;
    return output;
}
Copy the code

This is a very simple C++ class. This class must be inherited from the QObject class so that it can use Qt’s signal-slot mechanism. If you’re not familiar with this, there are some resources. For such a class, its functions or methods must be defined as “Q_INVOKABLE” if they can be called by QML. In addition, all functions or methods defined as “slot” can also be called directly by JS in QML. We can illustrate this problem with more examples. The getenv method is simple. It directly reads the value of the required environment variable and returns it through a transformation.

\

We also open another file “backend. CPP “in this directory. The modified file is displayed as follows:

\

#include <QtQml>
#include <QtQml/QQmlContext>
#include "backend.h"
#include "mytype.h"
#include "readenv.h"


void BackendPlugin::registerTypes(const char *uri)
{
    Q_ASSERT(uri == QLatin1String("ReadEnv"));

    qmlRegisterType<MyType>(uri, 1.0."MyType");
    qmlRegisterType<ReadEnv>(uri, 1.0."ReadEnv");
}

void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
    QQmlExtensionPlugin::initializeEngine(engine, uri);
}
Copy the code

There’s really nothing going on here. He added a sentence

\

 qmlRegisterType<ReadEnv>(uri, 1, 0, "ReadEnv");
Copy the code

\

The first “ReadEnv” here corresponds to the name of the class we defined, not the name of the project. The second “ReadEnv” is actually used in QML to instantiate one of these classes. The name can be different from the actual class, or it can be anything you like. However, you must also make the corresponding changes in the QML file. With this registration, the class can be instantiated in QML and referenced! Once we have the source code ready, we must add it to our project for compilation. Open the “cmakelists. TXT” file in the “backend” directory and add the following statement:

\

set(
Copy the code
    modules/ReadEnv/mytype.cpp
Copy the code
    modules/ReadEnv/readenv.cpp
Copy the code
)
Copy the code
Copy the code

We can update the file directories in the project by closing the project and re-opening the application. You can also use the right click to make the newly added file correctly displayed:

At this point, our C++ portion of the code is almost complete.

3) Modify the QML part of the code

Now that we’ve done the basic C++ part of the code, let’s move on to the QML part of the interface. Let’s get familiar with QML layout. We know layout is a handy way to manage our controls and automatically adapt to the right screen size and orientation changes at the right time. QML has columns and rows to help us manage our controls:

        Row {
Copy the code
            anchors.horizontalCenter: parent.horizontalCenter
Copy the code
            spacing: 10
Copy the code
Copy the code
            Label {
Copy the code
                text: "Value:  "
Copy the code
                fontSize: "large"
Copy the code
            }
Copy the code
Copy the code
            TextArea {
Copy the code
                id:value
Copy the code
                readOnly: true
Copy the code
                contentWidth: units.gu(40)
Copy the code
                contentHeight: units.gu(80)
Copy the code
            }
Copy the code
Copy the code
        }
Copy the code

\

\

Here we have two controls, a Label and a” TextArea”. By this combination, the whole thing can be thought of as a composite control, with two controls inside. The whole thing can be placed in the layout manager (Row, Grid, Flow, Column, etc.) outside of it. Through my modification, we created the following interface and interface.

\

\

\

\

We implemented the interface by modifying “readenv.qml”. The specific code is as follows:

\

The import QtQuick 2.0Copy the code
Ponents import Ubuntu.Com 0.1Copy the code
The import ReadEnv 1.0Copy the code
import "ui"
Copy the code
Copy the code
MainView {
Copy the code
    // objectName for functional testing purposes (autopilot-qt5)
Copy the code
    objectName: "mainView"
Copy the code
Copy the code
    // Note! applicationName needs to match the "name" field of the click manifest
Copy the code
    applicationName: "com.ubuntu.developer.liu-xiao-guo.ReadEnv"
Copy the code
Copy the code
    anchorToKeyboard: true
Copy the code
Copy the code
/ *Copy the code
     This property enables the application to change orientation
Copy the code
     when the device is rotated. The default is false.
Copy the code
* /Copy the code
    //automaticOrientation: true
Copy the code
Copy the code
    width: units.gu(100)
Copy the code
    height: units.gu(75)
Copy the code
Copy the code
    ReadEnv {
Copy the code
        id: readEnv
Copy the code
    }
Copy the code
Copy the code
    Column {
Copy the code
        // anchors.fill: parent
Copy the code
        //        anchors.verticalCenter: parent.verticalCenter
Copy the code
        //        anchors.horizontalCenter: parent.horizontalCenter
Copy the code
        anchors.centerIn: parent
Copy the code
        spacing: 20
Copy the code
Copy the code
        Row {
Copy the code
            anchors.horizontalCenter: parent.horizontalCenter
Copy the code
            spacing: 10
Copy the code
            Label {
Copy the code
                text: "Variable:"
Copy the code
                fontSize: "large"
Copy the code
            }
Copy the code
Copy the code
            TextField {
Copy the code
                id:input
Copy the code
                text:"PWD"
Copy the code
                focus: true
Copy the code
Copy the code
                placeholderText: "Please input a env variable"
Copy the code
Copy the code
                Keys.onPressed: {
Copy the code
                    if (event.key === Qt.Key_Return) {
Copy the code
print("Enter is pressed!" )Copy the code
                        value.text = readEnv.getenv(input.text)
Copy the code
                    }
Copy the code
                }
Copy the code
            }
Copy the code
        }
Copy the code
Copy the code
        Row {
Copy the code
            anchors.horizontalCenter: parent.horizontalCenter
Copy the code
            spacing: 10
Copy the code
Copy the code
            Label {
Copy the code
                text: "Value:  "
Copy the code
                fontSize: "large"
Copy the code
            }
Copy the code
Copy the code
            TextArea {
Copy the code
                id:value
Copy the code
                readOnly: true
Copy the code
                contentWidth: units.gu(40)
Copy the code
                contentHeight: units.gu(80)
Copy the code
            }
Copy the code
Copy the code
        }
Copy the code
Copy the code
        Button {
Copy the code
            anchors.horizontalCenter: parent.horizontalCenter
Copy the code
            text: "Get"
Copy the code
            onClicked: {
Copy the code
print("Button is clicked!" )Copy the code
                value.text = readEnv.getenv(input.text)
Copy the code
            }
Copy the code
        }
Copy the code
    }
Copy the code
}
Copy the code
Copy the code
Copy the code

\

Special attention should be paid to:

  • When calling the library in Backend, we must use the following actions.
The import ReadEnv 1.0Copy the code

The “ReadEnv” here actually corresponds to the” ReadEnv” in the following statement in “backend. CPP”. If that changes, the import statement also changes

\

    Q_ASSERT(uri == QLatin1String("ReadEnv"));
Copy the code

We can see that in the installed environment of the phone as well.” ReadEnv” is also a directory name under “lib”. Actually, this understanding is very important. All the text in the import is actually representing the path. It marks the location of the library

\

\

\

\

  • We instantiate the “ReadEnv” class with the following statement. When we instantiate, we also give an ID so that we can reference it elsewhere. In this application, we call it in Button
      ReadEnv {
Copy the code
          id: readEnv
Copy the code
      }
Copy the code
Copy the code
        Button {
Copy the code
            anchors.horizontalCenter: parent.horizontalCenter
Copy the code
            text: "Get"
Copy the code
            onClicked: {
Copy the code
print("Button is clicked!" )Copy the code
                value.text = readEnv.getenv(input.text)
Copy the code
            }
Copy the code
        }
Copy the code

\

\

The application runs as follows:

\

\

Ok, so we’ve done an exercise today. It implements calling C++ code from QML. We can add more functionality to the application later. The code for this routine can be found at:

\

bzr branch  lp:~liu-xiao-guo/debiantrial/readenv\

\