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

How to properly link C++ model to QML front end when Interfacing with a server



  • here is my setup:

    First is the model class, this stores a pointer to the data of type CompetitionsList. We can see that it implements the necessary basic functions when deriving from QAbstractListModel, and it uses the QML_ELEMENT macro to expose the model to the QML type system:

    class CompetitionsListModel : public QAbstractListModel
    {
        Q_OBJECT
        Q_PROPERTY(CompetitionsList* list READ list WRITE setList)
        QML_ELEMENT
    
    public:
        explicit CompetitionsListModel(QObject *parent = nullptr);
    
        enum {
            NameRole = Qt::ItemDataRole(),
            IdRole
        };
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
        CompetitionsList *list() const;
        void setList(CompetitionsList *list);
    
        virtual QHash<int, QByteArray> roleNames() const;
    
    private:
        CompetitionsList* m_list;  \\ <--- ptr to data
    };
    

    Then we have the data class. This class is also a QML_ELEMENT, and it has a userid property. This is used to populate the QVector<listItem> by requesting data from a RESTful http server.

    The two signals preItemAppended() and postItemAppended() are emitted when data is added to the QVector<listItem>. These signals are then connected to the Model as a way of notifying the model it must create a new row in the list. The important function here is populateCompetitions, which I explain below.

    struct listItem {
        QString competitionName;
        QString competitionId;
    };
    
    class CompetitionsList : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString userId READ getUserId WRITE setUserId)
        QML_ELEMENT
    
    public:
        explicit CompetitionsList(QObject *parent = nullptr);
        void populateCompetitions(const QString& userId);
        QVector<listItem> items();
        QString getUserId() const;
        void setUserId(const QString &value);
    
    signals:
        void preItemAppended();
        void postItemAppended();
        void errorPopulatingData();
    private:
        QVector<listItem> m_competitions;
        QString userId;
    };
    

    The populateCompetitions function looks as so:

    void CompetitionsList::populateCompetitions(const QString &userId)
    {
        qDebug() << "initialising data";
        HttpClient* client = new HttpClient();
        client->getUserCompetitions(userId);
    
        connect(client, &HttpClient::getUserCompetitionsResult, [=](const QByteArray& reply){
            QJsonDocument _reply = QJsonDocument::fromJson(reply);
    
            if(_reply["data"].isNull()) {
                emit errorPopulatingData();
            }
            else {
                const QJsonArray competitions = _reply["data"]["competitions"].toArray();
                for(const QJsonValue& competition : competitions) {
                    emit preItemAppended();
    
                    listItem item{competition["competition-name"].toString(), competition["competition-id"].toString()};
                    m_competitions.append(std::move(item));
    
                    emit postItemAppended();
                }
            }
        });
    }
    

    It requests data from the database, and then stores it locally.

    Then finally, the QML model which ties it all together:

    ListView {
            id: view
            implicitHeight: 1920
            implicitWidth: 1080
            clip: true
    
            model: CompetitionsListModel {
                list: CompetitionsList {
                    id: compList
                }
                Component.onCompleted: { compList.userId = "6033f377257e8630ed13299e" } //<-- calls the populateCompetitions function
            }
    
            delegate: RowLayout {
                Text {
                    text: qsTr(model.name + ":::" + model.id)
                }
            }
        }
    

    We can see that it has a ListView element, with a model of type CompetitionsListModel and a list of type CompetitionsList. Once the model component is created, we set the userId of the list, this in turn calls the populateCompetitions function which sets up the data for the model to use as seen above.

    Unfortunately this doesnt anything when I run the code. Blank screen. Nada. I was wondering if anyone has an insight as to what might be causing this based on the code provided. Ive been at it for so long and it just inst being nice.

    Thanks for any help given. Sorry about all the code - its hard to reproduce in a minimal way.


Log in to reply