c++ - 模型未使用 setContextProperty 更新
问题描述
我对 Qt 非常陌生,并且在将模型传递给视图时遇到问题。我的视图有一堆按钮和一个带有一些标记的地图,这些标记的纬度/经度来自我的模型。单击按钮应更新地图上的标记(删除一些和/或显示新标记)。
问题是:当我的模型(一个 QList)在 C++ 端更新时,QML 端没有。
(我知道这种问题似乎已经被问过了,但是在阅读了不同的答案之后,我无法清楚地了解我是否可以通过更聪明的方式来调用 setContextProperty() 或者我是否必须使用诸如发出信号和绑定属性之类的东西,在阅读了一些文档后我也无法清楚地看到)
架构如下:
具有 QApplication 实例化和 MainWindow 的主类(MainWindow 是自定义 QMainWindow 类)。应用程序被执行并显示窗口。
具有 updateMap() 方法的 Mapwidget 类(自定义 QQuickWidget 类):
- 对用户界面上的按钮点击做出反应
- 更新模型(QList)
- 使用 setContextProperty() 方法将更新后的模型传递给视图
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(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() 之后视图也看不到更新的模型?
谢谢您的帮助
解决方案
传递给您的名称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
}
}
}
}
完整的例子在这里。
推荐阅读
- java - 如何使用 GenericStoredProcedure 在 Spring Boot 中将 String 设置为输入参数?
- css - 解释这个 CSS 自定义属性行为
- google-cloud-platform - 大查询数据传输 - 如何避免文件限制?
- c++ - RSA_PKCS1 签名验证
- javascript - HTML5 音频 - 播放器仅加载和播放列表中的第一个文件
- html - 如何将内容与html页面的中心对齐
- php - Json编码时如何防止数组位置转换为字符串并强制输出为int
- java - 将 Android Studio 升级到 4.0 后,我得到“原因:java.lang.IllegalStateException:意外的非类文件”
- python - python在行中查找数字字符串
- java - Guice Assisted:没有使用 @com.google.inject.assistedinject.Assisted(value=prefix) 注释的 java.lang.String 的实现被绑定