我在QT4.8中使用QSignalMapper。现在,我提出了如下网络请求:
// start the request
QNetworkRequest request(url);
QNetworkReply* reply = networkManager->get(request);
// connect signals using QSignalMapper
QSignalMapper* signalMapper = new QSignalMapper(reply);
connect(signalMapper, SIGNAL(mapped(QObject*)),this, SLOT(onFeedRetrieved(QObject*)), Qt::UniqueConnection);
connect(reply,SIGNAL(finished()),signalMapper, SLOT(map())); // connect to the signal mapper
signalMapper->setMapping(reply, dataModel); // set the mapping (the mapping should be automatically removed when reply is destroyed)对于我提出的每一个请求,我都是这样做的,我每次都将QSignalMapper与我的插槽连接起来。是否有一种更优雅的解决方案可以做同样的事情,也许使用一个QSignalMapper?
发布于 2016-08-04 20:29:59
一种简单的方法是将dataModel设置为回复上的一个属性。
保持网络管理器和其他对象的值,而不是指针-指针是额外的间接,在大多数情况下完全没有必要。
下面是一个在Qt 4和5中工作的完整的C++11示例。
// 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: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"发布于 2016-08-04 19:40:34
This answer提供了一个优雅和通用的解决方案:将sender()与qobject_cast结合使用,而不是QSignalMapper。
您的代码可能如下所示:
connect(reply,SIGNAL(finished()), this, SLOT(onFeedRetrieved()));然后:
void Foo::onFeedRetrieved()
{
QNetworkReply *reply = qobject_cast<QNetworkReply>(sender());
if (reply) { //check if the cast worked
//check which QNetworkReply invoked the slot and do stuff here
}
}https://stackoverflow.com/questions/38775573
复制相似问题