Reputation: 322
I have an QAbstractListModel
object that maintains a list of items to show on a map. The position of these items changes every few seconds and it is easy to calculate a pretty accurate position 60 seconds in the future. What I am trying to do is to set the item's position when it gets a new position (that part works well) and then to immediately move the item toward the calculated future position.
The code without animation looks like this and it works fine:
Component {
id: drawTarget
MapQuickItem {
id: marker
coordinate: data.coords
sourceItem: Item {
id: item
...
The data
object has a property which returns the estimated position of the item 60 seconds in the future, so I tried this:
Component {
id: drawTarget
MapQuickItem {
id: marker
coordinate: data.coords
CoordinateAnimation {
id:anim
property: "coordinate"
}
onCoordinateChanged: {
anim.stop()
anim.from = data.coords
anim.to = data.coordsIn60sec
anim.duration = 60000
anim.start()
}
sourceItem: Item {
id: item
...
But although the object's position is updated properly at each position update, the animation toward the future estimated position doesn't work at all.
How would one go about doing something like this?
Upvotes: 2
Views: 1373
Reputation: 244212
In its code, it makes a binding coordinate: data.coords
that states that "coordinate" takes the value of "coords" but at the same time says that "coordinate" depends on the animation, isn't it contradictory? Well, it is contradictory.
The idea is not to do the binding coordinate: data.coords
but to update the property only through the animation.
The following code is a workable example:
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtLocation 5.6
import QtPositioning 5.6
Window {
visible: true
width: 640
height: 480
Plugin {
id: mapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 10
MapItemView{
model: datamodel
delegate: MapQuickItem{
id: item
// begin configuration
property var position: model.position
property var nextposition: model.nextposition
onPositionChanged: restart();
onNextpositionChanged: restart();
function restart(){
anim.stop()
anim.from = position
anim.to = nextposition
anim.start()
}
CoordinateAnimation {
id: anim
target: item
duration: 60 * 1000
property: "coordinate"
}
// end of configuration
anchorPoint.x: rect.width/2
anchorPoint.y: rect.height/2
sourceItem: Rectangle{
id: rect
color: "green"
width: 10
height: 10
}
}
}
}
}
datamodel.h
#ifndef DATAMODEL_H
#define DATAMODEL_H
#include <QAbstractListModel>
#include <QGeoCoordinate>
#include <QTimer>
#include <random>
#include <QDebug>
struct Data{
QGeoCoordinate position;
QGeoCoordinate nextposition;
};
static QGeoCoordinate osloposition(59.91, 10.75); // Oslo;
class DataModel : public QAbstractListModel
{
Q_OBJECT
QList<Data> m_datas;
public:
enum PositionRoles {
PositionRole = Qt::UserRole + 1,
NextPositionRole
};
explicit DataModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
{
init();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override{
return parent.isValid() ? 0: m_datas.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override{
if (!index.isValid() || index.row() < 0 || index.row() >= m_datas.count())
return QVariant();
const Data &data = m_datas[index.row()];
if (role == PositionRole)
return QVariant::fromValue(data.position);
else if (role == NextPositionRole)
return QVariant::fromValue(data.nextposition);
return QVariant();
}
QHash<int, QByteArray> roleNames() const override{
QHash<int, QByteArray> roles;
roles[PositionRole] = "position";
roles[NextPositionRole] = "nextposition";
return roles;
}
private:
void init(){
for(int i=0; i< 10; ++i){
Data data;
data.position = osloposition;;
data.nextposition = data.position;
m_datas << data;
}
QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &DataModel::updateData);
timer->start(60 * 1000);
updateData();
}
void updateData(){
qDebug() << __PRETTY_FUNCTION__;
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(-.1, .1);
for(int i=0; i < m_datas.count(); ++i){
Data & data = m_datas[i];
QModelIndex ix = index(i);
data.position = data.nextposition;
data.nextposition = QGeoCoordinate(osloposition.latitude() + dis(e),
osloposition.longitude() + dis(e));
Q_EMIT dataChanged(ix, ix, {PositionRole, NextPositionRole});
}
}
};
#endif // DATAMODEL_H
In the following link is the complete example
Upvotes: 2