Cannot use more the one QQmlListProperty<SomeQAbstractListModelDerivedClass> in QML
-
Hi,
I have two simple
QAbstractListModel
-derived classes, that ar virtually identical, with same base class but different hard-coded data they return for testing purposes.class FooModel : public QAbstractListModel { // ... }; QML_DECLARE_TYPE(FooModel)
class BarModel : public QAbstractListModel { // ... }; QML_DECLARE_TYPE(BarModel)
Also, I have some "central"
QObject
-derived class to be used in QML as "data source", which provides lists of these two models throughQ_PROPERTY
usingQQmlListProperty
:class SomeDataSource : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<FooModel> fooModels READ getFooModel NOTIFY fooModelsChanged) Q_PROPERTY(QQmlListProperty<BarModel> barModels READ getBarModel NOTIFY barModelsChanged) // ... };
Before
main.qml
is loaded inmain.cpp
, I callSomeDataSource::registerMetaTypes()
which looks like this:void SomeDataSource::registerMetaTypes() { qmlRegisterType<SomeDataSource>("TestApp", 1, 0, "SomeDataSource"); // Comment-out this line to make BarModel work, but break FooModel qmlRegisterType<FooModel>("TestApp", 1, 0, "FooModel"); qmlRegisterType<BarModel>("TestApp", 1, 0, "BarModel"); }
The problem is that I can get working only one of these QQmlListProperty<T> at a time. If
qmlRegisterType<FooModel>("TestApp", 1, 0, "FooModel");
line is commented-out,BarModel
works in QML (but not FooModel) and I can get the data. If this line is not commented-out (both types are registered), onlyFooModel
works!Also, I get this warning in console when these two models are registered:
QMetaType::registerTypedef: -- Type name 'QAbstractListModel*' previously registered as typedef of 'FooModel*' [1064], now registering as typedef of 'BarModel*' [1068]. QMetaType::registerTypedef: -- Type name 'QQmlListProperty<QAbstractListModel>' previously registered as typedef of 'QQmlListProperty<FooModel>' [1066], now registering as typedef of 'QQmlListProperty<BarModel>' [1069].
Google-fu didn't explained the problem for me, and I am quite lost now...
Any ideas?
Here's full sources of test application that reproduces this issue:
.pro:
QT += qml quick CONFIG += c++11 SOURCES += main.cpp \ FooModel.cpp \ BarModel.cpp \ SomeDataSource.cpp RESOURCES += qml.qrc HEADERS += \ FooModel.h \ BarModel.h \ SomeDataSource.h
main.cpp:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "SomeDataSource.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; SomeDataSource::registerMetaTypes(); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); }
SomeDataSource.h:
#ifndef SOMEDATASOURCE_H #define SOMEDATASOURCE_H #include <QList> #include <QObject> #include <QQmlEngine> #include <QQmlListProperty> #include "FooModel.h" #include "BarModel.h" class SomeDataSource : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<FooModel> fooModels READ getFooModel NOTIFY fooModelsChanged) Q_PROPERTY(QQmlListProperty<BarModel> barModels READ getBarModel NOTIFY barModelsChanged) public: SomeDataSource(QObject *parent = nullptr); QQmlListProperty<FooModel> getFooModel(); QQmlListProperty<BarModel> getBarModel(); static void registerMetaTypes(); signals: void fooModelsChanged(); void barModelsChanged(); private: QList<FooModel*> m_fooModels; QList<BarModel*> m_barModels; }; #endif // SOMEDATASOURCE_H
SomeDataSource.cpp
#include "SomeDataSource.h" SomeDataSource::SomeDataSource(QObject *parent) : QObject(parent), m_fooModels({new FooModel(this), new FooModel(this), new FooModel(this)}), m_barModels({new BarModel(this), new BarModel(this), new BarModel(this)}) { } QQmlListProperty<FooModel> SomeDataSource::getFooModel() { return QQmlListProperty<FooModel>(this, m_fooModels); } QQmlListProperty<BarModel> SomeDataSource::getBarModel() { return QQmlListProperty<BarModel>(this, m_barModels); } void SomeDataSource::registerMetaTypes() { qmlRegisterType<SomeDataSource>("TestApp", 1, 0, "SomeDataSource"); // Comment-out this line to make BarModel work, but break FooModel qmlRegisterType<FooModel>("TestApp", 1, 0, "FooModel"); qmlRegisterType<BarModel>("TestApp", 1, 0, "BarModel"); }
FooModel.h:
#ifndef FOOMODEL_H #define FOOMODEL_H #include <QAbstractListModel> #include <QQmlEngine> #include <QStringList> class FooModel : public QAbstractListModel { public: enum Roles { FooRole = Qt::UserRole + 1, }; public: FooModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; private: QHash<int, QByteArray> roleNames() const override; private: QStringList m_items; }; QML_DECLARE_TYPE(FooModel) #endif // FOOMODEL_H
FooModel.cpp:
#include "FooModel.h" const QHash<int, QByteArray> ROLE_NAMES = {{FooModel::FooRole, QByteArrayLiteral("foo")}}; FooModel::FooModel(QObject *parent) : QAbstractListModel(parent), m_items({QStringLiteral("foo1"), QStringLiteral("foo2"), QStringLiteral("foo3")}) { } int FooModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_items.size(); } QVariant FooModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_items.size()) return QVariant(); if (role == FooRole) return m_items[index.row()]; return QVariant(); } QHash<int, QByteArray> FooModel::roleNames() const { return ROLE_NAMES; }
BarModel.h:
#ifndef BARMODEL_H #define BARMODEL_H #include <QAbstractListModel> #include <QQmlEngine> #include <QStringList> class BarModel : public QAbstractListModel { public: enum Roles { BarRole = Qt::UserRole + 1, }; public: BarModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; private: QHash<int, QByteArray> roleNames() const override; private: QStringList m_items; }; QML_DECLARE_TYPE(BarModel) #endif // BARMODEL_H
BarModel.cpp:
#include "BarModel.h" const QHash<int, QByteArray> ROLE_NAMES = {{BarModel::BarRole, QByteArrayLiteral("bar")}}; BarModel::BarModel(QObject *parent) : QAbstractListModel(parent), m_items({QStringLiteral("bar1"), QStringLiteral("bar2"), QStringLiteral("bar3")}) { } int BarModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_items.size(); } QVariant BarModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_items.size()) return QVariant(); if (role == BarRole) return m_items[index.row()]; return QVariant(); } QHash<int, QByteArray> BarModel::roleNames() const { return ROLE_NAMES; }
main.qml:
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 import TestApp 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("QQmlListProperty test") property var someDataSource: SomeDataSource { } ColumnLayout { anchors.fill: parent ListView { id: fooView model: someDataSource.fooModels Layout.fillWidth: true Layout.fillHeight: true delegate: Row { Repeater { model: modelData Text { text: foo } } } } ListView { id: barView model: someDataSource.barModels Layout.fillWidth: true Layout.fillHeight: true delegate: Row { Repeater { model: modelData Text { text: bar } } } } } }
-
@Talkless
add theQ_OBJECT
macro to every class (FooModel, BarModel) you want to register. -
IT WORKS! Thank you @raven-worx very, very mutch :-) . I lost whole day on this!