Andrew Tomazos
Andrew Tomazos

Reputation: 68638

Implementing a QAbstractListModel subclass for QML ListView?

How can you implement the data function of QAbstractListModel such that it returns something with properties that are visible from a QML ListView's delegate?

For the item type, I tried implementing a QObject subclass that has Q_PROPERTYs, but QAbstractListModel::data returns QVariant, and a QObject* can't be converted to QVariant?

How do I create a QVariant that has named properties visible to QML?

class MyListItem : public QObject {
  Q_OBJECT
  Q_PROPERTY(type name READ name WRITE set_name NOTIFY nameChanged)
  /*...*/
public:
  MyListItem(QObject* parent, const QString& name) : QObject(parent) {
    set_name(name);
  }
};

class MyList : public QAbstractListModel
{
public:
  MyList(QObject* parent);

  Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
  Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

  QVector<MyListItem*> items;
};

Q_INVOKABLE int MyList::rowCount(const QModelIndex &) const {
  return items.size();
}

Q_INVOKABLE QVariant MyList::data(const QModelIndex &index, int) const {
  MyListItem* item = items[index.row()];
  return item; // <--- ERROR
}

Getting:

In member function ‘virtual QVariant MyList::data(const QModelIndex&, int) const’:
error: use of deleted function ‘QVariant::QVariant(void*)’
  18 |   return item;
     |          ^~~~

Upvotes: 1

Views: 1111

Answers (1)

eyllanesc
eyllanesc

Reputation: 243955

If you want to encapsulate a QObject in a QVariant then you must use QVariant::fromValue:

return QVariant::fromValue(item);

MWE:

main.cpp

#include <QAbstractListModel>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

class MyListItem : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE set_name NOTIFY nameChanged)
public:
    MyListItem(const QString& name, QObject* parent=nullptr) : QObject(parent), m_name(name) {
    }
    QString name() const{
        return m_name;
    }
public slots:
    void set_name(QString name){
        if (m_name == name)
            return;
        m_name = name;
        emit nameChanged(m_name);
    }
signals:
    void nameChanged(QString name);
private:
    QString m_name;
};

class MyList : public QAbstractListModel
{
public:
    enum MyListRoles {
        ItemRole = Qt::UserRole + 1
    };
    MyList(QObject* parent=nullptr): QAbstractListModel(parent){}
    ~MyList(){
        qDeleteAll(items);
        items.clear();
    }
    void append(MyListItem *item){
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        items.append(item);
        endInsertRows();
    }
    int rowCount(const QModelIndex &parent=QModelIndex()) const override{
        if(parent.isValid())
            return 0;
        return items.count();
    }
    QVariant data(const QModelIndex &index, int role) const override{
        if(!index.isValid())
            return {};
        if(index.row() <0 || index.row() >= rowCount())
            return {};
        if(role == ItemRole)
            return QVariant::fromValue(items[index.row()]);
        return {};
    }
    QHash<int, QByteArray> roleNames() const override{
        return {{ItemRole, "item"}};
    }
private:
    QVector<MyListItem*> items;
};
#include "main.moc"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);
    MyList model;
    model.append(new MyListItem("foo1"));
    model.append(new MyListItem("foo2"));
    model.append(new MyListItem("foo3"));
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("mylist", &model);
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
    return app.exec();
}

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    ListView{
        anchors.fill: parent
        model: mylist
        delegate: Text {
            text: model.item.name
        }
    }
}

enter image description here

Upvotes: 4

Related Questions