在Qt中显示图片以适应标签大小

42

我已经尝试了几种在表单上显示图像的方法,但没有一种是我想要的。

我看过很多地方都说最简单的方法是创建一个标签并使用它来显示图像。我有一个标签,其大小由布局指定,但如果我用 pixmap 将图片加载到标签中,标签会被调整为图像的大小。如果我将img标签作为文本或css背景属性使用,则不会显示整个图像。我想做的是将图像加载并适应标签,而不改变标签的大小。但当我调整窗口大小并因此调整标签的大小时,图片也应该被调整大小,以便始终适合标签中。

如果唯一的方法是获取标签的大小,并调整 pixmap 使其适合,并处理大小调整事件(信号),那么我该如何调整 pixmap 的大小呢?我希望不需要每次将整个图像保存到 QImage 中并从中创建一个 pixmap 。

另外,如何让它居中显示?如果无法同时适应宽度和高度,则希望将较小的尺寸居中显示。

哦,我不想使用滑块来处理溢出。

9个回答

35

其实这个问题有一个非常简单的解决方案。你需要修改两个东西:

  1. 将缩放内容设置为true(如上所述)
  2. 将标签的大小策略设置为ignored

QLabel lblImage;

lblImage->setPixmap( QPixmap( "big_image.jpg" ) );

lblImage->setScaledContents( true );

lblImage->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
如果 `lblImage` 自动调整大小,该图像将会被拉伸到标签的大小。

我猜保持图像的宽高比没有简单的解决方案吧? - Eric Lemanissier
“ignored”的问题在于标签无法用于“展开”布局。 - Mr. Developerdude
1
使用PyQt5,这也不起作用。QSizePolicy.Ignored似乎对图像是否调整大小没有影响。 - ely
这不会保持图像的纵横比。它将被水平/垂直拉伸以适应标签。 - Bojan Bogdanovic

27

3
对于快速解决问题来说,这是一个救星,但它通过拉伸图像(即:不保持纵横比)给了我一个相当丑陋的结果。无论如何,我将把它标记为解决方案。谢谢。 - Máthé Endre-Botond
2
使用PyQt5,这个解决方案不起作用。setScaledContents似乎对显示的图像大小没有任何影响。 - ely
4
ui->label->setPixmap( pix.scaled( ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) ); 对我非常好用。 pix 是一个包含原始像素数量的 QPixmap 对象(例如通过使用 QPixmap::fromImage(path) 构造)。 - Paulo Carvalho

25

保留原始的pixmap复制。然后将resized信号连接到实现以下功能的插槽(或覆盖resizeEvent()函数):

