Designing a "general purpose" QNetworkAccessManager interface



  • Hello!

    As a learning exercise I am trying to create an application that displays images from a website, with the option to save individual images to disk.

    I start out with a link to an index page, containing 40-some images. Currently, I have a "Downloader" class, where I have connected the QNetworkAccessManager::finished signal to a slot that gets the QByteArray from the QNetworkReply and emits a signal. That signal is then connected to one of my "Parser" class' slots, which gets the URLs of the individual images from the QByteArray and emits another signal with the URLs in a vector (Parser is based on some code I already had, so I haven't gotten around to substituting STL-types with q-types yet).

    At this point, I would like to download each of the 40-some URLs, so that I can move on to get the direct image links from preview pages. However, because the QNetworkAccessManager::finished signal is already connected to a slot, I am kind of stuck.

    So my question is then: what is an acceptable way to structure my "Downloader" class, so that it's able to do more than just one thing?
    Having never designed a GUI application, or worked with Qt before, this whole signal-slot is still a bit foreign to me.

    I'm also looking for good up-to-date beginner resources to go along with the documentation.
    Thank you!


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Nothing forbids you to have several slots connected to a signal.

    So if I understand correctly, you would like to start the next download when the current on is finished while the received data are being parsed, correct ?



  • @SGaist Oh, I didn't know that, how does that work?

    And I'm not quite sure if I would do it while the received data is being parsed, since the result(s) of the parsing is what I would like to download next. Or are you suggesting I do something like emitting signals as the data is parsed, instead of collecting it all in a vector and sending only one signal, like I am now?


  • Lifetime Qt Champion

    Usually, pretty nicely ;) In practice it's just several connection statements, one for each target. Or depending on your needs through a QSignalMapper.

    So basically, you have a list of downloads that will generate further downloads once parsed ? In that case, nothing is wrong to parallelise the downloads. IIRC, you can have up to 6 parallel transfers with QNAM, the rest is then queued. Thus you can trigger the next download once one is finished while something else processes the data received and generates additional downloads. Some sort of download manager.



  • @SGaist Alright, parallelising them like that is a much better idea!

    I don't think I would be able to get away with multiple connection statements.
    My connection right now looks like this:

    connect(m_qnam, &QNetworkAccessManager::finished, this, &Downloader::indexPageDownloaded);
    

    If I were to add another connection to &QNAM::finished, surely it would not be able to decide which to use?

    The QSignalManager looks like it can connect several signals to one slot, however I have one signal I want to connect to multiple slots. It's not immediately obvious to me how I would solve this :p

    I appreciate your fast replies, by the way!


  • Moderators

    @voiid said in Designing a "general purpose" QNetworkAccessManager interface:

    If I were to add another connection to &QNAM::finished, surely it would not be able to decide which to use?

    There is nothing to decide: each slot connected to the signal will be called. So, if you connect two slots to that signal, both will be executed.
    You should read this: http://doc.qt.io/qt-5.7/signalsandslots.html



  • If you want a slot to react to a specific request connect the reply directly instead of connecting the QNetworkAccessManager

    QNetworkReply* currentRep = m_qnam->get(/*your URL*/);
    connect(currentRep,&QNetworkReply::finished,[currentRep,this]()->void{indexPageDownloaded(currentRep);});
    connect(currentRep,&QNetworkReply::finished,currentRep,&QNetworkReply::deleteLater); //you probably also want this!
    

    This method lets you trigger indexPageDownloaded only for that specific reply and not for all the replies that the QNAM recieves



  • @VRonin Excellent! This is exactly what I was looking for! Thank you!



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.