我对QML还是很陌生,所以在如何将自定义QAbstractListModel
到QML List View
中传播更改方面遇到了困难。
我有以下HackNewsModel
。
头文件
#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
Cpp文件。
#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();
}
但是,此数据模型是通过NetworkRequestMaker
更新的,该模型向网络发出了一些请求并更新了模型。
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文件。
#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());
}
}
单例类如下。
#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
主要cpp文件如下。
#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();
}
我的QML文件如下。
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
}
}
}
尽管模型是单例的,但qmnl listview不会显示更新的整体。如何启用它以显示更新的条目?
谢谢。
答案 0 :(得分:1)
第一部分:QAbstractListModel接口
首先,您的HackNewsModel
需要表示已添加一行。将以下内容添加到您的无效addHackNews
方法中
void HackNewsModel::addHackNews(QJsonObject &hackNews)
{
...
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_hackNewsList.append(HackNews{id, deleted, type, by, time, text, dead, parentId, pollId, kidsIdList, url, score, title, partsIdList, descendantCount});
endInsertRows();
}
beginInsertRows
方法要求满足以下条件:
如果您要为HackNewsModel
添加更多功能,请确保也实现其他的begin *和end *对。
查看文档:{{3}}
第二部分:QML单例
第二,实现Singleton模式的方式对QML Engine毫无意义。您需要告诉引擎该类是单例:
qmlRegisterSingletonType<HackNewsModel>("HackNews", 1, 0, "HackNewsModel",
[](QQmlEngine *eng, QJSEngine *js) -> QObject *
{
eng->setObjectOwnership(&HackNewsModel::getInstance(),
QQmlEngine::ObjectOwnership::CppOwnership);
return &HackNewsModel::getInstance();
});
注意:设置所有权可能不是强制性的
这也意味着您无法像在QML中那样实例化HackNewsModel
,如果我正确的话,以下方法应该起作用:
ListView {
id: listView
model: HackNewsModel
...
}