PDA

View Full Version : NEED HELP: How to download file without breaking function



codeslicer
4th February 2008, 00:36
Ok, so this is my situation: I need to download a file from a server, and when it's done, read it or open it using QFile, QIODevice, etc. I saw the tutorial in the documentation, but it is simple and doesn't show how to do something with the file afterwards. The problem is, get() returns immediately and any attempt and reading the file afterwards results in a blank. Following the tutorial involves breaking the function, creating a done() signal, and connecting it to a function which will read the file/download a new one.

WHAT I AM ASKING: How do I download a file without having to exit the current function? Do I need to make a CPU-intensive while() loop which will continiously check if the download is done, or is there a different way which I have not found out yet?

If you're wondering why I need this, it's to create and updater which will check the server for a new version of the program (by downloading a file with version info, any alternative ways to transmit such small information will be helpful), then comparing it to the current version, and finally downloading the latest version.

Again, I'm using QHttp, and I DON'T want to use slots because I would end up with a bunch of confusing signals and slots leading to the next step. Instead, I want a clean "downloadFile()" function which returns a pointer to the file or a bool if it succeeds and only after the file is downloaded, i.e. redo the get() function to return only after it's done downloading.

Anyways, thanks in advance =] - codeslicer :p

jgvaldecasas
4th February 2008, 08:02
well, AFAIK, you will need to use slots.

You need to know that all your data has been received before trying to read it, that's why you need to wait for correct signal to be emitted.

If you do something like:

http->setHost("yourhost");
http->get("yourfile","your QIODevice");

Then you will need to connect the done() function with a slot that will read the data stored in your device and decide whether to read another file, download that version or whatever you want to do.

codeslicer
4th February 2008, 11:42
Ok thanks... but it's not possible to make a while() loop that will continiously check for some isDone() thing? Having millions of slots all connected to each other is confusing. Thanks for answering though :cool:

jgvaldecasas
4th February 2008, 12:13
But you are not going to have millions of slots. Just one slot. If you name your slot correctly, it will be quite straight forward to undestand the code.

IMHO, trying to solve this with a while loop is much more complex and requires more code lines than connecting the done() signal with a httpCommandFinished() slot. After all, you want to keep it simple, isn't it?

codeslicer
4th February 2008, 21:11
Ok... so I have to do this:

1.) Create a slot for the updateButton clicked() signal
2.) In that slot make a QHttp object called getVersionInfo and use get() to retrieve the version file. Exit the slot and enter the event loop.
3.) Connect the done() signal from getVersionInfo to a slot which will read the file, and determine if downloading the latest version is necessary or not
4.) If so, create a second QHttp object called getNewUpdate and again use get() to retrieve the installer. Again exit the slot and enter the event loop.
5.) Connect the done() signal from getNewUpdate object to another slot which will execute the installer, terminate the program, etc

If that is the only possible way to download a file, do something with it, and continue with other commands (In other words that it isn't possible to just create a downloadFile() function which will return only after the file is download, or else return an error message) then how would I go into implementing a downloadFile class?

Sorry, I'm a bit new to Qt and C++ (Only ~1/2 year) and used to the noob-friendly enviroment of Visual Basic .NET. The reason I quit was that it wasn't portable, required a huge framework, and wasn't *nix friendly.

If I get could some help, I would be very grateful. Thanks in advance - codeslicer

pherthyl
4th February 2008, 22:07
An alternative is to use http://www.libqxt.org/docs/classQxtSignalWaiter.html from the Qxt library, which should allow you to do this without multiple slots.

codeslicer
5th February 2008, 00:34
Ok... I tried it but I get lots of different errors =[ I guess I have to have multiple blocks of signals and slots that wait for each other :( What about while() loops though. Could those be used instead?

Also, can QFile read a file from the internet, for example without downloading it to a tmp folder then reading it from there, but instead somehow directly reading it from the server?

jgvaldecasas
5th February 2008, 20:53
You can read the file from internet using Qhttp::get() function. The second parameter is the QIODevice where you want to write the info, and QFile is an IODevice ;)

I never used http protocol, but, can't you request the current version number using this protocol and if the version mismatch, download the file?

I've read the doc a bit more and seems that you just need one slot. Let's call it HttpRequestFinished().

Connect the requestFinished() signal with this slot. Once the file is loaded, this signal will be emited and you can do whatever checks/operations you may want.

this is a draft example


connect(http,SIGNAL(requestFinished(int,bool)),thi s,SLOT(httpRequestFinished(int, bool)));
http->setHost("yourhost");
id1 = http->get("versionInfo");
... //whatever code you have
void httpRequestFinished(int id, bool error){
//check for errors if you want
if (id == id1){
//check here if the version number is right, if not, do id2 = http->get("thefile",yourQFile)
}
if (id == id2){
//you got your file and it's in your QFile.. do what you have to do with it
}
}


This should work, and means using just one slot. Connected to the right signal.

Check QHttp doc to find out how you could optimize the code in your case.

My 2 cents.

codeslicer
6th February 2008, 11:37
Ok thanks. First I'm gonna try to do the while loop so I can just make a downloadFile() function, but if that doesn't work, I'll try slots like you said. Thanks =]:o

stevey
19th April 2008, 11:42
1.) Create a slot for the updateButton clicked() signal
2.) In that slot make a QHttp object called getVersionInfo and use get() to retrieve the version file. Exit the slot and enter the event loop.
3.) Connect the done() signal from getVersionInfo to a slot which will read the file, and determine if downloading the latest version is necessary or not
4.) If so, create a second QHttp object called getNewUpdate and again use get() to retrieve the installer. Again exit the slot and enter the event loop.
5.) Connect the done() signal from getNewUpdate object to another slot which will execute the installer, terminate the program, etc


