Solved QAbstractListModel into a property
-
I have been trying to use the
QAbstractListModel
class recently, and based my code off the example for this specific class.However, I wish not to use
QQmlContext::setContextProperty()
to send my model over to the QML part of the code, and would rather do it through theQ_PROPERTY
of another manager class.
I've usedQ_DECLARE_METATYPE
aswell asqRegisterMetaType<T>
, but to no avail.
When used as a property, I cannot even call aQ_INVOKABLE
function without the following error message:
qrc:/main.qml:28: TypeError: Property 'rowCount' of object QVariant(Duckies::DuckModelList) is not a function
Can I actually use the QAbstractListModel as a
Q_PROPERTY
? If yes, what could I be doing wrong ?Here is the full code smallest reproduction example I could manage, version of QT is 5.9.1 :
main.cpp:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <duckmanager.h> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; qmlRegisterType<Duckies::DuckManager> ("DuckQML", 1, 0, "DuckManager"); qmlRegisterType<Duckies::DuckModelList> ("DuckQML", 1, 0, "DuckModelList"); qRegisterMetaType<Duckies::DuckModelList>(); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }
main.qml:
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import DuckQML 1.0 ApplicationWindow { visible: true width: 300 height: 200 title: qsTr("Hello World") DuckManager { id: manager } ListView { id: list model: manager.duckies delegate: Label { text: "This ducky is named " + model.duck + " !" } Component.onCompleted: { console.log("How many ducks ?") console.log(manager.duckies.rowCount() + " ?") } } }
duckmanager.h:
#pragma once #include <QObject> #include "duckmodellist.h" namespace Duckies { class DuckManager : public QObject { Q_OBJECT public: explicit DuckManager(QObject *parent = nullptr); Q_PROPERTY(Duckies::DuckModelList duckies MEMBER m_duckies NOTIFY duckiesChanged) public: signals: void duckiesChanged(); private: Duckies::DuckModelList m_duckies; }; }
duckmanager.cpp:
#include "duckmanager.h" Duckies::DuckManager::DuckManager(QObject *parent) : QObject(parent) { m_duckies.addDuck("Kuro"); m_duckies.addDuck("Quacky"); m_duckies.addDuck("Chico"); }
duckmodellist.h:
#pragma once #include "QAbstractListModel" namespace Duckies { class DuckModelList : public QAbstractListModel { Q_OBJECT public: enum RobotMessageRoles { DuckRole = Qt::UserRole + 1 }; DuckModelList(QObject *parent = nullptr); DuckModelList(const DuckModelList &other); DuckModelList& operator=(const DuckModelList& other); virtual ~DuckModelList(); bool operator!=(const DuckModelList& other); void addDuck(const QString &message); Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; protected: QHash<int, QByteArray> roleNames() const; private: QList<QString> m_ducks; }; } Q_DECLARE_METATYPE(Duckies::DuckModelList)
duckmodellist.cpp:
#include "duckmodellist.h" Duckies::DuckModelList::DuckModelList(QObject *parent) : QAbstractListModel(parent) {} Duckies::DuckModelList::~DuckModelList() {} Duckies::DuckModelList::DuckModelList(const Duckies::DuckModelList &other) : QAbstractListModel(nullptr) { for (const QString &it: other.m_ducks) addDuck(it); } Duckies::DuckModelList &Duckies::DuckModelList::operator=(const Duckies::DuckModelList &other) { beginResetModel(); this->m_ducks.clear(); endResetModel(); std::for_each(other.m_ducks.begin(), other.m_ducks.end(), [&](const QString &it) {addDuck(it);}); return *this; } bool Duckies::DuckModelList::operator!=(const Duckies::DuckModelList &other) { if (this->m_ducks.count() != other.m_ducks.count()) return true; for (int i = 0; i < this->m_ducks.count(); i++) if (m_ducks[i] != other.m_ducks[i]) return true; return false; } int Duckies::DuckModelList::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_ducks.count(); } QVariant Duckies::DuckModelList::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_ducks.count()) return QVariant(); const QString &message = m_ducks[index.row()]; if (role == DuckRole) return message; return QVariant(); } void Duckies::DuckModelList::addDuck(const QString &message) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_ducks.push_back(message); endInsertRows(); emit dataChanged(QModelIndex(), QModelIndex()); } QHash<int, QByteArray> Duckies::DuckModelList::roleNames() const { QHash<int, QByteArray> roles; roles[DuckRole] = "duck"; return roles; }
Thanks, and sorry for the lengthy example !
-
Use a pointer, QML doesn't support passing QObjects by value.
and set a parent on it otherwise the QML engine will take ownership of it.PS: your dataChanged is not necessary in addDuck.
-
Thanks a lot, got it working now ! I'll remember about using pointers in this case now.
For the record, I just had to change m_duckies to a pointer, aswell as the
Q_PROPERTY
type to a pointer too. TheqRegisterMetaType<T>
andqmlRegisterType<T>
forDuckModelList
in main.cpp proved to be of no use too.Thanks again !
-
Hi,
@GrecKo said in QAbstractListModel into a property:
Use a pointer, QML doesn't support passing QObjects by value.
Just one correction, it's nothing QML specific. QObject based class are not copyable.