I need to handle the display of thumbnails of photos in a separate stream, otherwise the code is processed for a very long time. I have basically a class class MainWindow model and variable:

mainwindow.h

public: static QStandardItemModel * model = new QStandardItemModel; static QStandardItem *item_one; static int rowTableDisk = 0; 

I create a stream to add thumbnails like this:

threadsminiature.h

  #ifndef THREADSMINIATURE_H #define THREADSMINIATURE_H #include <QThread> #include <QImageReader> #include <mainwindow.h> class ThreadsMiniature : public QThread { public: explicit ThreadsMiniature(QString threadName, QString Path); void run(); QString _Path; private: QString name; // Имя потока }; #endif // THREADSMINIATURE_H 

threadsminiature.cpp

 #include "threadsminiature.h" ThreadsMiniature::ThreadsMiniature(QString threadName, QString Path) : name(threadName), _Path(Path) { } void ThreadsMiniature::run() { int icoWidth = 200; int icoHeight = 200; QImageReader imageReader(_Path); QSize size; int image_width; int image_height; if (imageReader.supportsOption(QImageIOHandler::Size)) { size = imageReader.size(); image_width = size.width(); image_height = size.height(); } double ratio = (double)image_width / (double)image_height; if (ratio >= 1) { image_width = icoWidth; image_height = image_width / ratio; } else { image_height = icoHeight; image_width = image_height * ratio; } imageReader.setScaledSize(QSize(image_width, image_height)); //QImage image = imageReader.read(); MainWindow::item_one = new QStandardItem(); MainWindow::item_one->setData(QVariant(QPixmap::fromImage(imageReader.read())), Qt::DecorationRole); MainWindow::model->setItem(MainWindow::rowTableDisk, 2, MainWindow::item_one); } 

In the main code I fill in QTableView like this:

