Reputation: 68638
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_PROPERTY
s, 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
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
}
}
}
Upvotes: 4