PDA

View Full Version : Qhttp



incapacitant
14th May 2006, 17:04
I have the QT4 example network.http working. I mean I press download and it does it.
But I want to call the download directly from the code without going through this button.

So I edited the example to remove the download button and call directly as follows :


quitButton = new QPushButton(tr("Quit"));
// downloadButton = new QPushButton(tr("Download"));
quitButton->setDefault(true);

http = new QHttp(this);

connect(http, SIGNAL(requestFinished(int, bool)),
this, SLOT(httpRequestFinished(int, bool)));
connect(http, SIGNAL(dataReadProgress(int, int)),
this, SLOT(updateDataReadProgress(int, int)));
connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
// connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadFile()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));

QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
// buttonLayout->addWidget(downloadButton);
buttonLayout->addWidget(quitButton);

QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);

setWindowTitle(tr("HTTP"));

QString url = "http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit";
downloadFile(url);
}

void HttpWindow::downloadFile(QString mUrl)



All works well, the url is called, the download is done and the remaining Quit button screen is briefly displayed, then it crashes.

It looks like it is the return to the main event loop that aborts but why ?

jpn
14th May 2006, 19:24
void HttpWindow::showEvent(QShowEvent* e)
{
QDialog::showEvent(e);
QString url = "http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit";
downloadFile(url);
}

incapacitant
15th May 2006, 12:53
My main program is now reduced to :



HttpWindow::HttpWindow(QWidget *parent)
: QDialog(parent)
{

http = new QHttp(this);

connect(http, SIGNAL(requestFinished(int, bool)),
this, SLOT(httpRequestFinished(int, bool)));
connect(http, SIGNAL(dataReadProgress(int, int)),
this, SLOT(updateDataReadProgress(int, int)));
connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
this, SLOT(readResponseHeader(const QHttpResponseHeader &)));

QShowEvent *e;
showEvent(e);
}

void HttpWindow::showEvent(QShowEvent* e)
{
QDialog::showEvent(e);
QString url = "http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit";
downloadFile(url);
}


The result is a bit similar, but I may misuse your code. For instance I have a compile warning :


19 C:\root\dev\Qt\test\http\http\httpwindow.cpp [Warning] 'e' might be used uninitialized in this function


The application is executed, the url called, the file downloaded but again when it comes back to what I assume is the main loop it aborts.

jpn
15th May 2006, 13:29
Don't call the showEvent() yourself. It gets called when the HttpWindow dialog is shown.

The event loop is not yet running by the time when the constructor of the HttpWindow dialog is called. That's why I moved the piece of code which starts the downloading automatically, to the show event.

incapacitant
15th May 2006, 13:53
so now I have :


HttpWindow::HttpWindow(QWidget *parent)
: QDialog(parent)
{

http = new QHttp(this);

connect(http, SIGNAL(requestFinished(int, bool)),
this, SLOT(httpRequestFinished(int, bool)));
connect(http, SIGNAL(dataReadProgress(int, int)),
this, SLOT(updateDataReadProgress(int, int)));
connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
this, SLOT(readResponseHeader(const QHttpResponseHeader &)));

}

void HttpWindow::showEvent(QShowEvent* e)
{
QDialog::showEvent(e);
QString url = "http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit";
downloadFile(url);
}


The result is still not perfect. The new thing is that a big empty window is displayed, that I could do without. And when the downoload is completed it still crashes.

wysota
15th May 2006, 15:58
So if you don't need a window, why do you create one?


QApplication app(argc, argv);
QHttp http;
QFile file("writetothisfile");
file.open(QIODevice::WriteOnly);
connect(&http, SIGNAL(done(bool)), &app, SLOT(quit()));
http.get("http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit", &file);
return app.exec();

Of course you can connect something else than quit() to this signal :)

jpn
15th May 2006, 17:00
I somewhy have a feeling that you simply want to show the amount of credits in a way or another...

Here's for example a line edit which does the job:


#include <QApplication>
#include <QLineEdit>
#include <QHttp>
#include <QUrl>
#include <QBuffer>

