Magzhan  Abdibayev
Magzhan Abdibayev

Reputation: 697

How to dynamicaly change Items of a Repeater from C++

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

Answers (2)

Jairo
Jairo

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

dtech
dtech

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

Related Questions