Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 the Q_PROPERTY of another manager class.
    I've used Q_DECLARE_METATYPE aswell as qRegisterMetaType<T> , but to no avail.
    When used as a property, I cannot even call a Q_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 !


  • Qt Champions 2018

    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. The qRegisterMetaType<T> and qmlRegisterType<T> for DuckModelList in main.cpp proved to be of no use too.

    Thanks again !


  • Lifetime Qt Champion

    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.


Log in to reply