Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for a SLOT to finish

I use QNetworkAccessManager to do form POST.

I have connected signals and slots as:

connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(readCookies(QNetworkReply*)));

Now, I make a request by doing:

manager->post(request,postData);

Now readCookies(QNetworkReply *) will be run as soon as SIGNAL is emitted. Now, using the Cookies which I get in this slot, I have to make one more POST..

As signals & slots are asynchronous, I want to wait till I get the cookies from my first POST and then I again want to do another post using the cookies I got in first POST like

//Setting new request, headers etc...
manager->post(request2,postData2);

I want the later to always be executed after first one has executed (so that I get proper cookies value).

What is the way to go? I am new to all these SIGNALS & SLOTS so please bear with me.

like image 475
Abhijeet Rastogi Avatar asked Feb 26 '23 13:02

Abhijeet Rastogi


2 Answers

You can do the post in your readCookies() slot:

void readCookies( QNetworkReply* reply ) {
    if ( ...error? ) {
        report error...
        return;
    }

    ...
    manager->post(request2,postData2);
}

I will be called when the cookies is read, and you can then continue with your post. Connect that to a second slot, and so on. Managing multiple, possibly parallely running asynchronous operations like this can become errorprone though, if you manage many of them in a single object. I would suggest to use the Command Pattern - here I described why I find it extremely useful in exactly this context. The sequence of request and asnychronous operations is encapsulated in a single object (abbreviated, with some pseudo-code):

class PostStuffOperation : public QObject {
    Q_OBJECT
public:
    enum Error {
       NoError=0,
       Error=1,
       ...
    };

    Error error() const; //operation successful or not?
    QString errorString() const; //human-readable error description

    ... setters for all the information the operation needs
    ...
    void start() {
       ...start your first request and connect it to cookiesRead 
    }

public Q_SLOTS:
    void cookiesRead( QNetworkReply * ) {
    if ( error ) {
       // set error and errorString...
       emit finished( this ); //couldn't read cookies, so the operation fails
       return;
    }
    ... do post
 }

 void postFinished( QNetworkReply* ) {
     if ( error ) {
         // set error and errorString...
     }

    emit finished( this ); //post finished - that means the whole operation finished
 }
Q_SIGNALS:
    void finished( PostStuffOperation* );
};

To start the operation, you do

PostStuffOperation op* = new PostStuffOperation( this );
... pass data like server, port etc. to the operation
connect( op, SIGNAL(finished()), this, SLOT(postOperationFinished()) );
op->start();

void postOperationFinished( PostStuffOperation* op ) {
    if ( op->error != PostStuffOperation::NoError ) {
        //handle error, e.g. show message box
    }
}

It makes sense to have a common baseclass for such operations, see e.g. KDE's KJob.

like image 146
Frank Osterfeld Avatar answered Mar 08 '23 01:03

Frank Osterfeld


You can connect a signal from this to a slot from your manager and emit the signal after reading the cookies. By example:

connect(this, SIGNAL(cookiesRead()), manager, SLOT(PostAgain());

So your readCookies function will be:

{
   // Read cookies
   emit cookiesRead();
}

Of course you can send all data you want form signal to slot.

Hope that helps

like image 20
Patrice Bernassola Avatar answered Mar 08 '23 01:03

Patrice Bernassola