qt - 如何更新 QML ListView 上 SingleTon 类型自定义 QAbstractListModel 的更改?
问题描述
我对 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
.cp 文件。
#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 不显示更新的整体。如何启用它以显示更新的条目?
谢谢。
解决方案
第一部分:QAbstractListModel 接口
首先,您HackNewsModel
需要表明已经添加了一行。将以下内容添加到您的 voidaddHackNews
方法中
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
方法需要以下内容:
- 父模型索引。在您的情况下,这是一个无效的索引,列表是平的。
- 添加的开始索引。将此设置为列表的大小(比最后一项的从零开始的索引高 1)
- 结束索引添加。将此设置为起始索引,因为您只添加一项。
如果您要添加更多功能,请HackNewsModel
确保您也实现了其他 begin* 和 end* 对。
请参阅文档:https ://doc.qt.io/qt-5/qabstractitemmodel.html#beginInsertRows
第二部分:QML 单例
其次,您实现单例模式的方式对 QML 引擎没有任何意义。你需要告诉引擎这个类是单例的:
qmlRegisterSingletonType<HackNewsModel>("HackNews", 1, 0, "HackNewsModel",
[](QQmlEngine *eng, QJSEngine *js) -> QObject *
{
eng->setObjectOwnership(&HackNewsModel::getInstance(),
QQmlEngine::ObjectOwnership::CppOwnership);
return &HackNewsModel::getInstance();
});
注意:设置所有权可能不是强制性的
这也意味着您不能HackNewsModel
像在 QML 中那样实例化,如果我是正确的,以下应该可以工作:
ListView {
id: listView
model: HackNewsModel
...
}
推荐阅读
- azure-devops - 如何在自定义条件下同时在 Azure DevOps 发布管道中运行多个复制文件任务?
- c# - 使用 INotifyPropertyChanged 更改属性时如何执行代码
- vue.js - Vue 3 + ESLint 警告 [intlify] 检测到 HTML
- json - jsonformatter 显示无效的 json 格式
- oracle - 使用选择步骤和组时,OBIEE 计算数据透视表中的授予总计错误
- azure-devops - 跨项目克隆 Azure DevOps 工作项
- java - Domino 服务器:仅将 JVM 选项传递给 http 任务
- python - 如何在数据框中展开列,其中包含需要在熊猫中展开的每一行的完整列表?
- laravel - 使用 Laravel 启动 Docker 容器时,*有时*会触发导致 HTTP 500 的“类视图不存在”
- google-sheets - 阻止其他用户使用 Google 脚本