class Credits: public QLineEdit
{
Q_OBJECT

public:
Credits(QWidget* parent = 0): QLineEdit(parent)
{
setReadOnly(true);

// target url
QUrl url("http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit");

http = new QHttp(this);
connect(http, SIGNAL(done(bool)), this, SLOT(showCredits(bool)));
// remember to set the host
http->setHost(url.host());

// the resulting data will be stored in the buffer
buffer = new QBuffer(this);
http->get(url.toString(), buffer);
}

private slots:
void showCredits(bool error)
{
if (error)
setText(http->errorString());
else
setText(QString(buffer->data()));
}

private:
QHttp* http;
QBuffer* buffer;
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Credits c;
c.show();
app.connect(&app, SIGNAL(lastWindowClose()), &app, SLOT(quit()));
return app.exec();
}

#include "main.moc"

incapacitant
17th May 2006, 13:26
wysota:

I used your sample to write this main.cpp :


#include <QApplication>
#include <QtGui>
#include <QtCore>
#include <QtNetwork>
#include <QDialog>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QHttp http;
QFile file("writetothisfile");
file.open(QIODevice::WriteOnly);
connect(&http, SIGNAL(done(bool)), &app, SLOT(quit()));
http.get("http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit", &file);
return app.exec();
}


and I get a compile error :


