PDA

View Full Version : Why does this not work?



lnxusr
16th December 2009, 18:15
I'm trying to get the download example application to work in my application to download a file from a web site. As you can see, I've made only slight modifications to get it to work without errors, but when it returns to my main class, there is no file.


#ifndef GETHTFILE_H
#define GETHTFILE_H

#include <QCoreApplication>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QStringList>
#include <QTimer>
#include <QUrl>

#include <stdio.h>

class gethtfile: public QObject
{
Q_OBJECT
QNetworkAccessManager manager;
QList<QNetworkReply *> currentDownloads;

public:
gethtfile();
void doDownload(const QUrl &url);
QString saveFileName(const QUrl &url);
bool saveToDisk(const QString &filename, QIODevice *data);

public slots:
void execute();
void downloadFinished(QNetworkReply *reply);
};

#endif // GETHTFILE_H



#include "gethtfile.h"

#include <stdio.h>

gethtfile::gethtfile()
{

connect(&manager, SIGNAL(finished(QNetworkReply*)),
SLOT(downloadFinished(QNetworkReply*)));

execute();
}

void gethtfile::doDownload(const QUrl &url)
{
QString tst = url.path();
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
currentDownloads.append(reply);
}

QString gethtfile::saveFileName(const QUrl &url)
{
QString path = url.path();
QString basename = QFileInfo(path).fileName();

if (basename.isEmpty())
basename = "download";

if (QFile::exists(basename)) {
// already exists, don't overwrite
int i = 0;
basename += '.';
while (QFile::exists(basename + QString::number(i)))
++i;

basename += QString::number(i);
}

return basename;
}

bool gethtfile::saveToDisk(const QString &filename, QIODevice *data)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
fprintf(stderr, "Could not open %s for writing: %s\n",
qPrintable(filename),
qPrintable(file.errorString()));
return false;
}

file.write(data->readAll());
file.close();

return true;
}

void gethtfile::execute()
{
QUrl url;
url.setUrl("http://www.google.com/robots.txt");
doDownload(url);

}

void gethtfile::downloadFinished(QNetworkReply *reply)
{
QUrl url = reply->url();
if (reply->error()) {
fprintf(stderr, "Download of %s failed: %s\n",
url.toEncoded().constData(),
qPrintable(reply->errorString()));
} else {
QString filename = saveFileName(url);
if (saveToDisk(filename, reply))
printf("Download of %s succeded (saved to %s)\n",
url.toEncoded().constData(), qPrintable(filename));
}

currentDownloads.removeAll(reply);
reply->deleteLater();

if (currentDownloads.isEmpty())
// all downloads finished
QCoreApplication::instance()->quit();
}


I've been tearing my hair out for three days trying to use QT to get the file. Why is it so hard to use QHTTP and/or QNetworkAccessManager? All I need is a simple way to get a small text file from a web site. I can easily do it with libcurl, but I don't want to add an external library when QT can (or should) be able to do it.

wysota
16th December 2009, 18:52
You are forgetting these classes work in an asynchronous manner. When your code returns the data will not have been downloaded yet. If you want blocking semantics, you need to provide it yourself, i.e. using QEventLoop.

lnxusr
16th December 2009, 19:36
Thank you wysota, I added the QEventLoop to the doDownload function like so:


void gethtfile::doDownload(const QUrl &url)
{
QEventLoop loop;
QString tst = url.path();
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
connect(reply, SIGNAL(downloadFinished(QNetworkReply*)), &loop, SLOT(quit()));
// currentDownloads.append(reply);
loop.exec();
}


I commented out the append line as we'll only be getting one file at a time anyway. It works, but I get the following when I run:

Object::connect: No such slot QEventLoop::downloadFinished(QNetworkReply*) in src/gethtfile.cpp:24

I tried both downloadFinished(QNetworkReply*) and finished(QNetworkReply*) and both give the error (warning?) in the console.

What causes that, and how can I prevent it?

QT really does need a simple QHttpGetSingleFileWithNoThreading type function so we don't need to write 100+ lines of code for a simple one time small download.

wysota
16th December 2009, 20:22
It works, but I get the following when I run:

Object::connect: No such slot QEventLoop::downloadFinished(QNetworkReply*) in src/gethtfile.cpp:24

I tried both downloadFinished(QNetworkReply*) and finished(QNetworkReply*) and both give the error (warning?) in the console.

What causes that, and how can I prevent it?
There is no such slot in QEventLoop, right? So the message is obvious.


QT really does need a simple QHttpGetSingleFileWithNoThreading type function so we don't need to write 100+ lines of code for a simple one time small download.
There is no threading involved here. Qt just works in an asynchronous way and you need to accept it. It's like you complained because a dog barked while you were used to the fact that animals made different voices just because you always had cats.


