Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Lazy Loading File Icons to QTableWidget?



  • I'm displaying the contents of a directory in a QTableWidget along with the file icons. I've found that it can take a long time to load the icons, particularly with icons that are embedded in executable files. To make the application more responsive I changed it to show the directory contents immediately and then tried to load the icons gradually from a worker thread. However, I'm not sure how to implement the icon loading in the worker thread.

    I tried creating an IconLoader class to load the icons from a QFileIconProvider in a worker thread:

    struct IconRequest
    {
        QFileInfo fileInfo;
        int row;
        QIcon icon;
    };
    
    class IconLoader : public QObject
    {
        Q_OBJECT
    private:
        QFileIconProvider m_IconProvider;
    signals:
        void IconReady(IconRequest* request);  
    private slots:
        void LoadIconReq(IconRequest* request);
        {
            request->icon = m_IconProvider.icon(request->fileInfo);
            emit IconReady(request);
        }
    };
    

    I created connections for requesting and receiving icons and moved the IconLoader to a QThread:

    m_IconLoader = new IconLoader;
    m_IconLoader->moveToThread(&m_IconLoaderThread);
    connect(this, SIGNAL(RequestingIcon(IconRequest*)), m_IconLoader, SLOT(LoadIconReq(IconRequest*)));
    connect(m_IconLoader, SIGNAL(IconReady(IconRequest*)), this, SLOT(SetIcon(IconRequest*)));
    connect(&m_IconLoaderThread, SIGNAL(finished()), m_IconLoader, SLOT(deleteLater()));
    m_IconLoaderThread.start();
    

    After the IconReady() signal is emitted by the worker thread the SetIcon() slot is called in the main thread (m_dirListing is a pointer to a QTableWidget):

    void FileList::SetIcon(IconRequest* request)
    {
        m_dirListing->item(request->row, 0)->setIcon(request->icon);
        delete request;
    }
    

    Unfortunately, the directory contents still do not display until all the icons have loaded. Investigating the problem it appears that QFileIconProvider::icon() is actually a very fast operation that returns immediately. The time consuming operation seems to be QTableWidgetItem::setIcon() which appears to be where the icon is actually loaded. As QTableWidgetItem::setIcon() is called from the main thread the program still becomes unresponsive when loading directories.

    Since the above approach doesn't work I'm unsure how to get lazy loading of icons to work. QTableWidgetItem::setIcon() couldn't be called from the worker thread since QTableWidgetItem is reenterant and not thread-safe.

    How can I load the icon in the worker thread so it can be quickly set in the main thread?


  • Qt Champions 2019

    Use an own model and set the data there. QTableWidget is just a convenience class which was designed for simple stuff (just like QTreeWidget).



  • That probably would have been best, but at this point it would require a lot of changes.

    The application is complete and generally works well, but I later found that opening directories containing a lot of executables with embedded icons could be very slow. I was hoping to find a way of addressing that without making major changes.


Log in to reply