13 C:\root\dev\Qt\test\http\http-wysota\main.cpp `connect' undeclared (first use this function)

The background is that within an application that has windows I try to call urls (I try this one because it is the easiest) but I want nothing to be displayed during the call and I want to call the url without a button.

Meanwhile I will look at jpn's proposal...

incapacitant
17th May 2006, 13:45
wysota:
I replaced :


connect(&http, SIGNAL(done(bool)), &app, SLOT(quit()));


with



app.connect(&http, SIGNAL(done(bool)), &app, SLOT(quit()));

and it compiles.

Then it runs but I doubt that the url is called as usually I am warned by Norton Internet Security that the code is accessing the outside. Anyway the file is created but empty.

incapacitant
17th May 2006, 13:51
jpn:
Your code works wonderfully. I will try to adapt it to my needs.
For information what is the reason of this line :


#include "main.moc"

jpn
17th May 2006, 14:37
I was just lazy and didn't want to add separate header and implementation files for the Credits class. By including "main.moc", qmake knows to run moc on the main.cpp file. And moc'ing is required because the class uses signals and slots.

wysota
17th May 2006, 15:03
Then it runs but I doubt that the url is called as usually I am warned by Norton Internet Security that the code is accessing the outside. Anyway the file is created but empty.

Maybe it doesn't know how to extract all data from the url. Use QHttp::setHost to set the server address and port and then use the rest of the url as an argument to get().

My point was to show you that you don't need any windows to use QHttp.

incapacitant
17th May 2006, 15:17
jpn:
People say you should first learn C++, then QT. I did not. I will register to a university this fall to learn C++. I say that because I can't really understand your code structure because I don't know enough of C++.


Anyway I could extract what I need to make it work. I still have a problem though.

First from my main code I call :


qS = testUrl();

It fills in qS that I can put on the first window that I display.

testUrl() is :


QString ApplicationWindow::testUrl()
{
// target url
QUrl url("http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit");

http = new QHttp(this);
connect(http, SIGNAL(done(bool)), this, SLOT(defineData(bool)));
// remember to set the host
http->setHost(url.host());

// the resulting data will be stored in the buffer
buffer = new QBuffer(this);
http->get(url.toString(), buffer);
QMessageBox::warning( this, "Http", QString(buffer->data()) );
return QString(buffer->data());
}


defineData is just a return statement.

As listed above it works fine, first by displaying this QMessageBox that I put there for tracing what was return. By the way allthough the right value is returned the MessageBox does not display the text !!

The weird thing is that is I comment out the QMessageBox call then it does not work.

What I see when it works:
the message box with title Http but no value (?) and a OK button, I click
my first window with this credit value correctly retrieved.

What I see when it does not work (ie I comment out the QMessageBox)
my first window with an empty line and no credit value.

From day one when I started with Qhttp I noticed this kind of behaviour where the code is not executed sequentially.

jpn
17th May 2006, 15:42
As listed above it works fine, first by displaying this QMessageBox that I put there for tracing what was return. By the way allthough the right value is returned the MessageBox does not display the text !!
You cannot do it like this. QHttp::get() is asynchronous and does not cause the data to be stored in the buffer right away. QHttp offers a bunch of signals, which are emitted when something significant has happened. Basically there can't be a function which returns the data result of an http request. You will have the data by the time a slot connected to for example QHttp::done(bool) signal gets called. As you can see from my example, the resulting credits text is set in the slot connected to the QHttp::done(bool), not for example in the constructor.


The weird thing is that is I comment out the QMessageBox call then it does not work.
It "works" with the message box only because the http request may be (but not necessarily) finished meanwhile the message box is shown.


From day one when I started with Qhttp I noticed this kind of behaviour where the code is not executed sequentially.
QHttp::get() (http://doc.trolltech.com/4.1/qhttp.html#get):

The function does not block and returns immediately. The request is scheduled, and its execution is performed asynchronously.
To state it one more time, you will get notified by a signal when it's done... ;)

incapacitant
17th May 2006, 16:55
jpn

Thank you for all your good advice. Since http completes when the slot is activated, I moved the rest of my main code to the http slot and it works fine.

It looks like cascading down, moving the the main program down from slot to slot.

incapacitant
18th May 2006, 18:27
New issue :

This works :


QString mUrl;
mUrl = "http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa&action=credit";
QUrl url(mUrl);


This does not work (ie the slot when activated returns error)


QString mUrl;
mUrl = QString("/index.php?login=%1&pass=%2&action=%3")
.arg( Login, Password, Action );
QUrl url(mUrl);


This also does not work (ie url is called but server says invalid password)


QString Password = "aaaaaa";
QString mUrl;
mUrl = "http://api.smsbox.fr/index.php?";
mUrl.append("login=ahcoeur&pass=");
mUrl.append(Password);
mUrl.append("&action=credit");
QUrl url(mUrl);


My url contains variables. I used a constant for the test and now I want to insert my parameters. I tried various combinations but nothing works so far.

jpn
18th May 2006, 21:01
Does this work?


QUrl url("http://api.smsbox.fr/index.php");
url.addQueryItem("login", Login);
url.addQueryItem("pass", Password);
url.addQueryItem("action", Action);

incapacitant
18th May 2006, 21:52
jpn:

No, I get the url through but the server replies invalid password or login.
So the parameters are not inserted as they should.

The only case when when it works is when I use QUrl url("blah...blah...blah");
Only a string of characters between quotes.

jpn
19th May 2006, 06:31
Are you 100% sure about the user/passwd/act arguments' correctness? Did you try to output the resulting url?



#include <QtDebug>
...
QUrl url(..);
url.addQueryItem(...);
qDebug() << url;

incapacitant
19th May 2006, 09:15
I tried qDebug() but I don't know where the output goes... I ran it from a DOS window and nothing is displayed.

So I tried this :


QUrl url("http://api.smsbox.fr/index.php?login=ahcoeur&pass=aaaaaa");
// url.addQueryItem("login", Login);
// url.addQueryItem("pass", Password);
url.addQueryItem("action", Action);



And this works "Action" seems to be appended correctly.
Then I tried :


QUrl url("http://api.smsbox.fr/index.php?login=ahcoeur")
// url.addQueryItem("login", Login);
url.addQueryItem("pass", Password);
url.addQueryItem("action", Action);


The url goes through and the server replies invalid password or login. Strange.

I would like to display the url with QMessageBox but first I need to find how to convert a QUrl to QString :)

incapacitant
19th May 2006, 09:20
I converted QUrl with toString() and displayed it and its string conversion shows a correct text. The url converted to string is correct, all characters are there.
However the server does not accept it.

I'm stuck.

incapacitant
19th May 2006, 09:24
Oops found the problem.
The call with the constant works because the parameters are as constants.
As variables I was taking the values from an ini file where I had put an invalid pass and login for testing.

I am stupid, thanks a bunch.