Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QML. How to change MapPolyline path from C++?

Tags:

c++

qt

qt5

qml

Here is a QML map:

Map {
        anchors.fill: parent
        plugin: osmMapPlugin
        center: QtPositioning.coordinate(56.006355, 92.860984)
        zoomLevel: 14

        MapPolyline {
            line.width: 3
            line.color: 'red'
            path: [
                { latitude: -27, longitude: 153.0 },
                { latitude: -27, longitude: 154.1 },
                { latitude: -28, longitude: 153.5 },
                { latitude: -29, longitude: 153.5 }
            ]
        }
    }

How to change path from C++/qt? I tried this:

QML:

Map {
        anchors.fill: parent
        plugin: osmMapPlugin
        center: QtPositioning.coordinate(56.006355, 92.860984)
        zoomLevel: 14

        MapPolyline {
            line.width: 3
            line.color: 'red'

            path: map_path
        }
    }

C++:

map = new QQuickWidget();
map->setSource(QUrl("qrc:map.qml"));

QQmlContext *qml_map = map->rootContext();
QGeoPath *path = new QGeoPath();
path->addCoordinate(*(new QGeoCoordinate(56.0831528053, 92.8405031454)));
path->addCoordinate(*(new QGeoCoordinate(56.1, 93)));
qml_map->setContextProperty("map_path", path);

But I got a exception:

calling a private constructor of class 'QVariant':
    qml_map->setContextProperty("map_path", path);

and

attempted to use of deleted function:
    qml_map->setContextProperty("map_path", path);

UPD: @hi-im-frogatto suggested that it was necessary to do so:

qml_map->setContextProperty("map_path", QVariant::fromValue(path));

It helped, the error no longer occurs. But the line is not drawn. Got error:

qrc:map.qml:30: ReferenceError: map_path is not defined
qrc:map.qml:21:5: QML Map: Plugin is a write-once property, and cannot be set again.
like image 794
garbart Avatar asked Jan 03 '23 23:01

garbart


2 Answers

As it says @HiI'mFrogatto you should use QVariant::fromValue(), but it can not be used directly on the .qml side:

QGeoPath geopath;
geopath.addCoordinate(QGeoCoordinate(56.006355, 92.860984));
geopath.addCoordinate(QGeoCoordinate(56.1, 93));
geopath.addCoordinate(QGeoCoordinate(56.1, 92.777));


QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("geopath", QVariant::fromValue(geopath));

what you have to do is access each element through a loop:

Map {
    anchors.fill: parent
    plugin: osmMapPlugin
    center: QtPositioning.coordinate(56.006355, 92.860984)
    zoomLevel: 10

    MapPolyline {
        id: pl
        line.width: 3
        line.color: 'red'
    }

    Component.onCompleted: {
        var lines = []
        for(var i=0; i < geopath.size(); i++){
            lines[i] = geopath.coordinateAt(i)
        }
        pl.path = lines
    }
}

But with this case we can not update the values of the QGeoPath, it is appropriate to implement a class that inherits from QObject and has as property to QGeoPath, since you can manipulate it from C++ or from QML.

main.cpp

#include <QGeoPath>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>

class PathController: public QObject{
    Q_OBJECT
    Q_PROPERTY(QGeoPath geopath READ geoPath WRITE setGeoPath NOTIFY geopathChanged)
public:
    PathController(QObject *parent=0):QObject(parent){}
    void test(){
        mGeoPath.addCoordinate(QGeoCoordinate(56.006355, 92.860984));
        mGeoPath.addCoordinate(QGeoCoordinate(56.1, 93));
        mGeoPath.addCoordinate(QGeoCoordinate(56.1, 92.777));
        QTimer *timer = new QTimer(this);

        QObject::connect(timer, &QTimer::timeout, [this](){
            mGeoPath.translate(0.001, -0.01);
            emit geopathChanged();
        });
        timer->start(1000);

    }
    QGeoPath geoPath() const{return mGeoPath;}
    void setGeoPath(const QGeoPath &geoPath){
        if(geoPath == mGeoPath)
            return;
        mGeoPath = geoPath;
        emit geopathChanged();
    }

signals:
    void geopathChanged();
private:
    QGeoPath mGeoPath;
};

int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QGuiApplication app(argc, argv);

    PathController controller;
    controller.test();

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("pathController", &controller);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

#include "main.moc"

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2

import QtLocation 5.6
import QtPositioning 5.6

Window {
    visible: true
    width: 640
    height: 480

    Plugin {
        id: osmMapPlugin
        name: "osm"
    }

    Map {
        anchors.fill: parent
        plugin: osmMapPlugin
        center: QtPositioning.coordinate(56.006355, 92.860984)
        zoomLevel: 10

        MapPolyline {
            id: pl
            line.width: 10
            line.color: 'red'
        }
    }

    function loadPath(){
        var lines = []
        for(var i=0; i < pathController.geopath.size(); i++){
            lines[i] = pathController.geopath.coordinateAt(i)
        }
        return lines;
    }
    Connections{
        target: pathController
        onGeopathChanged: pl.path = loadPath()
    }

    Component.onCompleted: pl.path = loadPath()
}

In this link you will find the complete example.

like image 131
eyllanesc Avatar answered Jan 05 '23 14:01

eyllanesc


QQmlContext::setContextProperty accepts a QVariant and there's no any implicit conversion from QGeoPath to QVariant. However using the following method you can do this:

qml_map->setContextProperty("map_path", QVariant::fromValue(path));

More info: http://doc.qt.io/archives/qt-5.5/positioning-cpp-qml.html

like image 42
frogatto Avatar answered Jan 05 '23 12:01

frogatto