QImage 和线程 [英] QImage and Threads
问题描述
我在使用 QImages 和 Qthreads 时遇到问题.我正在尝试在线程中加载大图像,然后在 QLabel 上将它们显示为 QPixmap.我的问题是,只要我不使用不同的线程来加载 QImages,一切都是完美的,但是只要我使用不同的线程,就没有渲染器.虽然我的 QImage 仍然有一个有效的大小.
I am having problems with QImages and Qthreads. I am trying to load big images in a Thread and then display them as QPixmap on a QLabel. My problem is that as long as I don't use a different thread to load the QImages, everything is perfect but as soon as I use a different thread, nothing is renderder. Though I still have a valid size for my QImage.
让我感到困惑的是,如果我只是评论 cpp 中将加载器移动到另一个线程的第 22 行,标签显示得很好.
The thing that puzzles me is that, if I just comment the 22nd line in the cpp that moves the loader to the other thread, the label displays nicely.
有人有想法吗?
这是我非常简化的代码:标题:
Here is my very simplified code: Header :
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);
}
谢谢!
推荐答案
有几个错误:
您没有显示标签.当图像加载器在 GUI 线程中时,图像被加载并且标签在主窗口显示之前被添加到内容窗格.由于显示了父级,因此子级变得可见.
You're not showing the label. When the image loader is in the GUI thread, the image is loaded and the label added to the contents pane before the main window is shown. Since the parent is shown, the children become visible.
在另一个线程中完成加载后,您将向已显示的小部件添加图像标签.除非您明确show()
,否则此类子小部件不可见.
When the loading is done in another thread, you'll be adding image labels to a widget that's already shown. Such child widgets are not visible unless you explicitly show()
them.
您在 loadImage
中泄露了图像.没有理由将 QImage
放在堆上.
You're leaking the image in loadImage
. There's no reason to put that QImage
on the heap.
您允许破坏正在运行的 QThread
.这是一个常见的错误,因为 QThread
基本上被设计破坏了.理智的 C++ 类应该总是可破坏的.QThread
不是.因此,您需要一个解决方法.
You're allowing a running QThread
to be destructed. That's a common error since QThread
is essentially broken by design. Sane C++ classes should be always destructible. QThread
isn't. Thus you need a workaround.
您还没有设置内容小部件的最小高度.
You're not setting the minimum height of the contents widget as well.
您可能希望考虑使用 QtConcurrent::run
而不是专用线程.当您进行的操作或多或少是单班轮时,这尤其值得.我已经展示了两者,实现在运行时交替.注意需要在项目文件中添加concurrent
模块和CONFIG += c++11
.
You might wish to consider the use QtConcurrent::run
instead of a dedicated thread. This is especially worthwhile when the operation you're undertaking is a one liner, more or less. I've shown both, the implementations are alternated between at runtime. Note that you need to add the concurrent
module and CONFIG += c++11
to the project file.
风格错误:
没有理由为已经为零的默认值参数传递 NULL.
There's no reason to pass NULL for default-valued parameters that are already zero.
没有理由将具有父对象生命周期的 QObject
成员保留在堆上,如果这些成员是与父对象一起构造的.
There's no reason to keep QObject
members that have the lifetime of the parent object on the heap, if such members are constructed along with the parent object.
仅仅因为 Qt Creator 带有愚蠢的模板文件并不意味着您不应该使用 std::unique_ptr
或 QScopedPointer
来保存ui
成员.裸指针几乎永远不应该是成员,除非它们是指向带有父对象的 QObjects
的指针.
Just because Qt Creator comes with silly template files doesn't mean that you shouldn't be using a std::unique_ptr
or QScopedPointer
to hold the ui
member. Naked pointers should almost never be members unless they're pointers to QObjects
with parents.
由于缺少相当多的代码,我真的不知道还有什么可能是错误的.下面是一个完整的例子.
As quite a bit of the code is missing, I can't really tell what else might be wrong. Below is a complete example.
// 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"
这篇关于QImage 和线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!