【问题标题】:Model isn't updated with setContextProperty模型未使用 setContextProperty 更新
【发布时间】:2019-06-15 21:10:45
【问题描述】:

我对 Qt 很陌生,将模型传递给视图时遇到问题。 我的视图有一堆按钮和一个带有一些标记的地图,这些标记的纬度/经度来自我的模型。 点击按钮应该更新地图上的标记(删除一些和/或显示新的)。

问题是:当我的模型(QList)在 C++ 端更新时,QML 端没有。

(我知道这种问题似乎已经被问过了,但是在阅读了不同的答案之后,我无法清楚地了解我是否可以通过更聪明的方式来调用 setContextProperty() 或者如果我必须使用诸如发出信号和绑定属性之类的东西,在阅读了一些文档后我也无法清楚地看到)

架构如下:

  1. 具有 QApplication 实例化和 MainWindow 的主类(MainWindow 是自定义 QMainWindow 类)。应用程序被执行并显示窗口。

  2. 带有 updateMap() 方法的 Mapwidget 类(自定义 QQuickWidget 类):

    • 对用户界面上的按钮点击做出反应
    • 更新模型(QList)
    • 使用 setContextProperty() 方法将更新后的模型传递给 视图
  3. MainWindow 类有一个 Mapwidget 属性

到目前为止我尝试过的事情:

  • 在调用 setSource() 方法之前在 Mapwidget 构造函数中调用 setContextProperty() 时,会考虑模型。所以我用于将模型传递给视图的语法应该是正确的。问题似乎是之后对 setContextProperty() 的任何调用(在这种情况下:在 updateMap() 方法中)都没有传递给 QML 文件。

  • 在不同级别(Mapwidget 类、MainWindow 类)调用 setContextProperty(),结果是一样的,应用首次启动后从不考虑。

  • 我已经测试了模型,并且知道它确实在 updateMap() 方法中得到了更新,只是似乎更新没有传输到 QML 文件中。

QML 文件:

