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, async loading many images and threads.

QQuickImageProvider, async loading many images and threads.

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
1 Posts 1 Posters 1.4k Views
  • 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.
  • 0 Offline
    0 Offline
    0hno
    wrote on last edited by 0hno
    #1

    Hello,
    Here are problem: 150+ elements with image in GridLayout (or GridView, not so important) and I want to implement caching/handling images before they come to QML via QQuickImageProvider (or QQuickAsyncImageProvider).

    Approach 1: Make provider class that inherits from QQuickImageProvider, implement requestImage (like in official example imageprovider-example ) only with loading images via QNetworkManager + handling (rounding image) + caching.
    Problem: If Image.asynchronous = false then image loading pretty slow and there are some visual laggs in qml (scene not redrawing while image loading); If Image.asynchronous = true then another 2 variant: 1.1 If NOT try to make method requestImage "reentrant" like written in docs then app crush; 1.2 If try to make MutexLocker in requestImage then no one image will load because loading get stucked on trying load first image (but app will work) right on string with eventLoop.exec():

    QImage MenuImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) {
      QMutexLocker locker(&mutex);
      QUrl url("path_to_image_in_internet" + id);    
      QNetworkAccessManager *manager = new QNetworkAccessManager;
      QNetworkReply* reply = manager->get(QNetworkRequest(url));
      QEventLoop eventLoop;
      QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
      eventLoop.exec(); // all get stucked here if using QMutexLocker locker(&mutex);
        if (reply->error() != QNetworkReply::NoError)
          return QImage();    
      image = QImage::fromData(reply->readAll());
      // .. + some handling and caching stuff
      size->setWidth(image.width());
      size->setHeight(image.height());
      return image;
    }
    

    Approach 2. Make provider class that inherits from QQuickAsyncImageProvider, implement requestImageResponse and so on like in official example imageresponseprovider-example ) also with loading images via QNetworkManager + handling + caching.
    Problem: after creating 150+ elements with images via Repeater + model I see how about 100 images successfully loads but after i got error in console output (but app still working):

    QThread::start: Failed to create thread (Недостаточно памяти для обработки команды.)
    

    translate from russian: Not enough memory for handle command
    It happend on Windows. If I launch this app on slow Android with 1 gb memory then app got crush with message about unable creating thread.

    And if I not using any ImageProvider, and use in Image.source url to internet it works correctly (with Image.asynchronous = true or false, not important). So how QML Engine by default handling this, and why that ImageProviders (from official docs) doesn't works? Where my mistake?

    There are implemetation of QQuickAsyncImageProvider:

    1. Base class for loading and handling images that inherits from QObject and QRunnable (like in docs)
    class BaseAsyncImageResponseRunnable : public QObject, public QRunnable
    {
        Q_OBJECT
    
    signals:
        void done(QImage image);
    
    public:
        BaseAsyncImageResponseRunnable(const QString &id, const QSize &requestedSize, QString imagesPath)
            : m_id(id), m_requestedSize(requestedSize), m_imagesPath(imagesPath) {}
    
        void run() override
        {
            QImage image;
            QUrl url(m_imagesPath + m_id);
    
            QNetworkAccessManager *manager = new QNetworkAccessManager;
            QNetworkReply* reply = manager->get(QNetworkRequest(url));
            QEventLoop eventLoop;
            QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
            eventLoop.exec();
            if (reply->error() != QNetworkReply::NoError) {
                emit done(image);
                return;
            }
            image = QImage::fromData(reply->readAll());
            image = handleImage(image);
            emit done(image);
        }
    
        virtual QImage handleImage(QImage image) { return image; }
    
    protected:
        QString m_id;
        QSize m_requestedSize;
        QString m_imagesPath;
    };
    
    1. Implement QQuickImageResponse in base class
    class BaseAsyncImageResponse : public QQuickImageResponse
    {
        public:
            BaseAsyncImageResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool, BaseAsyncImageResponseRunnable *runnable)
            {
                connect(runnable, &BaseAsyncImageResponseRunnable::done, this, &BaseAsyncImageResponse::handleDone);
                pool->start(runnable);
            }
    
            void handleDone(QImage image) {
                m_image = image;
                emit finished();
            }
    
            QQuickTextureFactory *textureFactory() const override
            {
                return QQuickTextureFactory::textureFactoryForImage(m_image);
            }
    
            QImage m_image;
    };
    
    1. Implements QQuickAsyncImageProvider
    class MyAsyncImageProvider : public QQuickAsyncImageProvider
    {
    public:
        MyAsyncImageProvider() : QQuickAsyncImageProvider()
        { }
    
        QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override
        {
            BaseAsyncImageResponse *response;
            QString imagesPath = "pat_to_images_in_internet";
            auto runnable = new BaseAsyncImageResponseRunnable(id, requestedSize, imagesPath);
            response = new BaseAsyncImageResponse(id, requestedSize, &pool, runnable);
            return response;
        }
    
    private:
        QThreadPool pool;
    };
    
    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