一個簡單的方法來做到這將是設置dataModel
作爲回覆的屬性。
保持網絡管理器和其他對象的價值,而不是指針 - 指針是一種額外的間接性,在大多數情況下完全沒有必要。
下面是一個完整的C++ 11的例子,在這兩個的Qt 4的作品和5
// https://github.com/KubaO/stackoverflown/tree/master/questions/netreply-property-38775573
#include <QtNetwork>
#include <QStringListModel> // needed for Qt 4
using DataModel = QStringListModel;
const char kDataModel[] = "dataModel";
class Worker : public QObject {
Q_OBJECT
QNetworkAccessManager m_manager;
Q_SLOT void onFeedRetrieved(QNetworkReply* reply) {
auto dataModelObject = qvariant_cast<QObject*>(reply->property(kDataModel));
auto dataModel = qobject_cast<DataModel*>(dataModelObject);
qDebug() << dataModel;
emit got(reply);
}
public:
Worker(QObject * parent = nullptr) : QObject{parent} {
connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
SLOT(onFeedRetrieved(QNetworkReply*)));
}
void newRequest(const QUrl & url, DataModel * dataModel) {
QNetworkRequest request{url};
auto reply = m_manager.get(request);
reply->setProperty(kDataModel, QVariant::fromValue((QObject*)dataModel));
}
Q_SIGNAL void got(QNetworkReply*);
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
DataModel model;
Worker worker;
worker.newRequest(
QUrl{"http://stackoverflow.com/questions/38775573/best-way-to-use-qsignalmapper"},
&model);
QObject::connect(&worker, SIGNAL(got(QNetworkReply*)), &app, SLOT(quit()));
return app.exec();
}
#include "main.moc"
您只能使用QSignalMapper
如果有一個1:一個dataModel
實例和答覆之間的一對一映射。如果一個數據模型用於多個回覆,它將不起作用。
如果你真的關心分配計數,那麼使用屬性系統有一點開銷:設置對象的第一個屬性至少執行兩個分配 - 一個內部類和一個數據段QMap
。所以這是2N分配。相比之下,將映射添加到QSignalMapper
將執行分期日誌(N)分配。否則,QSignalMapper
是無用的。
在Qt 5中,使用它將會是一個反模式,因爲您可以連接到std::bind
或lambda。無論如何,如果QSignalMapper
映射到QVariant
會更好。
向對象添加第一個連接也會分配一個(不同的)內部類。爲了避免這種潛在的成本,您應該避免將連接添加到經常創建的對象。最好連接一次到QNetworkManager::finished(QNetworkReply*)
信號,而不是連接到每個QNetworkReply::finished()
。唉,一旦你使用排隊連接,這種保存就會消失:目前,他們花費額外的時間分配給每個傳遞給插槽的參數。這只是當前實施的一個缺點,而不是架構限制;它可以在隨後的一個小Qt版本中被刪除(如果我或其他人得到它的話)。
#include <QtNetwork>
#include <QStringListModel> // needed for Qt 4
using DataModel = QStringListModel;
class Worker : public QObject {
Q_OBJECT
QNetworkAccessManager m_manager;
QSignalMapper m_mapper;
// QObject::connect is not clever enough to know that QNetworkReply* is-a QObject*
Q_SLOT void map(QNetworkReply* reply) { m_mapper.map(reply); }
Q_SLOT void onFeedRetrieved(QObject * dataModelObject) {
auto dataModel = qobject_cast<DataModel*>(dataModelObject);
auto reply = qobject_cast<QNetworkReply*>(m_mapper.mapping(dataModelObject));
qDebug() << dataModel << reply;
emit got(reply);
}
public:
Worker(QObject * parent = nullptr) : QObject{parent} {
connect(&m_manager, SIGNAL(finished(QNetworkReply*)), SLOT(map(QNetworkReply*)));
connect(&m_mapper, SIGNAL(mapped(QObject*)), SLOT(onFeedRetrieved(QObject*)));
}
void newRequest(const QUrl & url, DataModel * dataModel) {
QNetworkRequest request{url};
auto reply = m_manager.get(request);
// Ensure a unique mapping
Q_ASSERT(m_mapper.mapping(dataModel) == nullptr);
m_mapper.setMapping(reply, dataModel);
}
Q_SIGNAL void got(QNetworkReply*);
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
DataModel model;
Worker worker;
QObject::connect(&worker, SIGNAL(got(QNetworkReply*)), &app, SLOT(quit()));
worker.newRequest(
QUrl{"http://stackoverflow.com/questions/38775573/best-way-to-use-qsignalmapper"},
&model);
return app.exec();
}
#include "main.moc"
我該如何檢查哪個QNetworkReply調用了插槽?我必須使用QObject :: setProperty嗎? –
您可以將指向活動回覆的指針存儲在'QMap'或'QHash'中,並通過一個鍵檢索它們,就像'QSignalMapper'一樣。或者你可以在答覆上設置一個對象名稱並檢查它。 –