Asynchronous function calls in Qt

2019-07-26 06:28发布

I'm pretty new to Qt and programming and are facing a problem I can't find a solution for.

I want to read some information from an online XML file and send it to my main program.

To do so, I created a class XMLParser and added the following to the constructor:

XMLParser::XMLParser(QString searchstring)
{
    QNetworkAccessManager *manager2 = new QNetworkAccessManager(this);
    reply = manager2->get(QNetworkRequest(QUrl("http://www.boardgamegeek.com/xmlapi/search?search="+searchstring)));

    XMLParser::connect(reply, SIGNAL(finished()),
                   this, SLOT(fileIsReady()) );
}

and fileIsReady fills a QMap and stores it as a private class member.

In my second class, I call

  XMLParser *xmlpars = new XMLParser(input_gamename->text());
  QMap<QString, int> searchResults = xmlpars->getSearchList();

and getSearchList is a simple getter function.

The problem is, that getSearchList is executed before fileIsReady finished reading the XML file and returns an empty map. From what I understand, the constructor should not be finished until fileIsReady() finished its work. And thus, getSearchList() shouldn't be called early.

My two questions:

  1. Why does my programm progresses while the function didn't finish reading.
  2. How can I make the second call "getSearchList" wait?

Thanks a lot in advance!

3条回答
够拽才男人
2楼-- · 2019-07-26 06:43

How can I make the second call getSearchList wait?

You don't! Instead, just move any code that expect the XML file to be downloaded into the fileIsReady() slot that you've already defined. That way your program won't lock up while it's waiting for the download to complete (which is the entire point of asynchronous programming.)

查看更多
疯言疯语
3楼-- · 2019-07-26 06:55

So, I found a solution using QEventLoop. But as far as I read, this isn't recommendend. Are there any other solutions? And why is using QEventLoop bad habit (That's what I read from other answers here StackOverflow).

XMLParser::XMLParser(QString searchstring)
{

QNetworkAccessManager *manager2 = new QNetworkAccessManager(this);
reply = manager2->get(QNetworkRequest(QUrl("http://www.boardgamegeek.com/xmlapi/search?search="+searchstring)));

QEventLoop loop;
XMLParser::connect(reply, SIGNAL(finished()),
                   this, SLOT(fileIsReady()) );
XMLParser::connect(this, SIGNAL(finishedReading()),
                   &loop, SLOT(quit()));
loop.exec();

}
查看更多
兄弟一词,经得起流年.
4楼-- · 2019-07-26 06:58

First, you need to understand the fundamental concept of signals and slots.

After you make a connection, the slot will get called every time the signal is emitted.

The connect() functions returns after connecting the signal to the slot. It doesn't wait for the signal to be emitted.

In your XMLParser constructor, your connect() function registers this: "When the finished() signal is emitted, run the fileIsReady() function".

Now, to answer your questions.

  1. Why does my programm progresses while the function didn't finish reading.

Because in your constructor code, you asked the constructor to finish after you connect the signal to the slot. You did not ask it to wait for the download to finish.

And then, you call getSearchList() without waiting for the finished() signal. So, getSearchList() gets called before fileIsReady().

  1. How can I make the second call "getSearchList" wait?

Like MrEricSir said, you shouldn't ask it to wait! (Think about it: What happens if you lose internet connection and can't finish downloading the file? The answer is, your program will freeze because it will wait forever. That's bad.)

Don't call getSearchList() immediately after constructing XMLParser. Instead, make XMLParser emit a "finishedParsing()" signal when it finishes parsing the XML file. Then, make another signal-slot connection: Connect the finishedParsing() signal to a slot that calls getSearchList().

查看更多
登录 后发表回答