lblImage->setPixmap(pixmap.scaled(lblImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

pixmap.scaled - 创建一个缩放副本,因此会使用额外的内存。 - iperov
2
是的,您需要将缩放版本制作成副本。如果您只是调整了同一图像的大小,则缩小后再放大会导致质量下降。 - Tim MB

15

我也会回答自己的问题,但不会将其标记为解决方案,因为我要求一个简单的解决方案,而上面已经给出了。最终我使用了一个并不太简单的解决方案,所以任何需要做类似事情并有时间尝试的人,这是我的最终工作代码。思路是扩展QLabel并重载setPixmap和drawEvent方法。

QPictureLabel.hpp(头文件)

#include "QImage.h"
#include "QPixmap.h"
#include "QLabel.h"

class QPictureLabel : public QLabel
{
private:
    QPixmap _qpSource; //preserve the original, so multiple resize events won't break the quality
    QPixmap _qpCurrent;

    void _displayImage();

public:
    QPictureLabel(QWidget *aParent) : QLabel(aParent) { }
    void setPixmap(QPixmap aPicture);
    void paintEvent(QPaintEvent *aEvent);
};

QPictureLabel.cpp(实现文件)

#include "QPainter.h"

#include "QPictureLabel.hpp"

void QPictureLabel::paintEvent(QPaintEvent *aEvent)
{
    QLabel::paintEvent(aEvent);
    _displayImage();
}

void QPictureLabel::setPixmap(QPixmap aPicture)
{
    _qpSource = _qpCurrent = aPicture;
    repaint();
}

void QPictureLabel::_displayImage()
{
    if (_qpSource.isNull()) //no image was set, don't draw anything
        return;

    float cw = width(), ch = height();
    float pw = _qpCurrent.width(), ph = _qpCurrent.height();

    if (pw > cw && ph > ch && pw/cw > ph/ch || //both width and high are bigger, ratio at high is bigger or
        pw > cw && ph <= ch || //only the width is bigger or
        pw < cw && ph < ch && cw/pw < ch/ph //both width and height is smaller, ratio at width is smaller
        )
        _qpCurrent = _qpSource.scaledToWidth(cw, Qt::TransformationMode::FastTransformation);
    else if (pw > cw && ph > ch && pw/cw <= ph/ch || //both width and high are bigger, ratio at width is bigger or
        ph > ch && pw <= cw || //only the height is bigger or
        pw < cw && ph < ch && cw/pw > ch/ph //both width and height is smaller, ratio at height is smaller
        )
        _qpCurrent = _qpSource.scaledToHeight(ch, Qt::TransformationMode::FastTransformation);

    int x = (cw - _qpCurrent.width())/2, y = (ch - _qpCurrent.height())/2;

    QPainter paint(this);
    paint.drawPixmap(x, y, _qpCurrent);
}

用法:与正常标签显示图像的方式相同,但不使用setScaledContents。

img_Result = new QPictureLabel(ui.parent);
layout = new QVBoxLayout(ui.parent);
layout->setContentsMargins(11, 11, 11, 11);
ui.parent->setLayout(layout);
layout->addWidget(img_Result);

//{...}

QPixmap qpImage(qsImagePath);
img_Result->setPixmap(qpImage);

很好的回复,我只是在处理我的父部件时遇到了问题,它出现了错误。 - fredcrs
当我改变像素图并调整屏幕大小时,它会出现错误。 - fredcrs
这是相当久以前的事情了(对我来说,一年太长了),我甚至不再安装Qt了,但我可能能提供帮助。你遇到了什么错误?在哪一行?你尝试过调试吗?或者它是如何“出错”的?在这里,你最好做的事情就是在stackoverflow上发布一个跟进问题,这样你就有更大的机会从整个用户群体中获得答案。 - Máthé Endre-Botond
很抱歉,我的一个标签在主窗口调整大小时也被调整了大小,因此标签的背景出现了问题,而不是标签内部的图像 - 您的代码。我通过设置标签的背景来解决这个问题 - label->setAutoFillBackground(true); - fredcrs
2
非常感谢您提供的QLabel扩展!它为我节省了数小时的工作时间。 - Greg Kramida

3
在Qt Designer中,您必须设置以下两个内容:
  1. 将像素图设置为所需的图像
  2. 检查scaledContents
如下图所示: enter image description here 这里,底部标签已选中scaledContents,而顶部标签未选中。 enter image description here

2

ui->label->setPixmap( pix.scaled( ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) );

对于我的情况(Qt 5.11),这个方法非常好用。其中pix是一个QPixmap对象,拥有原始像素计数(例如使用QPixmap::fromImage(path)构造)。


1

Python的解决方案

# Created by BaiJiFeiLong@gmail.com at 2022/2/4 11:28

import urllib.request

from PySide2 import QtWidgets, QtGui, QtCore


class PictureLabel(QtWidgets.QLabel):
    def __init__(self, text: str = None, pixmap: QtGui.QPixmap = None):
        super().__init__()
        self._pixmap = None
        text is not None and self.setText(text)
        pixmap is not None and self.setPixmap(pixmap)

    def setPixmap(self, pixmap: QtGui.QPixmap) -> None:
        self._pixmap = pixmap
        self.repaint()

    def paintEvent(self, event: QtGui.QPaintEvent) -> None:
        super().paintEvent(event)
        if self._pixmap is not None:
            imageWidth, imageHeight = self._pixmap.width(), self._pixmap.height()
            labelWidth, labelHeight = self.width(), self.height()
            ratio = min(labelWidth / imageWidth, labelHeight / imageHeight)
            newWidth, newHeight = int(imageWidth * ratio), int(imageHeight * ratio)
            newPixmap = self._pixmap.scaledToWidth(newWidth, QtCore.Qt.TransformationMode.FastTransformation)
            x, y = abs(newWidth - labelWidth) // 2, abs(newHeight - labelHeight) // 2
            QtGui.QPainter(self).drawPixmap(x, y, newPixmap)


app = QtWidgets.QApplication()
pixmap = QtGui.QPixmap()
pixmap.loadFromData(urllib.request.urlopen("https://www.baidu.com/favicon.ico").read())
label = PictureLabel(pixmap=pixmap)
label.resize(800, 600)
label.show()
app.exec_()

1
QPixmap pic = QPixmap(":/resource/xxx.png").scaled(16,16,Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

ui->yourLable->setPixmap(pic);

与@Team MB的答案相同,除了Qt :: IgnoreAspectRatio,我认为它不符合问题。 - Máthé Endre-Botond
1
我实际上找到了另一个更好的解决方案,即ui->yourLabel->setPixmap(QIcon("xx.png").pixmap(16,16)); - oscarz

0
对于Python用户,解决方案如下:
首先保留原始的“setQPixmap”行。
self.thumbnail.setPixmap(QPixmap(image))

然后添加以下行:
self.thumbnail.setScaledContents(1)

“1”将“setScaledContents”设置为true,这会使图像始终按照Qlabel的当前大小进行缩放。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接