2016-08-04 2 views
1

QSignalMapper를 Qt 4.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를 사용하여 같은 일을하는보다 세련된 솔루션이 있습니까?

답변

2

한 가지 간단한 방법을.

포인터로가 아닌 값으로 네트워크 관리자와 다른 개체를 유지하십시오. 포인터는 여분의 간접 참조이므로 대부분의 경우 완전히 필요하지 않습니다. dataModel 인스턴스와 응답 사이 : 1 매핑 아래

는 완전한 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" 

1가있을 경우에만 QSignalMapper을 사용할 수 있습니다. 하나의 데이터 모델이 여러 응답에 사용되면 작동하지 않습니다.

그런 다음 속성 시스템을 사용하여, 할당 횟수에 대해 정말 관심이 있다면 조금 더 많은 오버 헤드를 가지고 : 개체의 첫 번째 속성을 설정하면 두 개 이상의 할당을 수행 - 내부 클래스 및 QMap에 대한 데이터 세그먼트를. 그래서 2N 배정입니다. 이에 비해 매핑을 QSignalMapper에 추가하면 상각 된 로그 (N) 할당이 수행됩니다. 그렇지 않으면 QSignalMapper은 쓸모가 없습니다.

Qt 5를 사용하면 std::bind 또는 람다에 연결할 수 있기 때문에 완전히 반 패턴이됩니다. 어쨌든 QSignalMapperQVariant에 매핑 된 경우 훨씬 더 좋을 것입니다.

첫 번째 연결을 개체에 추가하면 (다른) 내부 클래스도 할당됩니다. 이러한 잠재적 비용을 피하려면 자주 작성한 오브젝트에 연결을 추가하지 않아야합니다. QNetworkReply::finished()에 연결하는 대신 번을 번에 연결하고 QNetworkManager::finished(QNetworkReply*) 신호를 연결하는 것이 좋습니다. 아아,이 절약은 대기열에있는 연결을 사용하면 사라집니다. 즉, 현재 슬롯에 ​​전달 된 모든 인수에 대해 추가 할당 비용이 발생합니다. 이것은 현재의 구현의 단점 일 뿐이며 아키텍처상의 한계는 아닙니다. 이후의 사소한 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" 
1

This answerQSignalMapper 대신 qobject_cast과 조합하여 sender()을 사용하여 우아하고 일반적인 해결책을 제공합니다.

코드는 다음과 같습니다 다음

connect(reply,SIGNAL(finished()), this, SLOT(onFeedRetrieved())); 

를 실행 한 다음, 그 응답에 속성으로 dataModel을 설정하는 것입니다 할

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 
    } 
} 
+0

어떤 QNetworkReply가 슬롯을 호출했는지 확인할 수 있습니까? QObject :: setProperty를 사용해야합니까? –

+0

'QMap' 또는'QHash'에 활성 응답에 대한 포인터를 저장할 수 있고'QSignalMapper'처럼 키로 검색 할 수 있습니다. 또는 회신에 개체 이름을 설정하고이를 확인할 수 있습니다. –

관련 문제