Reputation: 697
I have a QML Repeater on a Grid, when I click on an item, I emit a signal which is processed by a C++ class. Then, I change an array in C++ which is then assigned as a model of the QML Repeater.
Is there a way to change just two elements of the C++ model, not the whole model as I did?
QML code:
Grid{
height:width
rows:8
columns: 8
Repeater{
id: chessPiecesRptr
...
signal chessfigureSelected(int index)
delegate: Item{
id:chessPiecesItm
Image{
...
}
MouseArea
{
anchors.fill:parent
onClicked: {
chessPiecesRptr.chessfigureSelected(index)
}
}
}
}
}
The C++ method which updates the Repeater's model:
void ChessFiguresClass::changeModel()
{
QStringList dataList;
for(int i=0;i<64;i++)
dataList.append(QChar(posArray[i]));
QQmlProperty::write(chessPiecesRptr, "model", QVariant::fromValue(dataList));
}
Upvotes: 2
Views: 6941
Reputation: 886
I'm afraid it is not possible. QList<T>
(and QStringList) does not have inner mechanisms to notify Qml items about its changes. Only when model property from QML item is changed, the whole list is read again.
I had faced the same problem before, and I implemented a string list using QAbstractListModel as base class. The header looks like this:
#ifndef _SIMPLEMODEL_H_
#define _SIMPLEMODEL_H_
#include <QtCore>
class SimpleStringModel : public QAbstractListModel
{
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_DISABLE_COPY(SimpleStringModel)
Q_OBJECT
public:
explicit SimpleStringModel(QObject* parent = 0);
SimpleStringModel(const QList<QString>& initList, QObject* parent = 0);
virtual ~SimpleStringModel();
private:
enum Roles{
ModelDataRole = Qt::UserRole+1
};
public:
int count() const;
public:
void append(const QString& item);
void insert(int index, const QString& item);
void append(const QList<QString>& items);
void insert(int index, const QList<QString>& items);
bool removeOne(const QString& item);
int removeAll(const QString& item);
void removeAt(int index);
QList<QString> list() const;
signals:
void countChanged();
// QAbstractItemModel interface
public:
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QHash<int, QByteArray> roleNames() const;
private:
QList<QString> m_data;
};
#endif //_SIMPLEMODEL_H_
You can get all the code here (archive).
Upvotes: 4
Reputation: 49329
Contrary to the accepted answer it is indeed possible without going all the way to implement an entire QAbstractListModel
.
It is true that QStringList
doesn't have any way to notify for changes, but all you have to do is wrap it up in a property, something like this:
Q_PROPERTY(QVariant myModel READ myModel NOTIFY myModelChanged)
...
QVariant myModel() { return QVariant::fromValue(myStringList); }
...
void myModelChanged(); // signal
And just emit myModelChanged
every time you want to reflect a change in the model, be that replacing it with a different model or changing it internally. And use the myModel
property for the Repeater
's model.
However, if you have a lot of items in the model or the delegates are complex, it is always a good idea to implement your own QAbstractListModel
, because with the approach above, the repeater will recreate the entire model every time it "changes", whereas the QAbstractListModel
will only update the actual changes.
Upvotes: 13