Item {
    width: 1200
    height: 1000
    visible: true

    Plugin {
        id: osmPlugin
        name: "osm"
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: osmPlugin
        center: QtPositioning.coordinate(45.782074, 4.871263)
        zoomLevel: 5

        MapItemView {
            model : myModel
            delegate: MapQuickItem {
                coordinate:QtPositioning.coordinate(
                     model.modelData.lat,model.modelData.lon)
                sourceItem: Image {
                    id:image_1
                    source: <picturePath>
                }
                anchorPoint.x: image_1.width / 2
                anchorPoint.y: image_1.height / 2
            }

        }
}

Mapwidget 类:

mapwidget::mapwidget(QWidget *parent) : QQuickWidget(parent)
{
    this->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
}

void mapwidget::updateMap(QList<QObject *> &data)
{
    /**
     DO OPERATIONS TO UPDATE data 
     Each append has the following form :
     data.append(new DataObject(someLatitude, someLongitude))
    */
    this->rootContext()->setContextProperty("myModel", QVariant::fromValue(data));
}

在 updateMap() 方法中,附加到列表的 QObjects 属于自定义 Class DataObject :

class DataObject : public QObject
{
    Q_OBJECT

    Q_PROPERTY(double lat READ lat WRITE setLat)
    Q_PROPERTY(double lon READ lon WRITE setLon)


public:
    explicit DataObject(QObject *parent = nullptr);
    DataObject(double latitude, double longitude, QObject *parent = 
nullptr);

    void setLat(double latitude);
    void setLon(double longitude);
    double lat() const;
    double lon() const;

    double d_lat;
    double d_lon;
}

为什么即使在调用 setContextProperty() 之后视图也看不到更新的模型?

感谢您的帮助

【问题讨论】:

    标签: c++ qt model qml


    【解决方案1】:

    通过setContextProperty(...) 传递给您的名称是您传递的对象的别名,在model: myModel 绑定的情况下,它是在对象之间进行的,在您传递新对象的情况下由于它们是不同的对象,相同的别名不再有效,因为它们是不同的对象,类似于:

    T *t = new T;
    connect(t, &T::foo_signal, obj, &U::foo_slot);
    t = new T; 
    

    虽然两个对象具有相同的别名 (t),但这并不意味着与第二个对象的连接仍然存在。


    解决方案是使用通知 QML 更新的相同对象,在这种情况下,解决方案是实现自定义 QAbstractListModel:

    CoordinateModel 类

    // coordinatemodel.h
    #ifndef COORDINATEMODEL_H
    #define COORDINATEMODEL_H
    
    #include <QAbstractListModel>
    #include <QGeoCoordinate>
    
    class CoordinateModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
        enum{
            PositionRole = Qt::UserRole + 1000
        };
        explicit CoordinateModel(QObject *parent = nullptr);
    
        void insert(int index, const QGeoCoordinate & coordinate);
        void append(const QGeoCoordinate & coordinate);
        void clear();
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        QHash<int, QByteArray> roleNames() const override;
    
    private:
        QList<QGeoCoordinate> m_coordinates;
    };
    
    #endif // COORDINATEMODEL_H
    
    // coordinatemodel.cpp
    
    #include "coordinatemodel.h"
    
    CoordinateModel::CoordinateModel(QObject *parent)
        : QAbstractListModel(parent)
    {
    }
    
    void CoordinateModel::insert(int index, const QGeoCoordinate &coordinate){
        int i = index;
        if(index < 0) // prepend
            i = 0;
        else if (index >= rowCount()) // append
            i = rowCount();
        beginInsertRows(QModelIndex(), i, i);
        m_coordinates.insert(i, coordinate);
        endInsertRows();
    }
    
    void CoordinateModel::append(const QGeoCoordinate &coordinate){
        insert(rowCount(), coordinate);
    }
    
    void CoordinateModel::clear(){
        beginResetModel();
        m_coordinates.clear();
        endResetModel();
    }
    
    int CoordinateModel::rowCount(const QModelIndex &parent) const{
        if (parent.isValid())
            return 0;
        return m_coordinates.count();
    }
    
    QVariant CoordinateModel::data(const QModelIndex &index, int role) const{
        if (index.row() < 0 || index.row() >= m_coordinates.count())
                return QVariant();
        if (!index.isValid())
            return QVariant();
        const QGeoCoordinate &coordinate = m_coordinates[index.row()];
        if(role == PositionRole)
            return QVariant::fromValue(coordinate);
        return QVariant();
    }
    
    QHash<int, QByteArray> CoordinateModel::roleNames() const{
        QHash<int, QByteArray> roles;
        roles[PositionRole] = "position";
        return roles;
    }
    

    MapWidget 类

    // mapwidget.h
    
    #ifndef MAPWIDGET_H
    #define MAPWIDGET_H
    
    #include <QQuickWidget>
    
    class CoordinateModel;
    
    class MapWidget : public QQuickWidget
    {
    public:
        MapWidget(QWidget *parent=nullptr);
        CoordinateModel *model() const;
    private:
        CoordinateModel *m_model;
    };
    
    #endif // MAPWIDGET_H
    
    // mapwidget.cpp
    
    #include "coordinatemodel.h"
    #include "mapwidget.h"
    
    #include <QQmlContext>
    
    MapWidget::MapWidget(QWidget *parent):
        QQuickWidget(parent),
        m_model(new CoordinateModel{this})
    {
        rootContext()->setContextProperty("myModel", m_model);
        setSource(QUrl(QStringLiteral("qrc:/main.qml")));
    }
    
    CoordinateModel *MapWidget::model() const
    {
        return m_model;
    }
    

    然后您可以将其用作:

    MapWidget w;
    w.model()->append(QGeoCoordinate(45.782074, -6.871263));
    w.model()->append(QGeoCoordinate(50.782074, -1.871263));
    w.model()->append(QGeoCoordinate(55.782074, 4.871263));
    w.model()->append(QGeoCoordinate(45.782074, 4.871263));
    w.model()->append(QGeoCoordinate(50.782074, 4.871263));
    w.model()->append(QGeoCoordinate(55.782074, 4.871263));
    

    ma​​in.qml

    import QtQuick 2.12
    import QtLocation 5.12
    import QtPositioning 5.12
    
    Item {
        width: 1200
        height: 1000
        visible: true
    
        Plugin {
            id: osmPlugin
            name: "osm"
        }
    
        Map {
            id: map
            anchors.fill: parent
            plugin: osmPlugin
            center: QtPositioning.coordinate(45.782074, 4.871263)
            zoomLevel: 5
    
            MapItemView {
                model : myModel
                delegate: MapQuickItem {
                    coordinate: model.position
                    sourceItem: Image {
                        id: image_1
                        source: "http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png"
                    }
                    anchorPoint.x: image_1.width / 2
                    anchorPoint.y: image_1.height / 2
                }
            }
        }
    }
    

    完整的例子是here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多