QNetworkReply *reply = manager->get(QNetworkRequest(...));
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
QTimer timer;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); // just in case
timer.start(5000);
loop.exec();
QByteArray result = reply->readAll();
No 100 lines of code...

lnxusr
17th December 2009, 05:39
There is no such slot in QEventLoop, right? So the message is obvious.

Well yeah, to those who know what they're doing.. ;/

I don't get the error the first time I used up top. Would thedownloadFinished(QNetworkReply*) slot not still be valid, or can you only use them once?


There is no threading involved here. Qt just works in an asynchronous way and you need to accept it. It's like you complained because a dog barked while you were used to the fact that animals made different voices just because you always had cats.

Don't let my cats here you talking about dogs...

I'm writing this app to learn not only QT, but C++ as well. It'll take me a while to get the hang of the OO world. I've ordered a couple of books to help out. QT's documentation is not easy to follow if you don't know the basics of C++, but I'm still working on the app anyway. It's a mess, but things are working. I'll clean up the code as I learn different things.

Thanks for that snippet, it cleaned the class down to around 40 lines (including all the #includes). I just added six lines to save the data to a file. Most of the stuff in the download example wasn't needed, and I would have cleaned it out further, but nowhere near to the eight lines you gave.

wysota
17th December 2009, 08:38
Well yeah, to those who know what they're doing.. ;/
Or maybe to those that can read English :)


I don't get the error the first time I used up top. Would thedownloadFinished(QNetworkReply*) slot not still be valid, or can you only use them once?
I completly don't understand wha tou mean.



I'm writing this app to learn not only QT, but C++ as well.
If you want to learn QT you have to go to Appple forums. Here you can only learn Qt.


It'll take me a while to get the hang of the OO world.
OO has nothing to do with asynchronous execution.


QT's documentation is not easy to follow if you don't know the basics of C++, but I'm still working on the app anyway.
You should really learn C++ first before taking on Qt. You'll save yourself lots of time.

By the way, if all you wanted was to save the download to a file, you might have done it in much shorter code.


QHttp *http = new QHttp(this);
http->setHost(...);
QFile *file = new QFile("...");
file->open(QFile::WriteOnly);
http->get("...", file);
And that's it.

Alternatively with QNetworkAccessManager:

QNetworkReply *reply = manager->get(...);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(saveToFile(QNetworkReply*)));
//...
void ThisClass::saveToFile(QNetworkReply *reply) {
QFile f(...);
f.open(...);
f.write(reply->readAll());
}

lnxusr
17th December 2009, 22:48
I completly don't understand wha tou mean.

In my original post. The first statement of the gethtfile class is


connect(&manager, SIGNAL(finished(QNetworkReply*)),
SLOT(downloadFinished(QNetworkReply*)));

I don't get an the warning message from this, but did when I connect again in the event loop in doDownload.


You should really learn C++ first before taking on Qt. You'll save yourself lots of time.

I know, but until I get the books I ordered, I'll still play around with my app. I have nothing else to work on right now, and it fills my free time. It may be frustrating, but I am slowly figuring things out. Like I said before, things work, but it's nowhere near usable. It's more of a hodge podge of classes and functions for testing and seeing how things work. I don't know if you can really call it an app or not. I'll most likely start from scratch or rewrite 95% of what I've done, but I am learning a few things.

While searching the forums, I see alot of people saying QHttp is depreciated and to use QNetworkManager. That's why I moved away from it. It sounds like, in the future (5.0?) Qt will do away with QHttp.


By the way, if all you wanted was to save the download to a file, you might have done it in much shorter code.

Thanks for the snippets, I'll add them to my notes..

wysota
18th December 2009, 00:00
I have nothing else to work on right now, and it fills my free time.
I suggest reading these:

http://www.janiry.com/bruce-eckel/TICPP-2nd-ed-Vol-one.zip
http://www.janiry.com/bruce-eckel/TICPP-2nd-ed-Vol-two.zip

lnxusr
18th December 2009, 18:53
I suggest reading these:

http://www.janiry.com/bruce-eckel/TICPP-2nd-ed-Vol-one.zip
http://www.janiry.com/bruce-eckel/TICPP-2nd-ed-Vol-two.zip


Great! Thanks wysota,

I read the preface and scanned through the first volume last night. Looks pretty well organized, and I think it may be just what I need. Awesome reviews on Amazon as well. I may see about getting hard copies from Abebooks.

The second volume has some macro's in it. I have OpenOffice set to disable macros, are they required to use the book? Also not sure if word macros will work properly in OOo.