Мне надо обрабатывать отображение миниатюр фотографий в отдельном потоке, иначе код очень долго обрабатывается. У меня есть в основном классе class MainWindow модель и переменная:
mainwindow.h
public:
static QStandardItemModel * model = new QStandardItemModel;
static QStandardItem *item_one;
static int rowTableDisk = 0;
Я создаю поток для добавления миниатюр так:
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);
}
В основном коде заполняю QTableView так :
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);
}
Но ошибки :
Как использовать в потоке static QStandardItemModel * model = new QStandardItemModel;
и static int rowTableDisk = 0;
?
Не поместилось в комментарий, оставлю как ответ:
C потоками лучше так не работать.
Есть как минимум QThreadPool
, а также Qt Concurrent
- я правда в нём не разбираюсь, цитата из документации - 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.
Наследоваться от QThread не рекомендуется - Правильное использование QThread. Если вкратце - QThread в первую очередь нужен чтобы запустить QEventLoop в другом потоке и иметь возможность слать сигналы в этот поток и принимать их оттуда. Подробнее - прочитайте статью по ссылке на хабре.
Оригинальные статьи:
youre-doing-it-wrong QThread
how to realy use qthreads
* UPD. *
Прочитал исходники.
У вас получается что в цикле будет создано несколько потоков, и каждый из них будет обращаться к одному и тому же статическому элементу (item_one
) ?
Без синхронизации ?
* UPD 2. Пример. *
Я сделал предположение о том что Вам требуется, и сделал пример.
Пример грузит из некоторой папки все картинки форматов jpg/png
в таблицу с двумя столбцами - имя картинки и preview. Код получения preview скопировал из вашего метода run
в потоке
Класс ImageLoader
- наследник QObject
(чтобы умел слать сигнал) и QRunnable
(чтобы можно было добавлять в QThreadPool).
Я не уверен что так правильно, потому-что метода moveToThread
QThreadPool
скорей всего не вызывает, так как на вход ожидает обычный QRunnable
интерфейс, но всё же это лучше чем наследоваться от 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;
}
}
На форме одна кнопка для выбора директории и TableView для отображения модели:
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>
Как выглядит:
Вывод в консоль:
"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
Как видим, было задействовано до 4 потоков.
Чуть не забыл - в main.cpp пришлось зарегистрировать QFileInfo, чтобы слать его в сигнале в качестве параметра:
#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();
}
Возможно не корректный код, но рабочий и быстрый :
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
Более двух значений в функцию MainWindow::Miniature, можно передать с помощью
QImage image = imageReader.read();
image.setText("row", QString("%1").arg(_rowTableDisk));
Так как QVector< QImage > не имеет метода SetObjectName:
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);
}
Обойти обращение к модели(QStandardItemModel), в потоке можно вынеся в отделенную функцию 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++;
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Использую cron и quartzНужно реализовать следующие: Администратор назначает на конкретную дату некое задание, которое обновляет базу данных
У меня есть серверное приложение на JavaВозникла необходимость запускать пользовательские скрипты, для этого я решил использовать Python, так...
Всем приветСтолкнулся с такой проблемой: пытаюсь программно работать с каталогом, но сталкиваюсь с тем, что студия на проверке существования...