Я использую компонент QML MapItemView
с моделью на C++ QAbstractListModel
. MapItemView
работает нормально при сбросе модели или при добавлении нового элемента или удалении существующего элемента. Однако MapItemView
не отражает изменения уже добавленных элементов.MapItemView не обновляется после сигнала dataChanged
я впервые испытал этот вопрос с Qt 5.4, но я до сих пор перед этим после обновления до Qt 5.5
Следующий пример показывает проблему с 2-х различных моделей: модель C++ на основе QAbstractListModel
и QML ListModel
. можно переходить от одной модели к другой, нажав на кнопку сверху справа:
- Когда модель QML используется, щелкнув на карте будет добавлен новый элемент и изменить первый элемент.
- Модель C++ использует
QTimer
, чтобы изменить его содержание каждые несколько секунд.
MapItemView
не показывает изменения модели, независимо от типа модели. При переключении с одной модели на другую можно увидеть, что обновляется MapView
.
У меня, вероятно, отсутствует что-то очень очевидное, но я не вижу, что это такое. Спасибо заранее за вашу помощь.
Код main.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();
}
модель заголовка С ++ (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' и добавить его к этому вопросу. Этот код должен содержать весь код модели, а также 'main()', который устанавливает QML, модель и запускает приложение. –
Благодарим вас за ответ. На самом деле, я хотел показать проблему с меньшим количеством кода, поэтому я использовал модель QML вместо моей модели C++, поскольку у меня такая же проблема с моделью QML: мы можем видеть, что модель изменяется, когда нажатие мыши (можно также использовать элемент «Соединение» для демонстрации передаваемых сигналов DataChanged). Во всяком случае, я добавляю полный код с использованием модели C++ в новый комментарий. –
Теперь я редактирую свой вопрос с полным примером. –