No.

1.) Create a slot for the updateButton clicked() signal
2.) In that slot make a QHttp object called getVersionInfo, connect requestFinished(int, bool) signal to some slot you define.
3.) Set the host and user / pass if needed.
4.) Use get() passing in file uri to download. I also recommend passing in an opened QFile object as the second argument, then the request will automagically save the file data for you. (I don't understand why you're talking about event loops at this point). Assign the return integer value to a member variable which you can compare in your requestFinished(int, bool) receiver slot, say m_fileGetId.
The way the QHttp works is you can feed in loads of requests and they are processed in a queue which QHttp manages internally. At some point in future the requestFinished signal will emit passing the id return for the request as the first parameter, so you need to do something like:


void Downloader::fileDownloadRequestFinished(int id, bool error)
{
if(id == m_fileGetId)
{
m_file.close();

}
}

Bear in mind that that m_http->setHost(...) call actually returns and id and so do the user / pass setting functions so they count as requests which will emit the signal which is why you need to keep track of the id of the FILE request.



"How do I download a file without having to exit the current function? Do I need to make a CPU-intensive while() loop which will continiously check if the download is done, or is there a different way which I have not found out yet?"

Let it return, what's the problem, the download will be happening in the background and when the above slot is called and the id matches the get id, then you know that the download is complete, so open it. Here's an extended version of the above example:


void Downloader::fileDownloadRequestFinished(int id, bool error)
{
if(id == m_fileGetId)
{
m_file.flush();
m_file.close(); // Flushes and closes the file ready for opening.

if(m_file.open(QIODevice::ReadOnly))
{
// load the content

m_file.close();
}

}
}


You need to realise that signal / slot connections make your job much simpler, dont' be afraid of them.

The unfortunate thing about QHttp is that occasionally you may want to do job synchronously, but it's not supported by QHttp, but hey, it's a workable situation.
If you want to hold up your ui so nothing can be done until the file is downloaded, you can put a progressbar on your ui or modal dialog and do this:


connect(m_http, SIGNAL(dataReadProgress(int, int)), this, SLOT(SetProgress(int, int)));

void Downloader::SetProgress(int bytesRead, int totalBytes)
{
ui.progressBar->setMaximum( totalBytes );
ui.progressBar->setValue( bytesRead );
}


If you do the progressbar in a modal dialog, then guess what, you need another slot. You could define a slot on the modal progress ui which subscribes to the progressbars valueChanged ( int value ) signal, which then analyses the value of the progressbar. If it's at 100% (current value == max) then calls this->close().
There are other ways of approaching it but it's up to you.

BUT, if you insist on a tight while loop, then put some kind of thread "sleep for 100 ms" call in so you don't destroy the PC's performance.

stevey
22nd April 2008, 04:10
Something else you could also do is start a new thread and get it to do the get request, but setup in a QMutex and QWaitCondition in advance say m_getMutex and m_waitCond, then just after starting the thread, call m_waitCond( &m_getMutex). In your get request thread once the requestFinished slot is called and you've closed you QFile object, call m_waitCond.wakeAll() then you main thread will only continue once the download is done.