qt - MapItemView 在 dataChanged 信号后不更新

标签 qt geolocation qml qtquick2

我将 QML MapItemView 组件与基于 C++ QAbstractListModel 的模型一起使用。 MapItemView 在重置模型、添加新项目或删除现有项目时工作正常。但是,MapItemView 并未反射(reflect)对已添加项目的更改。

我第一次遇到这个问题是在 Qt 5.4 上,但在更新到 Qt 5.5 后我仍然遇到这个问题

以下示例显示了 2 个不同模型的问题:一个基于 QAbstractListModel 的 C++ 模型和一个 QML ListModel。 可以从一个模型切换到另一个模型,按下右上角的按钮:

  • 当使用QML模型时,点击 map 将添加一个新元素并修改第一个元素。
  • C++ 模型使用 QTimer 每秒修改其内容。

无论模型类型是什么,MapItemView 都不会显示模型更改。从一种模型切换到另一种模型时,可以看到 MapView 已更新。

我可能遗漏了一些非常明显的东西,但我不明白它是什么。预先感谢您的帮助。

ma​​in.cpp 代码:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "playermodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    PlayerModel playerModel;
    engine.rootContext()->setContextProperty("playerModel", &playerModel);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

C++ 模型头文件 (playermodel.h):

#ifndef PLAYERMODEL_H
#define PLAYERMODEL_H


#include <QObject>
#include <QAbstractListModel>
#include <QGeoPositionInfoSource>
#include <QTimer>
#include <QDebug>

struct PlayerData
{
    PlayerData(){    }
    PlayerData(int _Azimuth, double lat, double lng){
        Azimuth = _Azimuth;
        Latitude = lat;
        Longitude = lng;
    }

    int Azimuth = -1;
    double Latitude = 0.;
    double Longitude = 0.;

    QVariant getRole(int role) const;

    enum Roles{
        RoleAzimuth = Qt::UserRole + 1,
        RoleLatitude,
        RoleLongitude

    };

};

class PlayerModel : public QAbstractListModel
{
    Q_OBJECT
public:
    PlayerModel();

    ~PlayerModel();

    int rowCount(const QModelIndex & parent = QModelIndex()) const;
    QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const;
    Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;    

    virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);

    bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());

    void resetModel();
    void updateModel();

public slots:
    void testUpdateModel();

protected:
    QHash<int, QByteArray> roleNames() const;

private:
    QTimer m_timer;
    QVector< PlayerData> m_lstValues ;
};


#endif // PLAYERMODEL_H

C++ 模型 (playermodel.cpp)

#include "playermodel.h"

QVariant PlayerData::getRole(int role) const
{
    switch (role)
    {
        case Roles::RoleAzimuth:
            return Azimuth;
        case Roles::RoleLatitude:
            return Latitude;
        case Roles::RoleLongitude:
            return Longitude;

    default:
        return QVariant();
    }
}

PlayerModel::PlayerModel()
{

    resetModel();
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(testUpdateModel()));
    m_timer.start(1000);

}

PlayerModel::~PlayerModel()
{

}

void PlayerModel::testUpdateModel()
{
    updateModel();

}

int PlayerModel::rowCount(const QModelIndex & parent) const
{
    Q_UNUSED(parent);
    return m_lstValues.size();
}

QVariant PlayerModel::data( const QModelIndex & index, int role ) const
{
    if ( (index.row() < 0) || (index.row() >= rowCount()) )
        return QVariant();

    return m_lstValues[ index.row()].getRole( role);

}

void PlayerModel::resetModel()
{
    qDebug() << "Reset players model";

    beginResetModel();

    m_lstValues.clear();
    //populate with dummy value

    m_lstValues.push_back( PlayerData( 10,  47.1, -1.6 ));
    m_lstValues.push_back( PlayerData( 20,  47.2, -1.6 ));
    m_lstValues.push_back( PlayerData( 30,  47.1, -1.5 ));
    m_lstValues.push_back( PlayerData( 40,  47.2, -1.5 ));

    endResetModel();

}


void PlayerModel::updateModel()
{
    qDebug() << "update players model upon timeout";

    //change the Azimuth of every model items
    int row = 0;
    for (PlayerData player : m_lstValues)
    {
        setData( index(row), (player.Azimuth + 1) % 360, PlayerData::RoleAzimuth);
        row++;
    }

    //qDebug() << "First element azimuth is now : " << data( index(0),PlayerData::RoleAzimuth).toInt() << "°";
}

bool PlayerModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
    if ( (index.row() < 0) || (index.row() >= rowCount()) ) return false;

    PlayerData& player = m_lstValues[ index.row() ];
    switch (role)
    {

        case PlayerData::RoleAzimuth:
            player.Azimuth = value.toInt();
            break;
        case PlayerData::RoleLatitude:
            player.Latitude = value.toDouble();
            break;
        case PlayerData::RoleLongitude:
            player.Longitude = value.toDouble();
            break;

    }
    emit dataChanged(index, index );//, QVector<int>( role));

    return true;
}

