c++qtqmlqt5.5qtlocation

Adding new places to the QML map is not working


I am currently creating a program that can mark places on the map.

map.qml:

import QtQuick 2.10
import QtQuick.Controls 2.3
import QtLocation 5.6
import QtPositioning 5.6

Rectangle {
    ListModel {
        id: locationModel
    }
    Plugin {
        id: mapPlugin
        name: "esri"
    }

    Map {
        id: place
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(51.5, 0.1)
        zoomLevel: 7
        MapItemView
        {
            model: locationModel
            delegate: mapcomponent
        }
    }
    Component {
        id: mapcomponent
        MapQuickItem {
            id: marker
            anchorPoint.x: image.width/2
            anchorPoint.y: image.height/2
            coordinate: QtPositioning.coordinate(lat, lon)
            sourceItem: Image {
                id: image
                width: 100
                height: 50
                source: "qrc:/rec/marker.png"
            }
        }
    }
    function addPlace(lat: double, lon: double){
            locationModel.append({"lat": lat, "lon": lon})
            console.log("Done")
    }
}

mainwindow.cpp:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->quickWidget->setSource(QUrl("qrc:/rec/map.qml"));
    QQmlComponent component(ui->quickWidget->engine(), ui->quickWidget->source());
    object = component.create();
}

/*Some not interesting code.*/

void MainWindow::on_pushButton_4_clicked()
{
    double lat = 50.00;
    double lon = 20.00;
    QMetaObject::invokeMethod(object, "addDevice",
        Q_ARG(double, lat),
        Q_ARG(double, lon));
}

object is defined in mainwindow.h like this: QObject *object;. The add place function is called from a c ++ file when a certain button is clicked. Unfortunately, for some reason, this function does not work properly. After invoking it, the message "Done" appears in the console and the location of the point to be marked on the map is added to the ListModel. Unfortunately, the marker does not appear on the map. What am I doing wrong? I will be grateful for any advice.


Solution

  • The problem is that you are creating a new component with a new map and you are adding the markers to that new invisible map.

    Instead of exposing QML objects to C++ it is better to do the opposite:

    #ifndef PLACEHELPER_H
    #define PLACEHELPER_H
    
    #include <QObject>
    
    class PlaceHelper : public QObject
    {
        Q_OBJECT
    public:
        explicit PlaceHelper(QObject *parent = nullptr);
        void addPlace(double latitude, double longitude);
    Q_SIGNALS:
        void add(double latitude, double longitude);
    };
    
    #endif // PLACEHELPER_H
    
    #include "placehelper.h"
    
    PlaceHelper::PlaceHelper(QObject *parent) : QObject(parent)
    {
    
    }
    
    void PlaceHelper::addPlace(double latitude, double longitude)
    {
        Q_EMIT add(latitude, longitude);
    }
    

    *.h

    // ..
    private:
        Ui::MainWindow *ui;
        PlaceHelper placeHelper;
    };
    

    *.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QQmlContext>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->quickWidget->rootContext()->setContextProperty("placeHelper", &placeHelper);
        ui->quickWidget->setSource(QUrl("qrc:/rec/map.qml"));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_4_clicked()
    {
        double lat = 50.00;
        double lon = 0.10;
        placeHelper.addPlace(lat, lon);
    }
    
    import QtQuick 2.10
    import QtQuick.Controls 2.3
    import QtLocation 5.6
    import QtPositioning 5.6
    
    Rectangle {
        ListModel {
            id: locationModel
        }
        Plugin {
            id: mapPlugin
            name: "esri"
        }
    
        Map {
            id: place
            anchors.fill: parent
            plugin: mapPlugin
            center: QtPositioning.coordinate(51.5, 0.1)
            zoomLevel: 7
            MapItemView
            {
                model: locationModel
                delegate: mapcomponent
            }
        }
        Component {
            id: mapcomponent
            MapQuickItem {
                id: marker
                anchorPoint.x: image.width/2
                anchorPoint.y: image.height/2
                coordinate: QtPositioning.coordinate(lat, lon)
                sourceItem: Image {
                    id: image
                    width: 100
                    height: 50
                    source: "qrc:/rec/marker.png"
                }
            }
        }
        Connections{
            target: placeHelper
            function onAdd(latitude, longitude){
                locationModel.append({"lat": latitude, "lon": longitude})
            }
        }
    }
    

    Another solution is for the model to be implemented in C ++ and exported to QML:

    #ifndef PLACEMODEL_H
    #define PLACEMODEL_H
    
    #include <QStandardItemModel>
    
    
    class PlaceModel: public QStandardItemModel
    {
        Q_OBJECT
    public:
        enum PlaceRoles {
            LatitudeRole = Qt::UserRole + 1,
            LongitudeRole
        };
        PlaceModel(QObject *parent=nullptr);
        Q_INVOKABLE void addPlace(double longitude, double latitude);
    };
    
    #endif // PLACEMODEL_H
    
    #include "placemodel.h"
    
    PlaceModel::PlaceModel(QObject *parent):
        QStandardItemModel(parent)
    {
        setItemRoleNames({{LatitudeRole, "lat"},
                          {LongitudeRole, "lon"}});
    }
    
    void PlaceModel::addPlace(double latitude, double longitude)
    {
        QStandardItem *item = new QStandardItem;
        item->setData(latitude, LatitudeRole);
        item->setData(longitude, LongitudeRole);
        appendRow(item);
    }
    

    *.h

    private:
        Ui::MainWindow *ui;
        PlaceModel placeModel;
    

    *.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QQmlContext>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->quickWidget->rootContext()->setContextProperty("placeModel", &placeModel);
        ui->quickWidget->setSource(QUrl("qrc:/rec/map.qml"));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_4_clicked()
    {
        double lat = 50.00;
        double lon = 0.10;
        placeModel.addPlace(lat, lon);
    }
    
    import QtQuick 2.10
    import QtQuick.Controls 2.3
    import QtLocation 5.6
    import QtPositioning 5.6
    
    Rectangle {
        Plugin {
            id: mapPlugin
            name: "esri"
        }
    
        Map {
            id: place
            anchors.fill: parent
            plugin: mapPlugin
            center: QtPositioning.coordinate(51.5, 0.1)
            zoomLevel: 7
            MapItemView
            {
                id: mapView
                model: placeModel
                delegate: mapcomponent
            }
        }
        Component {
            id: mapcomponent
            MapQuickItem {
                id: marker
                anchorPoint.x: image.width/2
                anchorPoint.y: image.height/2
                coordinate: QtPositioning.coordinate(lat, lon)
                sourceItem: Image {
                    id: image
                    width: 100
                    height: 50
                    source: "qrc:/rec/marker.png"
                }
            }
        }
    }