qtqmlqvariantqqmlengineqtpositioning

Update a MapCircle on QML using a signal from C++


I'm trying to update a MapCircle in QML from a signal in C++ and I'm veen having several issues with it all day.

In my class I have a Q_PROPERTY which is read only and holds the GPS positions of 4 UAVs in a QVariantList

class GCS: public QObject
{
    Q_PROPERTY(QVariantList getUavPosition READ getUavPosition NOTIFY uavPositionSet)

    public:
      QVariantList getUavPosition() ; 
    signals:
      void uavPositionSet();
     public slots:
       void setUavPosition();
       void triggerPosition();
     private: 
       QVariantList connected_uavs;
       QVector<QGeoCoordinate> uav_positions;         

};

I then define the functions as:

void GCS::setUavPosition()
{
  double i = 0.0;

     QGeoCoordinate uav_id;
     uav_id.setLatitude(0.5);
     uav_id.setLongitude(0.5 + i);
     uav_id.setAltitude(5);
     uav_positions.insert(0, uav_id);
     connected_uavs.append( QVariant::fromValue(QGeoCoordinate(uav_positions[0].latitude(), uav_positions[0].longitude())));

      i+=0.15;  
    emit uavPositionSet();

}

 QVariantList GCS::getUavPosition() 
 {
   return connected_uavs;
 }

void GCS::triggerPosition()
{
  setUavPosition();
  ROS_INFO("Pos trig");
}

In my main function, I connect triggerPosition to a Timer so as to update the position periodically

int main(int argc, char *argv[])
{
     ros::init(argc, argv, "planner");
    QGuiApplication app(argc, argv);

     QQmlApplicationEngine engine;
     QQmlContext* context = engine.rootContext();
     GCS gcs;
     context->setContextProperty("planner", &gcs);    
     engine.load(QUrl(QStringLiteral("qrc:/planner.qml")));

     QTimer *timer = new QTimer();
     timer->setInterval(1000);
     QObject::connect(&gcs, SIGNAL(uavPositionSet()), &gcs, SLOT(setUavPosition()));
     QObject::connect(timer, SIGNAL(timeout()), &gcs, SLOT(triggerPosition()));
     timer->start();

    return app.exec();
}

However, when I run my program, there's a slight delay, my mouseArea becomes unusable and the program crashes. When I try to print the longitude to see if it updates, The initial value is printed out multiple times to the terminal but then the program crashes and there's is no MapCircle present on the map

The relevant part of My Qml file looks like this:

 Map{
        id: map
        anchors.fill:parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(0.5, 0.5)
        zoomLevel:50

        MapCircle{
            id:uavPos
            radius:2
            color:'black'
            border.width:3   
        }

    Connections{
        id:uavConnection
        target: planner
        onUavPositionSet:{

        var data = planner.getUavPosition
        uavPos.center = QtPositioning.coordinate(data[0].latitude, data[0].longitude)
        console.log(data[0].longitude)
        }

        }

    }

Can someone please kindly point me in the right direction here?


Solution

  • If you are going to handle information from several elements then it is better to use a model (together with a Repeater to create several elements), so it is only necessary to modify the role of an item:

    gcs.h

    #ifndef GCS_H
    #define GCS_H
    
    #include <QObject>
    
    class QStandardItemModel;
    class QAbstractItemModel;
    
    class GCS: public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QObject* uavModel READ uavModel CONSTANT)
    public:
        enum UAVRoles {
            PositionRole = Qt::UserRole + 1000
        };
        GCS(QObject *parent=nullptr);
        QObject *uavModel() const;
    public Q_SLOTS:
        void triggerPosition();
    private:
        QStandardItemModel* m_uavModel;
    };
    
    #endif // GCS_H
    

    gcs.cpp

    #include "gcs.h"
    
    #include <QGeoCoordinate>
    #include <QStandardItemModel>
    
    #include <random>
    
    GCS::GCS(QObject *parent):
        QObject(parent), m_uavModel(new QStandardItemModel(this))
    {
        m_uavModel->setItemRoleNames({{PositionRole, "position"}});
        for(int i =0; i < 4; i++){
            QStandardItem *item = new QStandardItem;
            item->setData(QVariant::fromValue(QGeoCoordinate()), PositionRole);
            m_uavModel->appendRow(item);
        }
    }
    
    QObject *GCS::uavModel() const{
        return m_uavModel;
    }
    
    void GCS::triggerPosition(){
        std::mt19937 rng;
        rng.seed(std::random_device()());
        std::normal_distribution<> dist(-0.0001, +0.0001);
    
        if(QStandardItem *item = m_uavModel->item(0)){
            QGeoCoordinate uav_id;
            uav_id.setLatitude(0.5 + dist(rng));
            uav_id.setLongitude(0.5 + dist(rng));
            uav_id.setAltitude(5);
            item->setData(QVariant::fromValue(uav_id), PositionRole);
        }
    }
    

    main.cpp

    #include "gcs.h"
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include <QTimer>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
        GCS gcs;
    
        QQmlApplicationEngine engine;
        QQmlContext* context = engine.rootContext();
        context->setContextProperty("planner", &gcs);
        const QUrl url(QStringLiteral("qrc:/planner.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        QTimer timer;
        timer.setInterval(1000);
        QObject::connect(&timer, &QTimer::timeout, &gcs, &GCS::triggerPosition);
        timer.start();
    
        return app.exec();
    }
    

    planner.qml

    import QtQuick 2.14
    import QtQuick.Window 2.14
    import QtLocation 5.14
    import QtPositioning 5.14
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Plugin {
            id: mapPlugin
            name: "osm"
        }
    
        Map{
            id: map
            anchors.fill:parent
            plugin: mapPlugin
            center: QtPositioning.coordinate(0.5, 0.5)
            zoomLevel:50
    
            MapItemView{
                model: planner.uavModel
                delegate: MapCircle{
                    id:uavPos
                    radius: 2
                    color:'black'
                    border.width:3
                    center: QtPositioning.coordinate(model.position.latitude, model.position.longitude)
                }
            }
        }
    }