PDA

View Full Version : Waiting for a download event



merajc
26th January 2011, 19:15
Hello

I'm new to programming, and this is my first Qt app.

I'm trying to download an XML file, and then parse it. The problem is that while downloading the XML, and waiting for the finished() signal, it begins parsing - but that means it's parsing nothing at all.

I need the XML to save first, and then the app opens it, and then it parses.


//creating new xmlReader
xml = new xmlReader(externalSettings);

The above is initialising a pointer to the xmlReader which downloads the XML file, and saves it. It's an overloaded constructor and 'externalSettings' is the URL of the XML file.


//getting number of images from the XML file
int numberOfImages = xml->getNumImages();

Once the XML file is downloaded, this function should get data from the downloaded XML file. But it does so before it is downloaded.

The second time I run the program, everything is fine, but that's because the XML file was downloaded during the first run.

I hope I'm being clear. My code is probably ugly, but I hope to get used to good programming practices soon.

Thank you

tbscope
26th January 2011, 20:12
There are not enough details to help you.
Obviously, you want to wait for some kind of finished signal (as you mention). I think that this would be trivial to implement. The problem is that we don't see how you implement the downloading part so it's not easy to help in detail.

merajc
26th January 2011, 20:43
I'm sorry about that. Here are some excerpts which should be relevant. If you would like to see the whole program, I can attach it.

Below is what happens when the user pressed the load button. The XML constructor executes (downloading and saving file), following by getNumImages() to see how many images there are. The rest works fine.


void WallpaperSelection::on_loadButton_clicked()
{
//ui->loadButton->setEnabled(false);

//creating new xmlReader
xml = new xmlReader(externalSettings);

//getting number of images from the XML file
howMany = xml->getNumImages();

//outputting number of images
QString numImages = QString::number(howMany);
qDebug(numImages.toAscii());

QString uRL, name;

//setting image objects
image = new imageEncapsulation[howMany];

//loop gets each image's name and URL
for(int i = 0; i < howMany; i++) //
{
qDebug("entered images loop");

//sets image URL and name. Parameters passed by reference
xml->xmlData(uRL, name, i);
qDebug("requested image");

//now with the paremeters sent back, we set the object variables
image[i].setimageName(name);
image[i].setimageURL(uRL);

//outputting newly-acquired information
qDebug("name is " + name.toAscii() + " url is " + uRL.toAscii());

//inserting each image name into the list widget. Only text, no other info
ui->listWidget->insertItem(i,image[i].imageName());
}
}

Overloaded constructor for xmlReader:

xmlReader::xmlReader(QString& newURL) //overloaded constructor
{
xmlURL = newURL;

//new network access manager, when request is finished, it goes to the slow below
m_netwManager = new QNetworkAccessManager(this);
connect(m_netwManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slot_netwManagerFinished(QNetworkReply*)));

QUrl url(xmlURL.toAscii());

//getting the request
QNetworkRequest request(url);
m_netwManager->get(request);
qDebug("I'm done with the xmlReader constructor");
qDebug("I requested the URL " + xmlURL.toAscii());
}

Slot for when the image is finished() downloading:

xmlReader::xmlReader(QString& newURL) //overloaded constructor
{
xmlURL = newURL;

//new network access manager, when request is finished, it goes to the slow below
m_netwManager = new QNetworkAccessManager(this);
connect(m_netwManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slot_netwManagerFinished(QNetworkReply*)));

QUrl url(xmlURL.toAscii());

//getting the request
QNetworkRequest request(url);
m_netwManager->get(request);
qDebug("I'm done with the xmlReader constructor");
qDebug("I requested the URL " + xmlURL.toAscii());
}

void xmlReader::slot_netwManagerFinished(QNetworkReply *reply)
{
//error handling
qDebug("I've entered the request finished slot of the xmlReader");
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
reply->deleteLater();
return;
}

//redirection, if needed
QVariant redir = reply->attribute(QNetworkRequest::RedirectionTargetAttrib ute);
if (redir.isValid()) {
QUrl url = redir.toUrl();
qDebug() << "must go to:" << url;
if (url.isRelative()) {
url.setScheme(reply->url().scheme());
url.setEncodedHost(reply->url().encodedHost());
}
QNetworkRequest req(url);
m_netwManager->get(req);
reply->deleteLater();
return;
}
qDebug() << "ContentType:" << reply->header(QNetworkRequest::ContentTypeHeader).toStrin g();

//saving the image
QFile file("Settings.xml");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();

qDebug("Saved settings.xml");

reply->deleteLater();
}

And lastly, getNumImages():

int xmlReader::getNumImages()
{
QFile settingsFile("Settings.xml");

qDebug("at get Num images");

if(settingsFile.open(QIODevice::ReadOnly))
{
qDebug("opened settings file");
int temp = parseForNum(settingsFile.readAll().data());

QString tempToString = QString::number(temp);

settingsFile.close();
qDebug("there are so many images: " + tempToString.toAscii());
return temp;
}
}

Thank you

hackerNovitiate
27th January 2011, 05:00
The issue is that QNetworkManager::get() DOES NOT wait for the download to finish before returning.

Instead, it initiates the download, then returns to allow the rest of the program to continue, while waiting for the download to finish. If you want any code to run AFTER the download is finished, you need to put it in the slot.

merajc
27th January 2011, 07:49
Thank you for your help

The reason I can't put the rest of the code in the slot is that they're two different classes, and need to be separated.

Is there any way I can make the app wait by making a MessageBox pop-up and only leave after the download is finished? Or any other method that allows me to make the app wait for the download to be finished?

tbscope
27th January 2011, 08:04
You have your mind set on a synchronous connection.
While, as stated already, the get() function is asynchronous.

You want to use the finished() signal.
And you need to change the program logic to deal with this. This should not be too hard.
Basically, create a signal in your xmlReader class that fires when the networkaccessmanager finished() signal is fired. Before that, the xmlReader class can do the necessary parsing.
Then connect a slot from your WallPaperselection class to the xmlReader finished signal.
Then use some getter functions to get the needed data.

merajc
27th January 2011, 08:31
Thank you for your reply

I actually tried implementing a signal and slot yesterday to do this, but it didn't work.

Went over my Qt book and now it works great. Thanks you once again!