-1

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.

aszan
  • 15
  • 5
  • 1
    add `dynamicRoles: true` in ListModel – eyllanesc Mar 24 '21 at 12:07
  • Also, double-check that your image resource is indeed properly added to your .qrc file at the path you are using. – David K. Hess Mar 24 '21 at 12:41
  • Maybe the problem that _Only QAbstractItemModel based models are supported_ (from [here](https://doc.qt.io/qt-5/qml-qtlocation-mapitemview.html#model-prop)) – folibis Mar 24 '21 at 12:47
  • @eyllanesc I did it but it's still not working – aszan Mar 24 '21 at 13:43
  • @aszan please provide a [mre] – eyllanesc Mar 24 '21 at 16:32
  • @eyllanesc I added `dynamicRoles: true` in ListModel and tried to run the program. When I clicked the button, the location was added to the ListModel, but the place was not marked on the map. I added code calling function from c ++ file to the post. If there is anything else I should show I would be grateful for the information what it should be because I have no idea what else to present to better illustrate my problem. – aszan Mar 24 '21 at 16:56
  • 1
    A quick note here: `dynamicRoles: true` is not needed here since roles don't change in this case are computed based on the first element. A `ListModel` is a `QAbstractItemModel` so it works with MapView. I would og with David's suggestion, or use another delegate to test it. You could add a console.log in the Component.onCompleted of the delegate to make sure it is created. – GrecKo Mar 24 '21 at 16:57
  • @aszan According to what I see, every time on_pushButton_4_clicked is invoked, you are creating a new map where you add the point but this depends on a "QQmlEngine" that being a local variable then it will be destroyed instantly and therefore will never be observed. Could you show the code where you create the map initially? – eyllanesc Mar 24 '21 at 17:01
  • @eyllanesc For the map display I am using QQuickWidget which has source set to the map.qml file – aszan Mar 24 '21 at 17:19
  • @eyllanesc Interestingly, when I try to call my function like this: `Map { /*Some code like in the post*/ MouseArea { anchors.fill: parent onClicked: { addDevice(30.80, 19.81); } } }` When I run the program with this code fragment added, after clicking on the map everything works and the place is marked on the map. However, when I call the function by clicking on the button, the location is added to the ListModel, but the place is not marked on the map. – aszan Mar 24 '21 at 17:23
  • @folibis i believe that part of the documentation might be outdated. – Pa_ Mar 24 '21 at 22:08
  • @eyllanesc Do you have any idea how can I solve this problem? I have tried to fix it in several ways, but none of it works. I made some changes to the code (I also edited the code presented in the post.) but my QQmlComponent is still different from the one used by QQuickWidget. For this reason, the place locations information is inserted into another ListModel, from which the data is nowhere displayed. What am I doing wrong? I will be really grateful for any advice. – aszan Mar 26 '21 at 10:24

1 Answers1

0

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"
            }
        }
    }
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241