QImage和线程

时间:2014-07-20 18:40:08

标签: qt qthread

我遇到QImages和Qthreads的问题。 我正在尝试在线程中加载大图像,然后在QLabel上将它们显示为QPixmap。 我的问题是,只要我不使用不同的线程加载QImages,一切都很完美,但只要我使用不同的线程,什么都不是渲染器。 虽然我的QImage仍然有一个有效的大小。

令我困惑的是,如果我只是在cpp中注释第22行将加载器移动到另一个线程,那么标签会很好地显示。

有没有人有想法?

这是我非常简化的代码: 标题:

class Loader : public QObject
{
    Q_OBJECT
public:
    explicit Loader(QObject *parent = 0);

signals:
    void imageLoaded(QString, const QImage &);
public slots:
    void loadImage(const QString& fichier);
};

namespace Ui {
class MainWindow;
}
class LoaderImages;

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

signals:
    void loadImage(const QString& dossier);
private slots:
    void imageAvailable(const QString& dossier, const QImage& img);

private:
    Ui::MainWindow *ui;
    //QString mDossier;
    Loader* mLoader;
    //QMap<QString, QImage*> mMapDesImages;
    int mWidth;
};

CPP:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QPixmap>
#include <QImage>
#include <QDir>
#include <QThread>
#include <QDebug>
#include <QLabel>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    mLoader(new Loader(NULL)),
    mWidth(0)
{
    ui->setupUi(this);

    QThread* thread = new QThread(this);
    mLoader->moveToThread(thread);
    thread->start();

    connect(this, SIGNAL(loadImage(QString)), mLoader, SLOT(loadImage(QString)));
    connect(mLoader, SIGNAL(imageLoaded(QString,QImage)), this, SLOT(imageAvailable(QString,QImage)));

    emit loadImage("C:/img.jpg");
}

void MainWindow::imageAvailable(const QString &dossier, const QImage& img)
{
    mWidth += (img.width() + 20);
    ui->mScrollContent->setMinimumSize(mWidth,img.height());
    QLabel* lab = new QLabel(ui->mScrollContent);
    lab->setFixedSize(img.width(), img.height());
    lab->setGeometry(mWidth - img.width() + 20, 0, img.width(), img.height());
    lab->setPixmap(QPixmap::fromImage(img));
}

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

Loader::Loader(QObject *parent) :
    QObject(parent)
{
}


void Loader::loadImage(const QString& fichier)
{
    QImage* image = new QImage(fichier);

    emit imageLoaded(fichier, *image);
}

THX!

1 个答案:

答案 0 :(得分:3)

有几个错误:

  1. 您没有显示标签。当图像加载器位于GUI线程中时,将加载图像,并在主窗口显示之前将标签添加到内容窗格中。由于显示了父级,因此子项变得可见。

    当在另一个线程中完成加载时,您将向已经显示的窗口小部件添加图像标签。除非您明确show(),否则此类子窗口小部件可见。

  2. 您正在loadImage泄漏图片。没有理由将QImage放在堆上。

  3. 您允许运行QThread被破坏。这是一个常见错误,因为QThread基本上被设计破坏了。 Sane C ++类应该始终是可破坏的。 QThread不是。因此,您需要一种解决方法。

  4. 您也没有设置内容小部件的最小高度

  5. 您可能希望考虑使用QtConcurrent::run而不是专用线程。当您正在进行的操作是单线或多或少时,这尤其值得。我已经展示了两者,这些实现在运行时交替进行。请注意,您需要将concurrent模块和CONFIG += c++11添加到项目文件中。

  6. 样式错误:

    • 对于已经为零的默认值参数,没有理由传递NULL。

    • 如果这些成员与父对象一起构造,则没有理由保留具有父对象生命周期的QObject成员。

    • 仅仅因为Qt Creator附带了愚蠢的模板文件并不意味着您不应该使用std::unique_ptrQScopedPointer来保留ui成员。裸指针应该几乎不会成为成员,除非他们指向父母的QObjects

  7. 由于缺少相当多的代码,我无法确定其他可能出错的地方。以下是一个完整的例子。

    // https://github.com/KubaO/stackoverflown/tree/master/questions/image-loader-24853687
    #include <QtWidgets>
    #include <QtConcurrent>
    
    class Thread final : public QThread {
    public:
        ~Thread() { quit(); wait(); }
    };
    
    class Loader : public QObject
    {
        Q_OBJECT
    public:
        explicit Loader(QObject *parent = nullptr) : QObject(parent) {}
        Q_SIGNAL void imageLoaded(const QString &, const QImage &);
        Q_SLOT void loadImage(const QString& fichier) {
            QImage img(fichier);
            if (! img.isNull()) emit imageLoaded(fichier, img);
        }
    };
    
    class MainWindow : public QWidget
    {
        Q_OBJECT
        Loader m_loader;
        Thread m_loaderThread;
        QGridLayout m_layout{this};
        QPushButton m_open{"Open"};
        QScrollArea m_view;
        QWidget m_content;
        int m_width{};
        bool m_threadImpl = true;
        Q_SIGNAL void loadImage(const QString &);
        Q_SIGNAL void imageLoaded(const QString &, const QImage & img);
        Q_SLOT void imageAvailable(const QString &, const QImage & img) {
            int spacing = 20;
            if (m_width) m_width += spacing;
            auto lab = new QLabel(&m_content);
            lab->setFixedSize(img.width(), img.height());
            lab->setGeometry(m_width, 0, img.width(), img.height());
            lab->setPixmap(QPixmap::fromImage(img));
            lab->show();
            m_width += img.width();
            m_content.setMinimumWidth(m_width);
            m_content.setMinimumHeight(qMax(m_content.minimumHeight(), img.height()));
        }
        Q_SLOT void open() {
            auto dialog = new QFileDialog(this);
            dialog->setAttribute(Qt::WA_DeleteOnClose);
            dialog->show();
            if (m_threadImpl)
                connect(dialog, &QFileDialog::fileSelected, this, &MainWindow::loadImage);
            else
                connect(dialog, &QFileDialog::fileSelected, [this](const QString & fichier){
                    QtConcurrent::run([this, fichier]{
                        QImage img(fichier);
                        if (! img.isNull()) emit this->imageLoaded(fichier, img);
                    });
                });
            m_threadImpl = !m_threadImpl;
        }
    public:
        explicit MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
            m_layout.addWidget(&m_open);
            m_layout.addWidget(&m_view);
            m_view.setWidget(&m_content);
            m_loader.moveToThread(&m_loaderThread);
            m_loaderThread.start();
            connect(&m_open, &QPushButton::clicked, this, &MainWindow::open);
            connect(this, &MainWindow::loadImage, &m_loader, &Loader::loadImage);
            connect(this, &MainWindow::imageLoaded, this, &MainWindow::imageAvailable);
            connect(&m_loader, &Loader::imageLoaded, this, &MainWindow::imageAvailable);
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    
    #include "main.moc"