nO_OnE_910 nO_OnE_910 - 2 months ago 18
C++ Question

Set GridView model property from C++ without setting Context

I'm trying to set the

model
property of a QML
GridView
from C++ by calling

QQmlProperty::write(gridview, "model", QVariant::fromValue(objlist));
.

gridview
is set correctly, I can modify the property from C++, when I set it to a QList with 6 entries and print them from QML I get

qml: model = item(0x30617b50), Item(0x30617b90), Item(0x30617bd0), Item(0x30617c10), Item(0x30617c50), Item(0x30617cd0)
, though the model is not being displayed.

The Qt documentation suggest calling

QQmlContext *ctxt = view->rootContext();
ctxt->setContextProperty("gridModel", QVariant::fromValue(objlist));


And then setting the property from QML with
model: gridModel
but that does not really suit my needs. It works fine though, as soon as the property is set the correct data is being displayed. When I print the variable from QML the output is

qml: model = [object Object]
so there is definitely a difference between setting the context property and setting the object property, but I don't know how to fix this.

Answer

The solution I came up with is slightly different from the answers posted, so I figured writing a clear answer would be best.

The key was to subclass QAbstractItemModel (or more precisely ..ListModel so you don't have to deal with rows/columns in C++ and QML).

When doing it this way, not only can you simply set the model as a property with

QQuickItem *mainform = view->rootObject();
QQuickItem *grid = (QQuickItem *)mainform->findChild<QObject*>("GridView object name");

ItemModel itemmodel;
itemmodel.setItems(objlist):

QQmlProperty::write(grid, "model", QVariant::fromValue(&itemmodel));

It also notifys QML whenever a change is made to the model, e.g. an Item deleted. (You'll have to handle the changes properly though, see removeRows() in the example)

Here is my ItemModel:

// itemmodel.h

#include <QAbstractListModel>
#include <item.h>

class ItemModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit ItemModel(QObject *parent = 0);
    QHash<int, QByteArray> roleNames() const;

public slots:
    void setItems(QList<Item *> items);
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    int rowCount(const QModelIndex & parent = QModelIndex()) const;
    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());

private:
    QList<Item *> items;
};

// itemmodel.cpp
#include "itemmodel.h"

ItemModel::ItemModel(QObject *parent) : QAbstractListModel(parent)
{

}

// Column Names have to match all the Q_PROPERTYs defined in Item
const char* COLUMN_NAMES[] = {
    "property1",
    "property2",
    "...",
    NULL
};
QHash<int, QByteArray> makeRoleNames()
{
    int idx = 0;
    QHash<int, QByteArray> roleNames;
    while(COLUMN_NAMES[idx])
        roleNames[Qt::UserRole + idx + 1] = COLUMN_NAMES[idx++];

    return roleNames;
}

QHash<int, QByteArray> ItemModel::roleNames() const
{
    static const QHash<int, QByteArray> roleNames = makeRoleNames();
    return roleNames;
}

void ItemModel::setItems(QList<Item *> items)
{
    this->items = items;
}

int ItemModel::rowCount(const QModelIndex & /* parent */) const
{
    return items.count();
}

bool ItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    beginRemoveRows(QModelIndex(), row, row + count - 1);
    while (count--) delete items.takeAt(row);
    // example for custom deletion:
    //              items.takeAt(row)->removeFromRoot();
    endRemoveRows();
    return true;
}

QVariant ItemModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid())
        return QVariant();

    if (index.row() >= items.size() || index.row() < 0)
        return QVariant();

    if (role == Qt::DisplayRole) {
        return QVariant::fromValue(this->items.at(index.row()));
    }

    if (role > Qt::UserRole)
        return this->items.at(index.row())->property(makeRoleNames()[role]);
}

Sources:

  • [1] Qt Documentation Page about creating QAbstractItemModel subclasses for QML
  • [2] QAbstractItemModel reference
  • [3] QAbstractItemModel subclassing guide
  • [4] Forum post with a QAbstractListModel subclass
  • [5] Working (almost, data() slightly altered) implementation of QAbstractItemModel from which I took the rolenames functions