How to propagate changes in SingleTon type custom QAbstractListModel on QML ListView?
-
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.
-
@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/afterm_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
-
@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/afterm_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(); }