bool PlayerModel::removeRows(int row, int count, const QModelIndex & parent)
{
    Q_UNUSED(count);
    Q_UNUSED(parent);
    beginRemoveRows(QModelIndex(), row, row);
    m_lstValues.remove( row);
    endRemoveRows();
    return true;
}

QHash<int, QByteArray> PlayerModel::roleNames() const
{
    QHash<int, QByteArray> roles;

    roles[PlayerData::Roles::RoleAzimuth] = "Azimuth";
    roles[PlayerData::Roles::RoleLatitude] = "Latitude";
    roles[PlayerData::Roles::RoleLongitude] = "Longitude";

    return roles;

}

Qt::ItemFlags PlayerModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
            return 0;

    return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}

最后是 QML 文件:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtLocation 5.3
import QtPositioning 5.0

Window {
    id:mainWnd
    visible: true
    width : 1024
    height:768
    property bool  useQMLModel: true
    Map {
       id: map
       anchors.fill: parent
       anchors.margins: 50
       plugin: Plugin{ name:"osm";}
       center: QtPositioning.coordinate(47.1, -1.6)
       zoomLevel: map.maximumZoomLevel

       MapItemView{
           id:mapItemView
           model: mainWnd.useQMLModel ? qmlModel : playerModel

           delegate: MapQuickItem {
              //anchorPoint:
              id:delegateMQI
              rotation: model.Azimuth
              sourceItem: Rectangle{
                  id:defaultDelegate
                  width:32
                  height:32
                  radius:16
                  opacity: 0.6
                  rotation:Azimuth
                  color:"blue"

                  Text{
                      text: Azimuth
                      anchors.centerIn : parent
                  }

              }
              coordinate: QtPositioning.coordinate(Latitude,Longitude)
          }

       }
       MouseArea{
           anchors.fill: parent
           enabled : useQMLModel
           //preventStealing: true
           propagateComposedEvents: true
           onClicked:
           {
               //Modify an item
               var newAzim = Math.random()*360;
               qmlModel.setProperty(0, "Azimuth", newAzim);
               //Check modification
               console.log("Azim:" + qmlModel.get(0).Azimuth );
               qmlModel.setProperty(0, "Color", "blue");


               //add a new item
               qmlModel.append({"Latitude": 47.05 + Math.random() *0.2, "Longitude":-1.75 + Math.random() *0.3, "Azimuth":0, "Color":"red"})
               console.log("Nb item:" + qmlModel.count );


               map.update();
               map.fitViewportToMapItems();

               mouse.accepted = false

           }
       }
    }


    Connections{
        target:mapItemView.model
        onDataChanged:{
            if (useQMLModel)
                console.log("dataChanged signal Azim:" + qmlModel.get(0).Azimuth );
            else
                console.log("dataChanged signal Azim:" + playerModel.data( topLeft, 0x0101) );
        }
    }

    ListModel{
        id:qmlModel
        ListElement {
            Latitude: 47.1
            Longitude: -1.6
            Azimuth: 10.0
        }

    }

    Rectangle{
        anchors.top : parent.top
        anchors.left : parent.left
        width : 400
        height : 300
        radius: 10
        color:"grey"
        ListView{
            id:lstView
            model:mapItemView.model
            anchors.fill:parent
            delegate: Text{
                width:parent.width
                height:50
                verticalAlignment: TextInput.AlignVCenter
                fontSizeMode : Text.Fit
                font.pixelSize: 42
                minimumPixelSize: 5
                text: "Latitude : " + Latitude + " - Longitude :" + Longitude + " - Azimuth : " + Azimuth
            }
        }
    }




    Rectangle{
        anchors.right : parent.right
        anchors.top : parent.top
        radius : 10
        color : "red"
        width : 200
        height : 50
        Text{
            anchors.centerIn: parent
            text:"switch model"
        }
        MouseArea{
            anchors.fill: parent
            onClicked:{
                mainWnd.useQMLModel = !mainWnd.useQMLModel;
            }
        }
    }

}

最佳答案

以防万一有人面临同样的问题issue经博主反馈,问题已在Qt 5.6.0中解决

Note that this is fixed by changeset Ib92252d18c2229bc6d43e11362b8f13cdb48f315 (https://codereview.qt-project.org/#/c/123660/ ) already merged in the 5.6 branch

关于qt - MapItemView 在 dataChanged 信号后不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31536014/

相关文章:

android - Qt 和 Android characteristicChanged 未发出

qt - QPolygonF/QLineF 相交

html - Windows Phone HTML 应用程序中的地理位置无法正常工作

c++ - 将新项目添加到基于 QAbstractListModel 的模型时,QML View 不会更新

qt - 动态创建QML ListElement和内容

c++ - 使用 Qt5 检测 Windows 10 版本

python - 从 python 列表中删除邮政编码(以从 MapQuest 输出获取州名称)

ios - 如果我的应用程序在其各种 View Controller 中使用位置管理器,我应该在哪里实例化它?

qt - ListElement字段作为属性?

c++ - 可变参数模板迭代和 GCC