MapItemView 在 dataChanged 信号后未更新

Posted

技术标签:

【中文标题】MapItemView 在 dataChanged 信号后未更新【英文标题】:MapItemView is not updated after a dataChanged signal 【发布时间】:2015-07-21 10:04:02 【问题描述】:

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

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

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

使用 QML 模型时,在地图中单击会添加一个新元素并修改第一个元素。 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;
            
        
    


【问题讨论】:

我们也需要为您的模型提供完整的代码。理想情况下,您应该将所有 C++ 代码放在一个 main.cpp 文件中,并将其添加到此问题中。该代码应包含模型的完整代码,以及用于设置 QML、模型和运行应用程序的 main() 感谢您的回复。实际上,我想用尽可能少的代码来展示这个问题,这就是我使用 QML 模型而不是我的 C++ 模型的原因,因为我对 QML 模型有同样的问题:我们可以看到模型在按下时发生了变化鼠标(也可以使用 Connection 元素来演示已发送 dataChanged 信号)。无论如何,我在新评论中添加了使用 C++ 模型的完整代码。 我现在用一个完整的例子来编辑我的问题。 我已经在错误报告中报告了这个问题:bugreports.qt.io/browse/QTBUG-47366 【参考方案1】:

以防万一有人面对帖子作者报告的相同issue,问题在Qt 5.6.0中得到解决

请注意,这已由已合并到 5.6 分支中的变更集 Ib92252d18c2229bc6d43e11362b8f13cdb48f315 (https://codereview.qt-project.org/#/c/123660/) 修复

【讨论】:

以上是关于MapItemView 在 dataChanged 信号后未更新的主要内容,如果未能解决你的问题,请参考以下文章

我如何调用 dataChanged

PyQT 列表视图没有响应 datachanged 信号

发出 dataChanged 信号 PyQt5

在应用程序未运行时检测 dataChange

QAbstractItemModel:为啥在向模型中插入大量项目时发出 dataChanged 和插入行信号这么慢?

无法分配给对象“#<ValidationComponent>”的只读属性“dataChange”