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 through Q_PROPERTY using QQmlListProperty:

    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 in main.cpp, I call SomeDataSource::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), only FooModel 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
                        }
                    }
                }
            }
        }
    }
    
    

  • Moderators

    @Talkless
    add the Q_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!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.