Правильный способ потоков в Qt

У меня много времени на загрузку изображения (изображение большое), также некоторые операции над ним выполняются при загрузке. Я не хочу блокировать графический интерфейс приложения.

Моя идея состоит в том, чтобы загрузить изображение в другой поток, подать сигнал, что изображение загружено, а затем перерисовать представление с этим изображением.

Мой подход:

void Window::loadImage()
{
ImageLoader* loaderThread = new ImageLoader();
connect(loaderThread,SIGNAL(imageLoaded()),this,SLOT(imageLoadingFinished());
loaderThread->loadImage(m_image, m_imagesContainer, m_path);
}
void Window::imageLoadingFinished()
{
m_imagesContainer->addImage(m_image);
redrawView();
}

class ImageLoader : public QThread
{
Q_OBJECT
public:
ImageLoader(QObject *parent = 0) : m_image(NULL), m_container(NULL)

void loadImage(Image* img, Container* cont, std::string path)
{
m_image = img;
m_container = cont;
...
start();
}
signals:
void imageLoaded();
protected:
void run()
{
//loading image and operations on it
emit imageLoaded();
}
protected:
Image* m_image;
Container* m_container;
}

Я основывался на quedcustomtype пример из Qt написания этого кода. При поиске и поиске в stackoverflow я также обнаруживаю, что подклассы QThread не очень хорошая идея

Так что вопрос в том, как правильно это сделать? Как я уже сказал, я хочу неблокирующий графический интерфейс, загрузку и операции, выполняемые в другом потоке, и сигнал, который говорит, что загрузка завершена. После подачи сигнала вид должен быть перерисован.
Я не знаю много о многопоточности, но думаю, чтобы понять или иметь достаточно знаний, чтобы понять основные идеи.

5

Решение

Я полагаю, это лучший способ пойти:

#include <QApplication>
#include <QLabel>
#include <QThread>

class ImageLoader : public QObject
{
Q_OBJECT
public:
ImageLoader() : QObject() {
moveToThread(&t);
t.start();
}
~ImageLoader() {
qDebug("Bye bye!");
t.quit();
t.wait();
}

void requestImage(QString absPath) {
QMetaObject::invokeMethod(this, "loadImage", Q_ARG(QString, absPath));
}

public slots:
void loadImage(QString absPath) {
// Simulate large image.
QImage image(absPath);
sleep(10);
qDebug("Image loaded!");
emit imageReady(image);
}

signals:
void imageReady(QImage image);

private:
QThread t;
};

class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel() : QLabel() {}

void mousePressEvent(QMouseEvent* ev) {
Q_UNUSED(ev);
qDebug("I got the event!");
}

public slots:
void setImage(QImage image) {
setPixmap(QPixmap::fromImage(image));
resize(image.width(), image.height());
qDebug("Image shown!");
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

MyLabel label;
label.show();

ImageLoader imageLoader;
QObject::connect(&imageLoader, SIGNAL(imageReady(QImage)), &label, SLOT(setImage(QImage)));
imageLoader.requestImage(some_abs_path);

return a.exec();
}

#include "main.moc"

Мне также нравится QtConcurrent, но учтите, что его использование как-то не рекомендуется: http://www.mail-archive.com/[email protected]/msg07794.html.

3

Другие решения

использование QtConcurent фреймворк.

#include <QtConcurentRun>
#include <QFutureWatcher>

//....
class Window: public QWidget /*or something*/
{
//....
private:
QFutureWatcher<QImage> _wacther; //this object will signal when loading finished
};

//...

void Window::loadImage()
{
connect(&_watcher, SIGNAL(finished(), SLOT(finishLoading());
_wacther.setFuture(QtConcurent::run<QImage>(this, &Window::doLoadImage));
}

QImage Window::doLoadImage() //this function will be executed in the new thread. SHOULD BE Thread Safe
{
return someImage;
}

void window::finishLoading()
{
QImage result = _watcher.result();
}
6

По вопросам рекламы [email protected]