Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QQuickImageProvider - get image from the network
Forum Updated to NodeBB v4.3 + New Features

QQuickImageProvider - get image from the network

Scheduled Pinned Locked Moved QML and Qt Quick
9 Posts 4 Posters 4.0k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    m_andrej
    wrote on last edited by
    #1

    Hi,
    I need to show images downloaded from the network, but I can't just set the image URL to Image element, because the image needs to be requested with certain cookies from the server. So it seems that I have to subclass QQuickImageProvider to fetch image the way the server requires it. The problem is that I can't return the image data immediately from my reimplemented QQuickImageProvider::requestImage() because data is returned in a slot connected to QNetworkRequest::finished(). In requestImage() I can only send the HTTP request and connect QNetworkRequest::finished() to a slot. I don't have the image data at the time when requestImage() returns.

    In other words, QQuickImageProvider's API is synchronous, but Qt network API is asynchronous. Any ideas?

    T 1 Reply Last reply
    1
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      You can use something like:

      QEventLoop loop;
      connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
      loop.exec();
      // process request and return image
      

      WARNING This currently doesn't take into account that there might be timeout or other network problems so you have to adapt that code for these situations.

      Hope it helps

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • M Offline
        M Offline
        m_andrej
        wrote on last edited by m_andrej
        #3

        Hello SGaist,

        thanks for your reply. It works! Unfortunately, it crashes my application. Here is my code:

        QImage EbookImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
        {
        	QNetworkRequest request{QUrl(id)};
        	request.setRawHeader("Cookie", QString("Csob=" + CServerCommunicator::sessionID()).toUtf8());
        	QNetworkReply *reply = CServerCommunicator::netManager()->get(request);
        	QEventLoop loop;
        	QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
        	loop.exec();
        
        	if(reply->error() != QNetworkReply::NoError) {
        		reply->deleteLater();
        		*size = m_defaultImage.size();
        		return m_defaultImage;
        	}
        
        	QByteArray data = reply->readAll();
        	reply->deleteLater();
        	reply = nullptr;
        
        	QImage returnImage;
        	if(returnImage.loadFromData(data)) {
        		*size = returnImage.size();
        		return returnImage;
        	}
        	*size = m_defaultImage.size();
        	return m_defaultImage;
        }
        

        I'm saving a default image to m_defaultImage in EbookImageProvider's constructor. I get the following output at the beginning (can be unrelated to image provider):
        QEventLoop: Cannot be used without QApplication
        QDBusConnection: system D-Bus connection created before QCoreApplication. Application may misbehave.
        And when images are shown:
        QObject: Cannot create children for a parent that is in a different thread.
        (Parent is QNetworkAccessManager(0xc76c50), parent's thread is QThread(0xc761a0), current thread is QQuickPixmapReader(0x25a5520)
        When I scroll the list view with images and reach the end of the list, application crashes. This happens on Desktop 64-bit and Android, in Qt 5.4.2. It's interesting that Images in listview are shown all at once, not one by one.
        Any ideas?

        UPDATE: I modified the code to create QNetworkAccessManager directly in requestImage(). This eliminated "different thread" warnings, but the application still crashes. But maybe less frequently.

        1 Reply Last reply
        1
        • M m_andrej

          Hi,
          I need to show images downloaded from the network, but I can't just set the image URL to Image element, because the image needs to be requested with certain cookies from the server. So it seems that I have to subclass QQuickImageProvider to fetch image the way the server requires it. The problem is that I can't return the image data immediately from my reimplemented QQuickImageProvider::requestImage() because data is returned in a slot connected to QNetworkRequest::finished(). In requestImage() I can only send the HTTP request and connect QNetworkRequest::finished() to a slot. I don't have the image data at the time when requestImage() returns.

          In other words, QQuickImageProvider's API is synchronous, but Qt network API is asynchronous. Any ideas?

          T Offline
          T Offline
          t3685
          wrote on last edited by
          #4

          @m_andrej

          Hi,

          We had a similar problem. We solved it as follows:

          • Make worker thread to do the fetching.
          • In requestImage() use a QSemaphore to block the main thread (yes I know)
          • When the fetching thread finishes, unlock the QSemaphore and processing continues as normal

          Hope this helps,

          M 1 Reply Last reply
          2
          • T t3685

            @m_andrej

            Hi,

            We had a similar problem. We solved it as follows:

            • Make worker thread to do the fetching.
            • In requestImage() use a QSemaphore to block the main thread (yes I know)
            • When the fetching thread finishes, unlock the QSemaphore and processing continues as normal

            Hope this helps,

            M Offline
            M Offline
            m_andrej
            wrote on last edited by
            #5

            @t3685
            Thanks for the suggestion. Does this mean that the application UI will freeze while main thread is blocked?

            T 1 Reply Last reply
            1
            • M m_andrej

              @t3685
              Thanks for the suggestion. Does this mean that the application UI will freeze while main thread is blocked?

              T Offline
              T Offline
              t3685
              wrote on last edited by
              #6

              @m_andrej

              Yes, it will block. But I believe the solution using the event loop will also block. If you don't want to UI to block, you can give an option to QImageProvider to use a separate thread

              http://doc.qt.io/qt-5/qqmlimageproviderbase.html#Flag-enum

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #7

                @m_andrej Do you have a QApplication or a QGuiApplication ?

                @t3685 Interesting flag, I've missed that one

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                M 1 Reply Last reply
                0
                • SGaistS SGaist

                  @m_andrej Do you have a QApplication or a QGuiApplication ?

                  @t3685 Interesting flag, I've missed that one

                  M Offline
                  M Offline
                  m_andrej
                  wrote on last edited by m_andrej
                  #8

                  @SGaist I'm creating a QApplication in main() - I left this code as generated by Qt Creator.

                  But I think I found a better solution altogether - I'm going to ditch QQuickImageProvider and subclass QQuickPaintedItem instead. I will call QQuickPaintedItem::update() when network reply finishes.

                  M 1 Reply Last reply
                  1
                  • M m_andrej

                    @SGaist I'm creating a QApplication in main() - I left this code as generated by Qt Creator.

                    But I think I found a better solution altogether - I'm going to ditch QQuickImageProvider and subclass QQuickPaintedItem instead. I will call QQuickPaintedItem::update() when network reply finishes.

                    M Offline
                    M Offline
                    mallah.rajesh
                    wrote on last edited by mallah.rajesh
                    #9

                    @m_andrej

                    There is an altogether different solution too to your original objective ie,

                    "because the image needs to be requested with certain cookies from the server. 
                    So it seems that I have to subclass QQuickImageProvider  ....."
                    

                    in involves using QQmlNetworkAccessManagerFactory . I was able to solve a similar
                    problem that involved using QNetworkDiskCache . Initially i had tried the provider
                    approach but the subclassing was much simpler and stable ( read no more application
                    crashes)

                    i am posting a relevant part of the code.

                    
                    #include "customnamfactory.h"
                    
                    #include <QNetworkDiskCache>
                    #include <QStandardPaths>
                    
                    CustomNAMFactory::CustomNAMFactory()
                    {
                    
                    }
                    
                    
                    QNetworkAccessManager *CustomNAMFactory::create(QObject *parent)
                    {
                        QNetworkAccessManager *nam = new QNetworkAccessManager(parent);
                        QNetworkDiskCache *diskCache = new QNetworkDiskCache ;
                        QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
                        diskCache->setCacheDirectory(cacheDir);
                        nam->setCache(diskCache);
                    
                        return nam;
                    }
                    
                    
                    
                    engine_ptr->setNetworkAccessManagerFactory(new CustomNAMFactory);
                    

                    checkout http://doc.qt.io/qt-5/qtqml-networkaccessmanagerfactory-example.html
                    for a complete example.

                    regds
                    mallah.

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved