Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?
QtWS25 Last Chance

How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
qmlqabstractlistmolistview
3 Posts 2 Posters 787 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Q Offline
    Q Offline
    qml.newbie
    wrote on last edited by qml.newbie
    #1

    I'm very new to QML, so having a struggle about how to propagate the changes in a custom QAbstractListModel to QML List View.

    I have the following HackNewsModel.

    The header file

    #ifndef HACKNEWSMODEL_H
    #define HACKNEWSMODEL_H
    
    #include "Singleton.hpp"
    #include <QAbstractListModel>
    #include <QJsonObject>
    #include <QDateTime>
    
    struct HackNews
    {
        QString m_id;
        bool m_deleted;
        QString m_type;
        QString m_by;
        QDateTime m_time;
        QString m_text;
        bool m_dead;
        QString m_parentId;
        QString m_pollId;
        QStringList m_kidsIdList;
        QString m_url;
        QString m_score;
        QString m_title;
        QStringList m_partsIdList;
        QString m_descendantCount;
    };
    
    class HackNewsModel : public QAbstractListModel, public Singleton<HackNewsModel>
    {
        Q_OBJECT
    
    public:
        void addHackNews(QJsonObject &hackNews);
        enum Roles {
            IdRole = Qt::UserRole + 1
        };
    
        QHash<int, QByteArray> roleNames() const override;
    
        int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    
        QVariant data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const override;
    
        friend class Singleton<HackNewsModel>;
        explicit HackNewsModel(QObject * parent = nullptr);
        ~HackNewsModel() override;
    
    private:
        QList<HackNews> m_hackNewsList;
        QHash<int, QByteArray> m_roles;
    
    };
    
    #endif // HACKNEWSMODEL_H
    

    The Cpp file.

    #include "HackNewsModel.h"
    #include <QJsonArray>
    #include <QDebug>
    
    HackNewsModel::HackNewsModel(QObject *parent) : QAbstractListModel(parent)
    {
        m_roles[0] = "id";
    
        QString id = "Demo id";
        bool deleted = false;
        QString type;
        QString by;
        QDateTime time;
        QString text;
        bool dead = false;
        QString parentId;
        QString pollId;
        QStringList kidsIdList;
        QString url;
        QString score;
        QString title;
        QStringList partsIdList;
        QString descendantCount;
        m_hackNewsList.append(HackNews{id+"1", deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
        m_hackNewsList.append(HackNews{id+"2", deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
        m_hackNewsList.append(HackNews{id+"3", deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
        m_hackNewsList.append(HackNews{id+"4", deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
        m_hackNewsList.append(HackNews{id+"5", deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
    }
    
    HackNewsModel::~HackNewsModel()
    {
    
    }
    
    void HackNewsModel::addHackNews(QJsonObject &hackNews)
    {
        QString id = "Demo id";
        bool deleted = false;
        QString type;
        QString by;
        QDateTime time;
        QString text;
        bool dead = false;
        QString parentId;
        QString pollId;
        QStringList kidsIdList;
        QString url;
        QString score;
        QString title;
        QStringList partsIdList;
        QString descendantCount;
    
        if(hackNews.contains("id"))
        {
            id = hackNews["id"].toString();
        }
    
        if(hackNews.contains("deleted"))
        {
            deleted = hackNews["deleted"].toBool();
        }
    
        if(hackNews.contains("type"))
        {
            type = hackNews["type"].toString();
        }
        if(hackNews.contains("by"))
        {
            by = hackNews["by"].toString();
        }
    
        if(hackNews.contains("time"))
        {
            time = QDateTime::fromTime_t(static_cast<unsigned int>(hackNews["time"].toInt()));
        }
    
        if(hackNews.contains("text"))
        {
            text = hackNews["text"].toString();
        }
    
        if(hackNews.contains("dead"))
        {
            dead = hackNews["dead"].toBool();
        }
    
        if(hackNews.contains("parent"))
        {
            parentId = hackNews["parent"].toString();
        }
    
        if(hackNews.contains("poll"))
        {
            pollId = hackNews["poll"].toString();
        }
    
        if(hackNews.contains("kids"))
        {
            foreach (QVariant value, hackNews["kids"].toArray().toVariantList()) {
                kidsIdList.append(value.toString());
            }
        }
    
        if(hackNews.contains("url"))
        {
            url = hackNews["url"].toString();
        }
    
        if(hackNews.contains("title"))
        {
            title = hackNews["title"].toString();
        }
    
        if(hackNews.contains("parts"))
        {
            foreach (QVariant value, hackNews["parts"].toArray().toVariantList()) {
                partsIdList.append(value.toString());
            }
        }
    
        if(hackNews.contains("descendents"))
        {
            descendantCount = hackNews["descendents"].toString();
        }
    
        m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
    }
    
    QHash<int, QByteArray> HackNewsModel::roleNames() const
    {
        return m_roles;
    }
    
    int HackNewsModel::rowCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return 0;
        return m_hackNewsList.size();
    }
    
    
    QVariant HackNewsModel::data(const QModelIndex &index, int /*role*/) const
    {
        //    if (!hasIndex(index.row(), index.column(), index.parent()))
        if(!index.isValid())
            return QVariant();
    
        const HackNews &news = m_hackNewsList.at(index.row());
    
        //    if(role == IdRole){
        //        qDebug() << "Seeking id";
        return news.m_id;
        //    }
    
        //    return QVariant();
    }
    

    However, this data model gets updated through NetworkRequestMaker that makes some request to a network and updates the model.

    Header file of NetworkRequestMaker.

    #ifndef NETWORKREQUESTMAKER_H
    #define NETWORKREQUESTMAKER_H
    
    #include <QObject>
    #include <QNetworkAccessManager>
    
    class QNetworkReply;
    class NetworkRequestMaker : public QObject
    {
        Q_OBJECT
    
    public:
        explicit NetworkRequestMaker(QObject *parent = nullptr);
        void startRequest(const QUrl &requestedUrl);
        void httpReadyRead();
        void httpFinished();
    
    private:
        QUrl url;
        QNetworkAccessManager m_qnam;
        QNetworkReply *m_reply;
    };
    
    #endif // NETWORKREQUESTMAKER_H
    

    Cpp file.

    #include "NetworkRequestMaker.h"
    #include <QNetworkReply>
    #include <QDebug>
    #include <QJsonDocument>
    #include <QJsonArray>
    #include <QJsonObject>
    #include <QDateTime>
    #include "HackNewsModel.h"
    
    NetworkRequestMaker::NetworkRequestMaker(QObject *parent)
        : QObject(parent),
          m_reply(nullptr)
    {
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty"));
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/2921983.json?print=pretty"));
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/121003.json?print=pretty"));
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/192327.json?print=pretty"));
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/126809.json?print=pretty"));
        startRequest(QUrl("https://hacker-news.firebaseio.com/v0/item/160705.json?print=pretty"));
    }
    
    void NetworkRequestMaker::startRequest(const QUrl &requestedUrl)
    {
        url = requestedUrl;
        m_reply = m_qnam.get(QNetworkRequest(url));
        connect(m_reply, &QNetworkReply::finished, this, &NetworkRequestMaker::httpFinished);
        connect(m_reply, &QIODevice::readyRead, this, &NetworkRequestMaker::httpReadyRead);
    }
    
    void NetworkRequestMaker::httpReadyRead()
    {
        QString strReply = QString(m_reply->readAll());
        QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8());
        QJsonObject jsonObj = jsonResponse.object();
        HackNewsModel::getInstance().addHackNews(jsonObj);
    }
    
    void NetworkRequestMaker::httpFinished()
    {
        if (m_reply->error()) {
            qDebug()<<tr("Download failed:\n%1.").arg(m_reply->errorString());
        }
    }
    

    The singleton class is as the following.

    #ifndef SINGLETON_HPP
       #define SINGLETON_HPP
       
       template <typename T>
       class Singleton
       {
       public:
       
       	/*!*************************************************************************
       	\brief      Constructs the singleton (if necessary) and returns the pointer.
       	****************************************************************************/
       	static T& getInstance()
       	{
       		static T _singleton; //!< Unique instance of class T
       		return _singleton;
       	}
       
       protected:
       
       	/*!*************************************************************************
       	\brief      Constructor.
       	\note		protected to avoid misuses.
       	****************************************************************************/
       	Singleton() {}
       
       	/*!*************************************************************************
       	\brief      Destructor.
       	\note		protected to avoid misuses.
       	****************************************************************************/
       	virtual ~Singleton() {}
       
       	/*!*************************************************************************
       	\brief      Copy constructor.
       	\note		protected to avoid misuses.
       	****************************************************************************/
       	Singleton(const Singleton&);
       
       	/*!*************************************************************************
       	\brief      Assignment operator.
       	\note		protected to avoid misuses.
       	****************************************************************************/
       	Singleton& operator=(const Singleton&);
       };
       
       #endif // SINGLETON_HPP
    

    My QML file is as below.

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    import QtQuick.Layouts 1.12
    import Hacknews 1.0
    
    Frame {
        width: 640
        height: 480
        ListView {
            id: listView
            anchors.fill: parent
            model: HackNewsModel {}
    
            delegate: Text {
                text: model.id
            }
        }
    }
    

    Despite the model being a singleton, qmnl listview doesn't show the updated entires. How do I enable it to show the updated entries?

    Thanks.

    1 Reply Last reply
    0
    • VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by
      #2

      @qml.newbie said in How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?:

      m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

      You need to call beginInsertRows/endInsertRows before/after m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      Q 1 Reply Last reply
      0
      • VRoninV VRonin

        @qml.newbie said in How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?:

        m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

        You need to call beginInsertRows/endInsertRows before/after m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

        Q Offline
        Q Offline
        qml.newbie
        wrote on last edited by
        #3

        @VRonin said in How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?:

        @qml.newbie said in How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?:

        m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

        You need to call beginInsertRows/endInsertRows before/after m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});

        I have now added these lines but even now it's not working. Could be down to something I did wrong in the main.cpp where I'm registering the model for qml? The main cpp file is as below.

        #include <QGuiApplication>
        #include <QQmlApplicationEngine>
        #include "NetworkRequestMaker.h"
        #include "HackNewsModel.h"
        
        int main(int argc, char *argv[])
        {
            QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            QGuiApplication app(argc, argv);
        
            NetworkRequestMaker testRequestMaker;
        
            qmlRegisterType<HackNewsModel>("Hacknews", 1, 0, "HackNewsModel");
        
            QQmlApplicationEngine engine;
            const QUrl url(QStringLiteral("qrc:/main.qml"));
            QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                             &app, [url](QObject *obj, const QUrl &objUrl) {
                if (!obj && url == objUrl)
                    QCoreApplication::exit(-1);
            }, Qt::QueuedConnection);
            engine.load(url);
        
            return app.exec();
        }
        
        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved