I have two get QNetworkRequest.
I want to handle finished signals from different methods.
For example this is code in
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
GetUserData();
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(GetUserDataCompleted(QNetworkReply*)));
GetMessages();
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(GetMessagesCompleted(QNetworkReply*)));
}
This my one method
I have tried replay->deleteLater(); but same result
Please advice me something useful
void MainWindow::GetUserDataCompleted(QNetworkReply *replay)
{
if(replay->error() == QNetworkReply::NoError)
{
QString getData = replay->readAll();
QMessageBox msg;
if(getData == "1")
{
msg.setText("User Is not Exits");
}
else
{
QDomDocument doc;
if(doc.setContent(getData))
{
QDomElement domElem = doc.documentElement();
QDomNode n = domElem.firstChild();
while(!n.isNull()) {
QDomElement e = n.toElement(); // try to convert the node to an element.
if(!e.isNull()) {
msg.setText(e.namedItem("Image").childNodes().at(0).nodeValue());
msg.exec();
}
n = n.nextSibling();
}
}
replay->deleteLater();
}
}
}
You can create a RequestSender class whose role is looking after requests.
Each RequestSender object will handle one unique request. While creating the QNetworkRequest that will be sent, the RequestSender will "tag" its own request with the originatingObject attribute. This attribute indicates which object sent the request. When a RequestSender object receives a reply, it will look if it is the sender of the request via the originatingObject attribute. For further informations about originatingObject, you can refer to the documentation here : http://qt-project.org/doc/qt-4.8/qnetworkrequest.html#originatingObject
Below is an example of what you can do.
requestsender.hpp :
class RequestSender {
public:
RequestSender();
~RequestSender();
void createRequest(/* Request parameters */);
public slots:
void endRequest(QNetworkReply* replay);
};
requestsender.cpp :
RequestSender::RequestSender() {
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(endRequest(QNetworkReply*)));
}
RequestSender::~RequestSender() {
disconnect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(endRequest(QNetworkReply*)));
}
void RequestSender::createRequest(/* Request parameters */) {
QNetworkRequest * myRequest = 0;
// Build myRequest with the request parameters
myRequest->setOriginatingObject(this);
nam->get(*myRequest);
}
void RequestSender::endRequest(QNetworkReply* replay) {
if (replay->request().originatingObject() != this) {
// That's not the request sent by the object -> stop the method here !
return;
}
// Treatments on replay
}
Every operation you do with your QNetworkAccessManager will return a QNetworkReply. This has also has an signal finished(). Maybe you can connect this signal to your different slots.
Related
Is there a better example on how to implement multiple async requests.
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc ListSayHello1 (HelloRequest1) returns (stream HelloReply1) {}
rpc ListSayHello2 (HelloRequest2) returns (stream HelloReply2) {}
}
// The request message containing the user's name.
message HelloRequest1 {
string name = 1;
}
message HelloRequest2 {
string name = 1;
}
// The response message containing the greetings
message HelloReply1 {
string message = 1;
}
message HelloReply2 {
string message = 1;
}
I am using below async pattern to handle SayHello1, but now I want to add support for SayHello2. What should be the approach?
new CallData(&service_, cq_.get(), *this);
void* tag; // uniquely identifies a request.
bool ok;
while (true) {
// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
In CallData, I doing this
void CallData::Proceed()
{
if (status_ == CREATE) {
status_ = PROCESS;
service_->RequestListSayHello1(&ctx_, &request_, &writer_, cq_, cq_,
this);
}
...
}
It is not necessary to create extra completion queues. We just need to know how to handle what the completion queue returns. We can solve this by making a common base class, that is able to do the necessary functions:
class CallDataBase
{
protected:
virtual void WaitForRequest() = 0;
virtual void HandleRequest() = 0;
public:
virtual void Proceed() = 0;
CallDataBase() {}
};
Each specialization of CallDataBase know how to Proceed, WaitForRequest and HandleRequest. Some of this is common for all requests, so it is convenient to use a templatized class:
template < class RequestType, class ReplyType>
class CallDataT : CallDataBase
{
protected:
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
Greeter::AsyncService* service_;
ServerCompletionQueue* completionQueue_;
RequestType request_;
ReplyType reply_;
ServerAsyncResponseWriter<ReplyType> responder_;
ServerContext serverContext_;
// When we handle a request of this type, we need to tell
// the completion queue to wait for new requests of the same type.
virtual void AddNextToCompletionQueue() = 0;
public:
CallDataT(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) :
status_(CREATE),
service_(service),
completionQueue_(completionQueue),
responder_(&serverContext_)
{
}
public:
virtual void Proceed() override
{
if (status_ == CREATE)
{
status_ = PROCESS;
WaitForRequest();
}
else if (status_ == PROCESS)
{
AddNextToCompletionQueue();
HandleRequest();
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
}
else
{
// We're done! Self-destruct!
if (status_ != FINISH)
{
// Log some error message
}
delete this;
}
}
};
And finally, the actual implementation of the message types:
class CallDataHello : CallDataT<HelloRequest, HelloReply>
{
public:
CallDataHello(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) : CallDataT(service, completionQueue)
{
Proceed();
}
protected:
virtual void AddNextToCompletionQueue() override
{
new CallDataHello(service_, completionQueue_);
}
virtual void WaitForRequest() override
{
service_->RequestSayHello(&serverContext_, &request_, &responder_, completionQueue_, completionQueue_, this);
}
virtual void HandleRequest() override
{
reply_.set_message(std::string("Hello ") + request_.name());
}
};
class CallDataHelloAgain : CallDataT<HelloAgainRequest, HelloAgainReply>
{
public:
CallDataHelloAgain(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) : CallDataT(service, completionQueue)
{
Proceed();
}
protected:
virtual void AddNextToCompletionQueue() override
{
new CallDataHelloAgain(service_, completionQueue_);
}
virtual void WaitForRequest() override
{
service_->RequestSayHelloAgain(&serverContext_, &request_, &responder_, completionQueue_, completionQueue_, this);
}
virtual void HandleRequest() override
{
reply_.set_message(std::string("Hello again ") + request_.name());
}
};
Finally, in the GRPC server implementation, we can then handle the different requests in a unified way:
void HandleRpcs()
{
new CallDataHello(&service_, completionQueue.get());
new CallDataHelloAgain(engine_, &service_, completionQueue.get());
void* tag;
bool ok;
while (true) {
bool ret = completionQueue->Next(&tag, &ok);
if (ok == false || ret == false)
{
return;
}
static_cast<CallDataBase*>(tag)->Proceed();
}
}
This works because all the CallData we add to completionQueue, is of baseclass CallDataBase, so we can safely call Proceed() on them. This approach makes it relatively easy to add new requests. All the requests are handled by the same completion queue. However, all the requests will be handled serially, so if you are after more parallel processing, I think you have to make more than one completion queue.
I was able to figure this out.
For every protobuf function, I had to create separate completion queue.
I'm facing an error I don't really understand.
var http = new XMLHttpRequest();
var url = "http://..........";
http.open("GET", url, true);
http.onreadystatechange = function() {
console.log("readyState=" + http.readyState);
if (http.readyState == 4) {
if (http.status == 200) {
console.log("ok");
} else {
console.log("not ok");
}
}
}
http.send();
QtCreator console log:
qml: readyState=2
qml: readyState=3
ASSERT failure in QList::at: "index out of range", file C:\Qt\5.5\mingw492_32\include/QtCore/qlist.h, line 510
In Wireshark, the request is made as expected and the page correctly downloaded. What am I doing wrong?
Edit:
I am trying to use same networkaccessmanager in C++ and QML.
I have a Login class to make a connection to a website. The I need to use the same manager to make other requests in QML.
I first thought the problem was about pointer or the way I call Login(), but with Wireshark, the login request and the one made via QML have the same session id. I deduced the problem was not that, but i'm probably wrong.
main.cpp
QQmlApplicationEngine engine;
.....
QNetworkAccessManager *networkmanager = new QNetworkAccessManager();
networkmanager = engine.networkAccessManager();
Login login(networkmanager);
login.h
.....
class Login : public QObject
{
Q_OBJECT
public:
explicit Login(QNetworkAccessManager *manager, QObject *parent = 0);
.....
private:
QNetworkAccessManager *m_manager;
.....
}
login.cpp
.....
Login::Login(QNetworkAccessManager *manager, QObject *parent) : QObject(parent)
{
m_manager = manager;
connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotRequestFinished(QNetworkReply*)));
}
.....
i use QNetworkAccessManager post request to a website:
void Spider::getProducts()
{
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(getProducts(QNetworkReply*)));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setUrl(QUrl("http://www.example.com/query"));
for(int i = 0; i < categories.size(); ++i)
{
if(categories[i].isCategory())
{
isSubCategory = false;
emit manager.finished(reply);
}
else
{
for(int page_number = 0; page_number < categories[i].getPageCount(); ++i)
{
isSubCategory = true;
QJsonObject json;
json.insert("NValue", categories[i].getNValue());
json.insert("NodeId", categories[i].getNodeId());
json.insert("StoreId", categories[i].getStoreId());
json.insert("StoreType", categories[i].getStoreType());
json.insert("PageNumber", ++page_number);
json.insert("SubCategoryId", categories[i].getSubCategoryId());
QJsonDocument doc;
doc.setObject(json);
QByteArray request_body = doc.toJson();
manager.post(request, request_body);
}
}
}
}
when i run this program, at beginning, this program run normally, after running for a while, it will stop: neither terminated nor continue to run. i can not figure out why it behavior like this? is there anything that needed to be noticed when use QNetworkAccess? or i am refused by that website? ...
You are using the same QNetworkAccessManager object in a loop multiple times. It is incorrect. For each separate post request you need a separate QNetworkAccessManager object if you want to send requests in parallel. Else if you only want to use a single instance then you need to serialize your post requests by sending next post request when the previous ends in the finished slot.
Updated: Try out this code, it uses single QNetworkAccessManager object, you will need to check for correct place of i++ as per your needs
connect(&manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(getProducts(QNetworkReply*))); // add this to constructor of your program
void Spider::getProducts()
{
static int i = 0;
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setUrl(QUrl("http://www.example.com/query"));
while(categories[i].isCategory())
{
isSubCategory = false;
i++;
}
if(i == categories.size())
{
emit allPostRequestsDone();
return;
}
else
{
i++;
if(page_number < categories[i].getPageCount())
{
isSubCategory = true;
QJsonObject json;
json.insert("NValue", categories[i].getNValue());
json.insert("NodeId", categories[i].getNodeId());
json.insert("StoreId", categories[i].getStoreId());
json.insert("StoreType", categories[i].getStoreType());
json.insert("PageNumber", ++page_number);
json.insert("SubCategoryId", categories[i].getSubCategoryId());
QJsonDocument doc;
doc.setObject(json);
QByteArray request_body = doc.toJson();
manager.post(request, request_body);
}
}
}
}
So I have FINALLY gotten to the point where I can select multiple items on a ListView:
ListView {
id: lv_stuffs
horizontalAlignment: HorizontalAlignment.Fill
dataModel: _app.personDataModel //REFERENCE 1
multiSelectAction: MultiSelectActionItem {
}
multiSelectHandler {
actions: [
// Add the actions that should appear on the context menu
// when multiple selection mode is enabled
ActionItem {
title: "Search for stuffs"
onTriggered: {
_app.search(lv_stuffs.selectionList());
}
...
And I am sending this selection list through to my search method:
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.at(0)).firstName);//<---- THIS IS THE PROBLEM
}
I am trying to get the "Person" object out of the GroupedDataModel that originally bound to the item... and I have to say I am more than a little stumped. The person is being added to the personDataModel via a simple insert method in a database class:
personDataModel->insert(person);
and the items are then bound to the ListView in the QML (REFERENCE 1 above). The binding is all fine and the items are visible in the list. What I can't figure out is how to now extract these "Person" objects out of the QVariantList I am sent via the MultiSelectionMethod.
My person class:
Person::Person(QObject *parent) : QObject(parent){}
Person::Person(const QString &id, const QString &firstname, const QString &lastname, QObject *parent)
: QObject(parent)
, m_id(id)
, m_firstName(firstname)
, m_lastName(lastname)
{
}
QString Person::customerID() const
{
return m_id;
}
QString Person::firstName() const
{
return m_firstName;
}
QString Person::lastName() const
{
return m_lastName;
}
void Person::setCustomerID(const QString &newId)
{
if (newId != m_id) {
m_id = newId;
emit customerIDChanged(newId);
}
}
void Person::setFirstName(const QString &newName)
{
if (newName != m_firstName) {
m_firstName = newName;
emit firstNameChanged(newName);
}
}
void Person::setLastName(const QString &newName)
{
if (newName != m_lastName) {
m_lastName = newName;
emit lastNameChanged(newName);
}
}
I have been PAINFULLY following this tutorial here, https://developer.blackberry.com/cascades/documentation/ui/lists/list_view_selection.html, which conveniently stops right where my question begins.
Are you perhaps looking for the value function?
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.value(0)).firstName);
}
(syntax of the extraction of the firstName value may not be right there, depends on your implementation)
Your Person class will be stored in a QVariant. To accomplish this, Qt has to generate some code specific to your class. It can be done by adding this right after your class definition, in the header file for example: Q_DECLARE_METATYPE(Person). You can read more about this here: http://qt-project.org/doc/qt-4.8/qmetatype.html#Q_DECLARE_METATYPE
Now, to extract the value as a Person object, you can use QVariant::value<T>() (http://qt-project.org/doc/qt-4.8/qvariant.html#value):
alert(list.at(0).value<Person>().firstName());
I have the following code, and I would like to add some HTTP header info along with the call. Anyway that I can do that?
void NeoAPI::call(QString apiCall) {
if (this->ApiCall.contains(apiCall)) {
QNetworkAccessManager* manager = new QNetworkAccessManager(0);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(netReplyFinished(QNetworkReply*)));
QUrl url = this->ApiCall[apiCall];
url.addQueryItem("memberid","76710"); // Set for backdoor debugging
manager->get(QNetworkRequest(url));
} else {
this->requestResultText = QString("Call %1 doesn't exist").arg(apiCall);
}
}
void NeoAPI::netReplyFinished(QNetworkReply *netReply) {
if (netReply->error() == QNetworkReply::NoError) {
this->requestResultText = netReply->readAll();
} else {
this->requestResultText = "API Call Failed";
}
QMessageBox messageBox;
messageBox.setText(this->requestResultText);
messageBox.exec();
//delete netReply;
}
Also, if I wasn't using these inside a class, what would the this in the connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(netReplyFinished(QNetworkReply*))); be?
Thanks!
Yes, see the documentation of QNetworkRequest.
You'll want to do something like:
QNetworkRequest request(url);
request.setHeader( QNetworkRequest::ContentTypeHeader, "some/type" );
request.setRawHeader("Last-Modified", "Sun, 06 Nov 1994 08:49:37 GMT");
manager->get( header );
Also, if I wasn't using these inside a
class, what would the this in the
connect(manager,
SIGNAL(finished(QNetworkReply*)),
this,
SLOT(netReplyFinished(QNetworkReply*)));
be?
It wouldn't be anything. To connect a signal to a slot, that slot must be a member function of some object. The Qt primer on signals and slots explains this.