5

I'm use QTreeView to get the images path, then I'm use QListView to display the images that in specific path as thumbnail.

The problem in the period, create and display the thumbnail images.
The previous process, take a long time to done, depend on the number of images.
And for that reason I decided to use the threads, maybe helps to prevent the hung up which occur in application and increase the speed of create and display the thumbnail images.

void mainWidget::on_treeView_clicked(const QModelIndex &index){
    filesModel->clear();
    QFileSystemModel *sysModel = qobject_cast<QFileSystemModel*>(ui->treeView->model());
    QDir dir(sysModel->filePath(ui->treeView->currentIndex()));
    QFileInfoList filesList = dir.entryInfoList(QStringList() << "*.jpg" << "*.jpeg" << "*.tif" << "*.png" << "*.gif" << "*.bmp" ,QDir::Files);
    int filesCount = filesList.size();
    for(int i=0;i<filesCount;i++){
        QPixmap originalImage(filesList[i].filePath());
        if(!originalImage.isNull()){
            QPixmap scaledImage = originalImage.scaled(150, 120);    
            filesModel->setItem(i, new QStandardItem(QIcon(scaledImage), filesList[i].baseName()));
        }
    }
}

How can I use threads with the previous code ?

Lion King
  • 32,851
  • 25
  • 81
  • 143
  • I'd do it just as explorer and everybody else does: show a placeholder picture and replace that when the rendering of the real one is done. – Voo Nov 09 '14 at 15:45

3 Answers3

3

I believe that a simple and correct approach is using QtConcurrent as the following:

Note: If you are using Qt 5 you will need to add QT += concurrent to the .pro file.

Header:

#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <QtConcurrent>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void UpdateItem(int, QImage);

private slots:
    void on_treeView_clicked(const QModelIndex &);
    void List(QFileInfoList filesList, QSize size);
    void setThumbs(int index, QImage img);

private:
    Ui::MainWindow *ui;
    QFileSystemModel *model;
    QStandardItemModel *filesmodel;
    QFuture<void> thread;
    bool running;
};

CPP file:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QThreadPool::globalInstance()->setMaxThreadCount(1);

    model = new QFileSystemModel(this);
    model->setRootPath("\\");

    filesmodel = new QStandardItemModel(this);

    ui->treeView->setModel(model);
    ui->listView->setModel(filesmodel);

    connect(this, SIGNAL(UpdateItem(int,QImage)), SLOT(setThumbs(int,QImage)));

    ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);

    running = false;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_treeView_clicked(const QModelIndex&)
{
    filesmodel->clear();

    running = false;

    thread.waitForFinished();

    QDir dir(model->filePath(ui->treeView->currentIndex()));

    QFileInfoList filesList = dir.entryInfoList(QStringList() << "*.jpg" << "*.jpeg" << "*.tif" << "*.png" << "*.gif" << "*.bmp", QDir::Files);

    int filesCount = filesList.size();

    QPixmap placeholder = QPixmap(ui->listView->iconSize());
    placeholder.fill(Qt::gray);

    for (int i = 0; i < filesCount; i++)
        filesmodel->setItem(i, new QStandardItem(QIcon(placeholder), filesList[i].baseName()));

    running = true;

    thread = QtConcurrent::run(this, &MainWindow::List, filesList, ui->listView->iconSize());
}

void MainWindow::List(QFileInfoList filesList, QSize size)
{
    int filesCount = filesList.size();

    for (int i = 0; running && i < filesCount; i++)
    {
        QImage originalImage(filesList[i].filePath());
        if (!originalImage.isNull())
        {
            QImage scaledImage = originalImage.scaled(size);
            if (!running) return;
            emit UpdateItem(i, scaledImage);
        }
    }
}

void MainWindow::setThumbs(int index, QImage img)
{
    QIcon icon = QIcon(QPixmap::fromImage(img));
    QStandardItem *item = filesmodel->item(index);
    filesmodel->setItem(index, new QStandardItem(icon, item->text()));
}
Antonio Dias
  • 2,751
  • 20
  • 40
  • From the docs: β€œThe QtConcurrent namespace provides high-level APIs that make it possible to write multi-threaded programs without using low-level threading primitives such as mutexes, read-write locks, wait conditions, or semaphores.” that way the `QtConcurrent` namespace is just a simplest way to multi thread, especially if you want to run just a function, not a entire class, on a separate thread. – Antonio Dias Nov 10 '14 at 16:22
1

You don't have to use threads to keep your application responsive in this case. Use QCoreApplication::processEvents() in the loop to keep the application responsive. QCoreApplication::processEvents() will process all the events in the event queue of the thread which calls it.

user1415913
  • 325
  • 4
  • 9
0

This is an older thread, but still comes up in Google. Check out the answer to this related question. I found the use of QIdentityProxyModel to be a bit more elegant, as it allowed QFileSystemModel to be used as the list view's model.

Community
  • 1
  • 1
Alex Goldberg
  • 975
  • 11
  • 14