带有像素图图像的 QLabel 变得模糊
QLabel with pixmap image getting blurred
我有一张图片需要显示为 QLabel
的背景。这是我的代码(摘自 Qt 文档 here):
#include <QtWidgets>
#include "imageviewer.h"
ImageViewer::ImageViewer()
{
imageLabel = new QLabel;
imageLabel->setBackgroundRole(QPalette::Base);
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
imageLabel->setScaledContents(true);
setCentralWidget(imageLabel);
createActions();
createMenus();
resize(570,357);
}
bool ImageViewer::loadFile(const QString &fileName)
{
QImageReader reader(fileName);
const QImage image = reader.read();
if (image.isNull()) {
QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
tr("Cannot load %1.").arg(QDir::toNativeSeparators(fileName)));
setWindowFilePath(QString());
imageLabel->setPixmap(QPixmap());
imageLabel->adjustSize();
return false;
}
imageLabel->setPixmap(QPixmap::fromImage(image).scaled(size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
return true;
}
void ImageViewer::open()
{
QStringList mimeTypeFilters;
foreach (const QByteArray &mimeTypeName, QImageReader::supportedMimeTypes())
mimeTypeFilters.append(mimeTypeName);
mimeTypeFilters.sort();
const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
QFileDialog dialog(this, tr("Open File"),
picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setMimeTypeFilters(mimeTypeFilters);
dialog.selectMimeTypeFilter("image/jpeg");
while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {}
}
void ImageViewer::createActions()
{
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcut(tr("Ctrl+O"));
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
}
void ImageViewer::createMenus()
{
fileMenu = new QMenu(tr("&File"), this);
fileMenu->addAction(openAct);
menuBar()->addMenu(fileMenu);
}
头文件:
#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H
#include <QMainWindow>
class QAction;
class QLabel;
class QMenu;
class QScrollArea;
class QScrollBar;
class ImageViewer : public QMainWindow
{
Q_OBJECT
public:
ImageViewer();
bool loadFile(const QString &);
private slots:
void open();
private:
void createActions();
void createMenus();
QLabel *imageLabel;
QAction *openAct;
QMenu *fileMenu;
};
#endif
主要:
#include <QApplication>
#include <QCommandLineParser>
#include "imageviewer.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGuiApplication::setApplicationDisplayName(ImageViewer::tr("Image Viewer"));
ImageViewer imageViewer;
imageViewer.show();
return app.exec();
}
如您所见,视口大小为 570 x 357。我使用的图像是 this(大小为 2560 x 1600,两者的宽高比都差不多是 1.6,太大了,无法在此处上传, 所以会上传图片的截图):
但是当我打开应用程序并在其中添加图像时,我在 QLabel
中获得的图像非常模糊:
如何让标签中的图片和实际图片一样清晰(是bmp格式,所以你必须在[=中的文件打开对话框中将mime类型更改为bmp
46=])?
当我通过 QPainter
执行此任务时,即从图像文件中制作 QImage
,然后将其传递给画家并调用 update
,效果很好。但是我需要能够在单击时内联缩放图像(我通过在 QLabel
上调用 resize()
来实现),而 QPainter
方式不允许我缩放图像。
平台 - OS X 10.10(视网膜显示屏),Qt 5.3.1,32 位。
您将图像压缩了 4 倍以上 (2560 -> 570),图像中似乎有一些小细节在压缩后无法保留。
实际上,在将图像缩小这么多之后,您或多或少会得到预期的效果。
您 运行 在 Qt 5.3 的 Retina 显示器 Mac 上的 QLabel 中遇到了一个已知错误:
https://bugreports.qt.io/browse/QTBUG-42503
从你的图像来看,当你使用 QLabel 时,你似乎只是获得了图像的 non-retina 版本,但如果你在 QPainter 中手动编码,你将获得源的完整分辨率Q图像。如果是这样的话,那确实是这个bug造成的。
你有两个解决方案:
滚动你自己的解决方案,不要使用 QLabel。没有理由不使用 QLabel 就不能轻松 "zoom the image inline"(只需使用自定义 QWidget class 和覆盖的 PaintEvent)。
升级到已修复此错误的 Qt 版本。无论如何,这可能是正确的解决方案,除非最新版本中有任何回归导致您的应用程序出现问题。根据错误报告,此问题已在 v5.5.0 中修复,因此最新的 v5.5.1 应该可以正常工作。
第一种方法的示例(为简洁起见,我将 header 留在 header 文件中,它非常简单):
void ImageWidget::setImage(QImage image)
{
//Set the image and invalidate our cached pixmap
m_image = image;
m_cachedPixmap = QPixmap();
update();
}
void ImageWidget::paintEvent(QPaintEvent *)
{
if ( !m_image.isNull() )
{
QSize scaledSize = size() * devicePixelRatio();
if (m_cachedPixmap.size() != scaledSize)
{
QImage scaledImage = m_image.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
m_cachedPixmap = QPixmap::fromImage(scaledImage);
m_cachedPixmap.setDevicePixelRatio(devicePixelRatio());
}
QPainter p(this);
p.drawPixmap(0, 0, m_cachedPixmap);
}
}
这只是一个非常基本的小部件,它只是将 QImage 绘制到其完整大小,尊重 DevicePixelRatio,因此利用了 Retina 分辨率。注意:在 non-retina 机器上编码,所以我不能保证它在 Retina 上工作,但我从 QLabel 的 Qt 5.5.1 修复中获得了基本实现。与 QLabel 一样,它也缓存 Pixmap,因此它不必 re-scale 每次调用 paintEvent 时,除非小部件实际上已被调整大小。
我有一张图片需要显示为 QLabel
的背景。这是我的代码(摘自 Qt 文档 here):
#include <QtWidgets>
#include "imageviewer.h"
ImageViewer::ImageViewer()
{
imageLabel = new QLabel;
imageLabel->setBackgroundRole(QPalette::Base);
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
imageLabel->setScaledContents(true);
setCentralWidget(imageLabel);
createActions();
createMenus();
resize(570,357);
}
bool ImageViewer::loadFile(const QString &fileName)
{
QImageReader reader(fileName);
const QImage image = reader.read();
if (image.isNull()) {
QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
tr("Cannot load %1.").arg(QDir::toNativeSeparators(fileName)));
setWindowFilePath(QString());
imageLabel->setPixmap(QPixmap());
imageLabel->adjustSize();
return false;
}
imageLabel->setPixmap(QPixmap::fromImage(image).scaled(size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
return true;
}
void ImageViewer::open()
{
QStringList mimeTypeFilters;
foreach (const QByteArray &mimeTypeName, QImageReader::supportedMimeTypes())
mimeTypeFilters.append(mimeTypeName);
mimeTypeFilters.sort();
const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
QFileDialog dialog(this, tr("Open File"),
picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setMimeTypeFilters(mimeTypeFilters);
dialog.selectMimeTypeFilter("image/jpeg");
while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {}
}
void ImageViewer::createActions()
{
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcut(tr("Ctrl+O"));
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
}
void ImageViewer::createMenus()
{
fileMenu = new QMenu(tr("&File"), this);
fileMenu->addAction(openAct);
menuBar()->addMenu(fileMenu);
}
头文件:
#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H
#include <QMainWindow>
class QAction;
class QLabel;
class QMenu;
class QScrollArea;
class QScrollBar;
class ImageViewer : public QMainWindow
{
Q_OBJECT
public:
ImageViewer();
bool loadFile(const QString &);
private slots:
void open();
private:
void createActions();
void createMenus();
QLabel *imageLabel;
QAction *openAct;
QMenu *fileMenu;
};
#endif
主要:
#include <QApplication>
#include <QCommandLineParser>
#include "imageviewer.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGuiApplication::setApplicationDisplayName(ImageViewer::tr("Image Viewer"));
ImageViewer imageViewer;
imageViewer.show();
return app.exec();
}
如您所见,视口大小为 570 x 357。我使用的图像是 this(大小为 2560 x 1600,两者的宽高比都差不多是 1.6,太大了,无法在此处上传, 所以会上传图片的截图):
但是当我打开应用程序并在其中添加图像时,我在 QLabel
中获得的图像非常模糊:
如何让标签中的图片和实际图片一样清晰(是bmp格式,所以你必须在[=中的文件打开对话框中将mime类型更改为bmp
46=])?
当我通过 QPainter
执行此任务时,即从图像文件中制作 QImage
,然后将其传递给画家并调用 update
,效果很好。但是我需要能够在单击时内联缩放图像(我通过在 QLabel
上调用 resize()
来实现),而 QPainter
方式不允许我缩放图像。
平台 - OS X 10.10(视网膜显示屏),Qt 5.3.1,32 位。
您将图像压缩了 4 倍以上 (2560 -> 570),图像中似乎有一些小细节在压缩后无法保留。
实际上,在将图像缩小这么多之后,您或多或少会得到预期的效果。
您 运行 在 Qt 5.3 的 Retina 显示器 Mac 上的 QLabel 中遇到了一个已知错误: https://bugreports.qt.io/browse/QTBUG-42503
从你的图像来看,当你使用 QLabel 时,你似乎只是获得了图像的 non-retina 版本,但如果你在 QPainter 中手动编码,你将获得源的完整分辨率Q图像。如果是这样的话,那确实是这个bug造成的。
你有两个解决方案:
滚动你自己的解决方案,不要使用 QLabel。没有理由不使用 QLabel 就不能轻松 "zoom the image inline"(只需使用自定义 QWidget class 和覆盖的 PaintEvent)。
升级到已修复此错误的 Qt 版本。无论如何,这可能是正确的解决方案,除非最新版本中有任何回归导致您的应用程序出现问题。根据错误报告,此问题已在 v5.5.0 中修复,因此最新的 v5.5.1 应该可以正常工作。
第一种方法的示例(为简洁起见,我将 header 留在 header 文件中,它非常简单):
void ImageWidget::setImage(QImage image)
{
//Set the image and invalidate our cached pixmap
m_image = image;
m_cachedPixmap = QPixmap();
update();
}
void ImageWidget::paintEvent(QPaintEvent *)
{
if ( !m_image.isNull() )
{
QSize scaledSize = size() * devicePixelRatio();
if (m_cachedPixmap.size() != scaledSize)
{
QImage scaledImage = m_image.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
m_cachedPixmap = QPixmap::fromImage(scaledImage);
m_cachedPixmap.setDevicePixelRatio(devicePixelRatio());
}
QPainter p(this);
p.drawPixmap(0, 0, m_cachedPixmap);
}
}
这只是一个非常基本的小部件,它只是将 QImage 绘制到其完整大小,尊重 DevicePixelRatio,因此利用了 Retina 分辨率。注意:在 non-retina 机器上编码,所以我不能保证它在 Retina 上工作,但我从 QLabel 的 Qt 5.5.1 修复中获得了基本实现。与 QLabel 一样,它也缓存 Pixmap,因此它不必 re-scale 每次调用 paintEvent 时,除非小部件实际上已被调整大小。