首页 > 解决方案 > 模型未使用 setContextProperty 更新

问题描述

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

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

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

架构如下:

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

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

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

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

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(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 属于自定义类 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++qtmodelqml

解决方案


传递给您的名称setContextProperty(...)是您传递的对象的别名,如果在对象之间进行绑定,model: myModel则在您传递具有相同别名的新对象时,初始绑定不再有效由于它们是不同的对象,因此类似于:

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

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


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

坐标模型类

// 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));

main.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
            }
        }
    }
}

在此处输入图像描述

完整的例子在这里


推荐阅读