【发布时间】:2018-03-17 20:45:11
【问题描述】:
我目前正在开发一个ticker客户端,它从Web api轮询数据,将其传递给ListModel,然后用于在qml ListView中显示数据。
class TickerClient : public QObject
{
Q_OBJECT
public:
explicit TickerClient(QObject *parent = nullptr);
QList<QVariantMap> items() const;
protected:
void registerErrorHandlers(QNetworkReply *reply);
signals:
void statusChanged(QNetworkAccessManager::NetworkAccessibility acc);
void dataChanged();
void preItemRefresh();
void postItemRefresh();
public slots:
void fetch(int start = 0, int limit = 100);
protected slots:
void onReceive(QNetworkReply *reply);
protected:
QSharedPointer<QNetworkAccessManager> mNetMan;
QList<QVariantMap> mItems;
};
class TickerModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(TickerClient *client READ client WRITE setClient)
public:
explicit TickerModel(QObject *parent = nullptr);
enum {
IdRole = Qt::UserRole + 1,
NameRole,
SymbolRole,
...
};
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
TickerClient *client() const;
void setClient(TickerClient *client);
private:
TickerClient *mClient;
};
ticker 客户端不仅获取,还处理获取的数据,并将其作为 QVariantMaps 列表暴露给周围的 ListModel。
void TickerClient::onReceive(QNetworkReply *reply)
{
if (!reply)
return;
if(reply->error()) {
qCritical() << "Error: "
<< reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString();
return;
}
// Read all data as json document.
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
if(!jsonDoc.isArray()) {
qCritical() << "Error: Expected array";
return;
}
emit preItemRefresh();
mItems.clear();
QJsonArray currencies = jsonDoc.array();
for(int i = 0; i < currencies.count(); i++) {
QJsonObject currency = currencies[i].toObject();
mItems.append(currency.toVariantMap());
}
emit postItemRefresh();
reply->deleteLater();
}
TickerClient 和 TickerModel 都暴露给 qml:
qmlRegisterType<TickerModel>("Ticker", 1, 0, "TickerModel");
qmlRegisterUncreatableType<TickerClient>("Ticker", 1, 0, "TickerClient",
QStringLiteral("MarketCapProvider should not be created in QML"));
TickerClient tickerClient;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("tickerClient", &tickerClient);
然后将暴露的客户端交给模型并每 5 秒刷新一次:
ListView {
id: page
Timer {
interval: 5000
running: true
repeat: true
triggeredOnStart: true
onTriggered: {
var pos = scrollBar.position
tickerClient.fetch()
scrollBar.position = pos
}
}
ScrollBar.vertical: ScrollBar {
id: scrollBar
}
model: TickerModel {
client: tickerClient
}
delegate: RowLayout {
width: parent.width
spacing: 10
Label {
text: "#" + model.rank
padding: 5
}
Label {
text: model.name
padding: 5
}
}
}
但是,由于返回了错误的数据,数据的获取似乎并不顺利。所以如果我要求model.name,我可能会以model.rank 结束。以下代码用于获取给定一对index 和role 的条目。
QVariant TickerModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || !mClient)
return QVariant();
auto it = roleNames().find(role);
if(it != roleNames().end())
return mClient->items().at(index.row())[it.value()];
else
qDebug() << "Didn't match a role";
return QVariant();
}
QHash<int, QByteArray> TickerModel::roleNames() const
{
static const QHash<int, QByteArray> names{
{ IdRole, "id" },
{ NameRole, "name" },
...
};
return names;
}
TickerClient *TickerModel::client() const
{
return mClient;
}
void TickerModel::setClient(TickerClient *client)
{
beginResetModel();
if(mClient) {
mClient->disconnect(this);
}
mClient = client;
if(mClient) {
connect(mClient, &TickerClient::preItemRefresh, this, [=]() {
beginResetModel();
});
connect(mClient, &TickerClient::postItemRefresh, this, [=]() {
endResetModel();
});
}
endResetModel();
}
什么,我做错了吗?如何为更复杂的 Json 对象扩展此解决方案?
【问题讨论】:
-
学习 C++ 似乎是一种巨大的浪费。由于 JS 原生支持 JSON,您可以使用简单的 JS 数组作为列表视图的模型...
-
我没有看到在 qml Timer
tickerClient.fetch()中调用的公共插槽fetch()的任何实现 -
@MohammadKanan 我没有将它添加到我的问题中,因为理解问题并不重要。如您所见,调用
fetch()返回的数据在onReceive()中处理,它从http响应中提取json数据并将其推送到QVariantMaps列表中。 -
@dtech 有几个原因我认为(不知道)在这里使用 JS 不是一个好主意。我对 JS 不熟悉,也没有看到并行处理大量 http 请求的有效方法。此外,我不确定如何处理 Javascript 中丢失或更改模型实体等错误。那么,为什么我要使用 qml 而不是 Qt Widgets?我喜欢现代 UI 元素,例如
QDrawer和QStackView,它们不能作为小部件使用。我的总体印象是 Widget UI 元素在外观和感觉上有些过时。但是,我愿意接受替代方案。 -
好吧,你已经在使用 QML 来做 GUI,你不妨好好利用它。 C++ 代码所做的一切在 QML 中都是可能的并且更容易做到,您可以发出请求,解析返回的 JSON 并将结果用作模型。 JS 原生支持所有这些东西。在这种特殊情况下,使用 C++ 并没有为您带来任何好处,当您的瓶颈是 http 时,不会有明显的性能提升,只会增加复杂性并降低开发速度,因为您必须为每一个微小的变化重新编译。