📜 ⬆️ ⬇️

Development for Sailfish OS: Creating your own QML component in C ++

Hello! This article is a continuation of a series of articles devoted to the development of the mobile platform Sailfish OS. In this article we will talk about creating your own components in QML in C ++, and specifically about creating properties and methods available in QML, about signals and linking. Also show how to connect a new component to the application.

Motivation


For a start, it is worthwhile to determine the cases in which the use of components in C ++ makes sense. The built-in Qt Quick types certainly offer some tools for working with sensors, with databases and notifications, which we already talked about earlier here and here , with various system capabilities such as network or Bluetooth, D-Bus. However, the capabilities of these tools are rather limited and are not suitable for creating more complex applications. In this case, C ++ just allows you to implement functionality beyond what Qt Quick provides.

Another argument in favor of C ++ is the ability to use a variety of low-level libraries. In addition, complex logic is simply more correct to allocate in classes C ++. That is what we were guided by when creating a component for displaying data on the time axis as a graph.

Component description


The appearance of the component is a coordinate plane on which various graphs are depicted. Moving left and right on the graph is done using the buttons below the component. Data on the plane can be displayed for different time periods: for a week, month, quarter or year. When choosing one of the last three periods, the data is compacted. So, for example, for the month, the graph shows the average values ​​for each day of the range, and for the quarterly and annual range - the average values ​​for every 3 and 12 days, respectively. You can select the desired period using the buttons that are located above the coordinate plane:
')

Component implementation


It is worth noting that the process of creating components does not differ from that on Qt, however, there are still several features specific to Sailfish OS. About them will be discussed below.

First you need to create a C ++ class inherited from QObject or its descendants and containing the Q_OBJECT macro:

#include <QQuickPaintedItem> #include <QObject> class PlotView : public QQuickPaintedItem { Q_OBJECT public: explicit PlotView(QQuickItem* parent = NULL); void paint(QPainter* painter); }; 

Our class is inherited from QQuickPaintedItem to be able to override the QQuickPaintedItem :: paint () method, which will implement the process of drawing graphics using the QPainter interface. To implement visual components without drawing, you can use the QQuickItem class.

Let us show the creation of properties that will be visible in QML, and work with them using the example of the periodIndex property, which is responsible for the index of the current selected period. To declare a property, you must use the Q_PROPERTY macro:

 class PlotView : public QQuickPaintedItem { //... Q_PROPERTY(int periodIndex READ periodIndex WRITE setPeriodIndex NOTIFY periodIndexChanged) private: int periodIndexValue; //... public: void setPeriodIndex(int periodIndex); int periodIndex() const; //... signals: void periodIndexChanged(); }; 

As you can see from the example, for a property, you must specify its name, type, and READ function to get the property value. You can also specify the WRITE function for assigning a property to a new value. Full list of available options can be found here . In addition, inside the class it is best to declare a variable to store the value of the property (which we did in the example).

Also, in order to use the binding mechanism inside QML, using the NOTIFY parameter, the periodIndex property determines the signal that its value changes. The call of the corresponding signal must be placed inside the PlotView :: setPeriodIndex () method:

 void PlotView::setPeriodIndex(int periodIndex) { periodIndexValue = periodIndex; //... emit periodIndexChanged(); //... } 

For all signals declared inside our component, you can create corresponding handlers when declaring a component in QML. So, the signal handler about a change in the index of the current period will look like this:

 PlotView { onPeriodIndexChanged: {} //... } 

To create a method that will be available for calling inside QML, when you declare it, you must add the Q_INVOKABLE macro. Let's declare a method that updates data for graphs and redraws the component:

 class PlotView : public QQuickPaintedItem { //... public: Q_INVOKABLE void drawPlot(); //... }; 

Now we can add a call to this method to the onPeriodIndexChanged () handler .
 PlotView { onPeriodIndexChanged: drawPlot() } 

Thus, when the periodIndex property changes, the coordinate plane and graphs will automatically be updated.

It is worth noting that instead of using the Q_INVOKABLE method, you can declare it as a slot, which also makes it visible inside QML.

The component itself draws, as mentioned above, using the standard classes provided by the Qt library. An example of drawing horizontal lines of the coordinate plane is as follows:

 void PlotView::drawVerticalScaleLines(QPainter* painter) { painter->setPen(QPen(QBrush(Qt::white), 1)); for (float i = minValue; i < maxValue; i += step) { int y = calculatePlotYCoordinate(i); painter->drawLine(0, y, width(), y); } } 

So, to make our component available inside QML, you need to register it inside the application. It is worth noting several features characteristic of Sailfish applications, namely, with their publication in the store Harbor:

  1. The name of your application should begin with harbor and be recorded through "-". For example, harbor-plot-app
  2. It is necessary that the URI on which you register the components starts with the name of the application, in which "-" is replaced with ".". In our case, it will look like harbour.plot.app.plotview

A complete list of name requirements for publishing an application in the store can be found here .

Registration is performed using the qmlRegisterType () method and looks like this:

 #include <QtQuick/QQuickView> #include <QGuiApplication> #include "plotview.h" //... int main(int argc, char *argv[]) { QGuiApplication* app = SailfishApp::application(argc, argv); //... qmlRegisterType<PlotView>("harbour.plotapp.plotview", 1, 0, "PlotView"); //... return app->exec(); } 

Thus, using this component in QML will look something like this:

 import QtQuick 2.0 import Sailfish.Silica 1.0 import harbour.plotapp.plotview 1.0 //... PlotView { id: plotView periodIndex: 0 onPeriodIndexChanged: drawPlot(); //... } //... 

It is important to note that within our components, you can use classes and properties from the Sailfish Silica library, thereby creating components that satisfy the Sailfish OS UI. A good example for this is the Theme class, which provides access to the standard style properties of Sailfish OS, such as color fonts and indents:

 PlotView { //... width: parent.width - Theme.horizontalPageMargin * 2 anchors.horizontalCenter: parent.horizontalCenter //... } 

Conclusion


As a result, the main steps for creating your own components were shown. Similar steps can be used to create non-visual components for your application. More information on this topic can be found here and here . Sources of a small application to demonstrate the operation of our component are available on Bitbucket .

Technical issues can also be discussed on the Sailfish OS Russian-speaking community channel in a Telegram or VKontakte group .

The author: Daria Roychikova

Source: https://habr.com/ru/post/321426/


All Articles