Модель не обновляется с помощью setContextProperty - PullRequest
2 голосов
/ 16 июня 2019

Я очень новичок в Qt и у меня есть проблемы с передачей моей модели в мой вид. В моем представлении есть несколько кнопок и карта с некоторыми маркерами, широта / долгота которых исходит от моей модели. Нажатие на кнопки должно обновить маркеры на карте (удалить некоторые и / или отобразить новые).

Проблема в том, что когда моя модель (QList) обновляется на стороне C ++, сторона QML этого не делает.

(Я знаю, что такого рода вопросы, кажется, уже задавались, но после прочтения разных ответов, я не могу получить четкое представление о том, смогу ли я избежать более разумного способа вызова setContextProperty () или если я приходится использовать такие вещи, как испускать сигналы и свойства привязки, о которых я также не могу понять, прочитав небольшую документацию)

Архитектура следующая:

  1. Основной класс с экземпляром QApplication и MainWindow (MainWindow - пользовательский класс QMainWindow). Приложение исполняется, и окно отображается.

  2. Класс Mapwidget (пользовательский класс QQuickWidget) с методом updateMap (), который:

    • Реагирует на нажатия кнопок в пользовательском интерфейсе
    • Обновляет модель (QList)
    • Использует метод setContextProperty () для передачи обновленной модели в Взгляд
  3. Класс MainWindow имеет атрибут Mapwidget

То, что я до сих пор пробовал:

  • При выполнении вызова setContextProperty () в конструкторе Mapwidget перед вызовом метода setSource () модель учитывается. Поэтому синтаксис, который я использую для передачи модели в представление, должен быть правильным. Кажется, проблема в том, что любой вызов setContextProperty () впоследствии (в данном случае: в методе updateMap ()) не передается в файл QML.

  • При вызове setContextProperty () на разных уровнях (класс Mapwidget, класс MainWindow) результаты совпадают, это никогда не учитывается после первого запуска приложения.

  • Я протестировал модель и точно знаю, что она обновляется внутри метода 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 () объекты QObject, добавленные в список, имеют пользовательский класс 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;
}

Почему View не может видеть обновленную модель даже после вызова setContextProperty ()?

Спасибо за вашу помощь

1 Ответ

0 голосов
/ 16 июня 2019

Имя, переданное вам через 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));

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

enter image description here

Полный пример здесь .

...