mainwindow.cpp

 void MainWindow::TableListFiles(QStringList imagePathListCopy, QString DeviceFolder) { if (!DeviceFolder.isEmpty()) { //Заголовки столбцов QStringList horizontalHeader; horizontalHeader.append("Отметь, что перенести"); horizontalHeader.append("Кликабельные ссылки на изображения"); horizontalHeader.append("Папка в которую будет скопирован файл"); model->setHorizontalHeaderLabels(horizontalHeader); for(int i=0; i<imagePathListCopy.count(); i++) { if(rowTableDisk == 0) { item_one = new QStandardItem("Выбрать/Убрать всё"); model->setItem(rowTableDisk, 1, item_one); rowTableDisk++; //item_one = new QStandardItem("<a href=\""+imagePathListCopy.at(i)+"\">"+QFileInfo(imagePathListCopy.at(i)).fileName()+"</a>"); item_one = new QStandardItem(imagePathListCopy.at(i)); model->setItem(rowTableDisk, 1, item_one); QImage image(imagePathListCopy.at(i)); item_one = new QStandardItem(); item_one->setData(QVariant(QPixmap::fromImage(image)), Qt::DecorationRole); model->setItem(rowTableDisk, 2, item_one); item_one = new QStandardItem(DeviceFolder); model->setItem(rowTableDisk, 3, item_one); } else { //item_one = new QStandardItem("<a href=\""+imagePathListCopy.at(i)+"\">"+QFileInfo(imagePathListCopy.at(i)).fileName()+"</a>"); item_one = new QStandardItem(imagePathListCopy.at(i)); model->setItem(rowTableDisk, 1, item_one); ThreadsMiniature *threadB = new ThreadsMiniature(QString("thread_%1").arg(i),imagePathListCopy.at(i)); threadB->start(); //QImage image(imagePathListCopy.at(i)); item_one = new QStandardItem(DeviceFolder); model->setItem(rowTableDisk, 3, item_one); } rowTableDisk++; } } else { //Заголовки столбцов QStringList horizontalHeader; horizontalHeader.append("Отметь, что перенести"); horizontalHeader.append("Кликабельные ссылки на изображения"); model->setHorizontalHeaderLabels(horizontalHeader); int rowTableDisk = 0; for(int i=0;i<imagePathListCopy.count();i++) { if(rowTableDisk == 0) { item_one = new QStandardItem("Выбрать/Убрать всё"); model->setItem(rowTableDisk, 1, item_one); rowTableDisk++; //item_one = new QStandardItem("<a href=\""+imagePathListCopy.at(i)+"\">"+QFileInfo(imagePathListCopy.at(i)).fileName()+"</a>"); item_one = new QStandardItem(imagePathListCopy.at(i)); model->setItem(rowTableDisk, 1, item_one); } else { //item_one = new QStandardItem("<a href=\""+imagePathListCopy.at(i)+"\">"+QFileInfo(imagePathListCopy.at(i)).fileName()+"</a>"); item_one = new QStandardItem(imagePathListCopy.at(i)); model->setItem(rowTableDisk, 1, item_one); } rowTableDisk++; } } CheckBoxDelegate *CheckBoxD = new CheckBoxDelegate(this); HrefDelegate *HrefD = new HrefDelegate(this); TableDisk->setModel(model); TableDisk->setItemDelegateForColumn(0, CheckBoxD); TableDisk->setItemDelegateForColumn(1, HrefD); TableDisk->setColumnWidth(0,150); TableDisk->setColumnWidth(1,600); TableDisk->setColumnWidth(2,300); } 

But errors: enter image description here

How to use static QStandardItemModel * model = new QStandardItemModel; and static int rowTableDisk = 0; ?

  • Transfer the initialization to a cpp file, or make the model and rowTableDisk constant. This is a free translation of what the compiler tells you. - vegorov

2 answers 2

Did not fit in the comment, leave as an answer:

It’s better not to work with threads.

There is at least QThreadPool , as well as Qt Concurrent - I really don’t understand it, a quote from the documentation - 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.

Inheriting from QThread is not recommended - Proper use of QThread . In short, QThread is first of all needed to start QEventLoop in another thread and be able to send signals to this stream and receive them from there. Read more - read the article on the link to Habré.

Original articles:

youre-doing-it-wrong QThread

how to realy use qthreads

* UPD. *

Read the source.

Do you get that several threads will be created in the loop, and each of them will access the same static item ( item_one )?

No sync?

* UPD 2. Example. *

I made an assumption about what you need and made an example.

The example loads from a folder all the jpg/png format pictures into a table with two columns - the name of the picture and preview. The code for getting the preview was copied from your run method in the stream

The ImageLoader class is a successor of QObject (to send a signal) and QRunnable (so that it can be added to QThreadPool).

I’m not sure what’s right, because the moveToThread QThreadPool probably doesn’t call the method, since a normal QRunnable interface expects the input, but still it’s better than inheriting from QThread .

 #ifndef IMAGELOADER_H #define IMAGELOADER_H #include <QObject> #include <QRunnable> #include <QFileInfo> #include <QImage> class ImageLoader: public QObject, public QRunnable { Q_OBJECT public: explicit ImageLoader(const QFileInfo& img, QObject* parent = nullptr); virtual ~ImageLoader(){ m_img = nullptr; //не удаляем - удалить обязан тот кто будет обрабатывать сигнал imgReady } virtual void run() override; signals: void imgReady(const QFileInfo& file, QImage* img); private: QFileInfo m_imgFile; QImage* m_img; }; #endif // IMAGELOADER_H #include "imageloader.h" #include <QImageReader> #include <QDebug> ImageLoader::ImageLoader(const QFileInfo &img, QObject *parent): QObject(parent), m_imgFile{img}, m_img{nullptr} { setAutoDelete(true); } void ImageLoader::run() { if (!m_imgFile.exists()){ qDebug() << __FUNCTION__ << "file" << m_imgFile.absoluteFilePath() << "do not exists"; return; } int icoWidth = 200; int icoHeight = 200; QImageReader imageReader(m_imgFile.absoluteFilePath()); QSize size; int image_width; int image_height; if (imageReader.supportsOption(QImageIOHandler::Size)) { size = imageReader.size(); image_width = size.width(); image_height = size.height(); }else{ qCritical() << "QImageReader don support option QImageIOHandler::Size"; return; } double ratio = (double)image_width / (double)image_height; if (ratio >= 1) { image_width = icoWidth; image_height = image_width / ratio; } else { image_height = icoHeight; image_width = image_height * ratio; } imageReader.setScaledSize(QSize(image_width, image_height)); m_img = new QImage(); if (imageReader.read(m_img)){ emit imgReady(m_imgFile, m_img); }else{ qWarning() << "can't read image" << m_imgFile.fileName() << imageReader.errorString(); delete m_img; } } 

MainWindow

 #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QDir> #include <QStandardItemModel> #include <QMutex> #include <QFileInfo> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_m_selectDirBtn_clicked(); void onImgReady(const QFileInfo& file, QImage* img); private: Ui::MainWindow *ui; QDir m_imgDir; QStandardItemModel* m_imgModel; }; #endif // MAINWINDOW_H 

MainWindow.cpp

 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QDebug> #include <QThreadPool> #include <QMutexLocker> #include "imageloader.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_imgDir{"", "*.jpg; *.png", QDir::Name | QDir::IgnoreCase, QDir::Files}, m_imgModel{} { ui->setupUi(this); m_imgModel = new QStandardItemModel(0, 2, this); ui->m_imageTableView->setModel(m_imgModel); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_m_selectDirBtn_clicked() { QString dir = QFileDialog::getExistingDirectory(this); qDebug() << dir; if (dir.isEmpty()){ qWarning() << "Пользователь отменил выбор директории"; }else{ m_imgDir.setPath(dir); qDebug() << m_imgDir.entryList(); m_imgModel->clear(); for (const auto& img: m_imgDir.entryList()){ ImageLoader* imgLoader = new ImageLoader(QFileInfo(m_imgDir.filePath(img))); connect(imgLoader, SIGNAL(imgReady(QFileInfo,QImage*)), this, SLOT(onImgReady(QFileInfo,QImage*)), Qt::QueuedConnection); QThreadPool::globalInstance()->start(imgLoader); } } } void MainWindow::onImgReady(const QFileInfo &file, QImage *img) { if (img){ QStandardItem* imgItem = new QStandardItem(); imgItem->setData(QVariant(QPixmap::fromImage(*img)), Qt::DecorationRole); m_imgModel->appendRow({new QStandardItem(file.fileName()), imgItem}); ui->m_imageTableView->resizeRowToContents(m_imgModel->rowCount()-1); ui->m_imageTableView->resizeColumnsToContents(); qInfo() << "img loaded" << "threads count:" << QThreadPool::globalInstance()->activeThreadCount(); } if (img){ delete img; } } 

There is one button on the form to select the directory and the TableView to display the model:

mainwindow.ui

 <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>489</width> <height>256</height> </rect> </property> <property name="maximumSize"> <size> <width>640</width> <height>480</height> </size> </property> <property name="windowTitle"> <string>Потоки</string> </property> <widget class="QWidget" name="centralWidget"> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QTableView" name="m_imageTableView"/> </item> <item> <widget class="QPushButton" name="m_selectDirBtn"> <property name="text"> <string>Выбрать директорию</string> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menuBar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>489</width> <height>21</height> </rect> </property> </widget> <widget class="QToolBar" name="mainToolBar"> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> </attribute> <attribute name="toolBarBreak"> <bool>false</bool> </attribute> </widget> <widget class="QStatusBar" name="statusBar"/> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui> 

How it looks like:

Images of the same size Images of various sizes

Output to console:

 "C:/Users/User/Pictures" ("2017-07-17_01.13.04.png", "galio.jpg", "galio.under.lol.png", "galio_and_text.png", "galio_w320.png", "lol.jpg", "lol.png", "LoL_Bug.1.png", "LoL_Bug.2.png", "LoL_Bug.3.png", "LoL_Bug.png", "photo_2018-03-08_23-37-28.jpg", "photo_2018-03-08_23-37-45.jpg", "photo_2018-03-08_23-37-51.jpg", "photo_2018-03-08_23-37-54.jpg", "photo_2018-03-08_23-37-57.jpg", "photo_2018-03-08_23-38-00.jpg", "photo_2018-03-08_23-38-02.jpg", "photo_2018-03-08_23-38-05.jpg", "photo_2018-03-08_23-38-08.jpg", "twitch.png", "Безымянный.png", "Снимок.PNG") img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 4 img loaded threads count: 2 img loaded threads count: 2 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 1 img loaded threads count: 0 

As you can see, up to 4 threads were involved.

I almost forgot - I had to register QFileInfo in main.cpp in order to send it in the signal as a parameter:

 #include "mainwindow.h" #include <QApplication> #include <QFileInfo> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; qRegisterMetaType<QFileInfo>("QFileInfo"); w.show(); return a.exec(); } 
  • I saw these articles. I did the following lesson: youtube.com/watch?v=PR6wVy7Et1A&t=262s just how to pass two values ​​to emit drowMiniature (imageReader.read ()); Not understood. Thought Vektorom but at QVector <QImage> MiniatureVector; no SetObjectName - Ivan Triumphov
  • Otherwise, the pictures in the wrong place are added - Ivan Triumphov
  • The presence of this lesson on YouTube, like other lessons, does not make it correct. It uses mutex for nothing, signals from the stream are sent without explicit instructions from Qt :: QueuedConnection, and so on, and there are comments from users in the comments to the video. Yes, and the example there is strange, it was easier to use QTimer for what is shown in the video - vegorov
  • I do not understand what you want to do, can you give a complete (minimal and sufficient) example? - vegorov
  • I already brought what I wanted to the stream. Although most likely wrong. But at this stage, everything works quickly and it suits me. And I wanted to bring in a separate stream the display of thumbnails of photos. Now I really need to sort out another question ru.stackoverflow.com/questions/848477/ ... I will write my finished version here. - Ivan Triumphov

Perhaps not the correct code, but working and fast:

threadsminiature.h

 #ifndef THREADSMINIATURE_H #define THREADSMINIATURE_H #include <QObject> #include <QThread> #include <QImageReader> #include <QVector> class ThreadsMiniature : public QThread { Q_OBJECT public: explicit ThreadsMiniature(QObject *parent=0, QString Path="", int rowTableDisk = 0); void run(); QString _Path; int _rowTableDisk; //QList<QImage> MiniatureList; signals: //QImage drowMiniature(QImage image) {return image;} void drowMiniature(QImage); private: QString name; // Имя потока }; #endif // THREADSMINIATURE_H 

More than two values ​​in the MainWindow :: Miniature function can be passed using

 QImage image = imageReader.read(); image.setText("row", QString("%1").arg(_rowTableDisk)); 

Since QVector <QImage> does not have a SetObjectName method:

threadsminiature.cpp

 #include "threadsminiature.h" #include <QtCore> ThreadsMiniature::ThreadsMiniature(QObject *parent, QString Path, int rowTableDisk) : QThread(parent), _Path(Path), _rowTableDisk(rowTableDisk) { } void ThreadsMiniature::run() { int icoWidth = 200; int icoHeight = 200; QImageReader imageReader(_Path); QSize size; int image_width; int image_height; if (imageReader.supportsOption(QImageIOHandler::Size)) { size = imageReader.size(); image_width = size.width(); image_height = size.height(); } double ratio = (double)image_width / (double)image_height; if (ratio >= 1) { image_width = icoWidth; image_height = image_width / ratio; } else { image_height = icoHeight; image_width = image_height * ratio; } imageReader.setScaledSize(QSize(image_width, image_height)); QImage image = imageReader.read(); image.setText("row", QString("%1").arg(_rowTableDisk)); emit drowMiniature(image); } 

To bypass the call to the model (QStandardItemModel), in the stream, you can bring the call to this model to the separated function MainWindow :: Miniature.

mainwindow.cpp

 ..... //item_one = new QStandardItem("<a href=\""+imagePathListCopy.at(i)+"\">"+QFileInfo(imagePathListCopy.at(i)).fileName()+"</a>"); item_one = new QStandardItem(imagePathListCopy.at(i)); model->setItem(rowTableDisk, 1, item_one); ThreadsMiniature *threadB = new ThreadsMiniature(this,imagePathListCopy.at(i),rowTableDisk); connect(threadB, SIGNAL(drowMiniature(QImage)),this,SLOT(Miniature(QImage))); threadB->start(); item_one = new QStandardItem(DeviceFolder); model->setItem(rowTableDisk, 3, item_one); .... void MainWindow::Miniature (QImage image) { QString rowString = image.text("row"); qDebug()<<rowString; int rowTable = rowString.toInt(); item_one = new QStandardItem(); item_one->setData(QVariant(QPixmap::fromImage(image)), Qt::DecorationRole);//DecorationRole model->setItem(rowTable, 2, item_one); //rowMiniature++; }