42

I already tried several methods on displaying an image on a form, but none of them works how I would like.

I've read many places that the easiest way is to create a label and use that to display the image. I have a label, which size is specified by the layout, but if I load an image into it with a pixmap, the label is resized to the size of the image. If I use img tag as text or css background property, it won't display the whole image. What I would like to do is to load the image and fit into the label, not changing the label's size, but when I resize my window, and by that resizing the label as well, the image should be resized too so it will always fit into it.

If the only method is to get the label's size, and resize the pixmap so it would fit, and handle the resize event (signal), how could I resize the pixmap? I hope I won't need to save the whole thing into a QImage and create a pixmap from it each time.

Also, how can I center it? If it can't fit both the width and the height, I would like the smaller dimension to be centered.

Oh, and I don't want to use sliders to handle overflows.

Tetsujin no Oni
  • 7,300
  • 2
  • 29
  • 46
Máthé Endre-Botond
  • 4,826
  • 2
  • 29
  • 48

9 Answers9

35

Actually there is a very simple solution for this problem. There are two things you should modify:

  1. Set the scaled content to true (mentioned above)
  2. Set the label's size policy to ignored

    QLabel lblImage;
    
    lblImage->setPixmap( QPixmap( "big_image.jpg" ) );
    
    lblImage->setScaledContents( true );
    
    lblImage->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
    

If the lblImage is resizing automatically, the image will stretch to the size of the label.

Waqar
  • 8,558
  • 4
  • 35
  • 43
bukkfa
  • 373
  • 3
  • 9
27

Does QLabel::setScaledContents(bool) help? There may also be some useful information in the image viewer example too.

meakgoz
  • 568
  • 4
  • 18
Arnold Spence
  • 21,942
  • 7
  • 74
  • 67
  • 3
    For a fast solution this was a saver, but it gave me a pretty ugly result by stretching the image (i.e.: not keeping the aspect ratio). I will mark this as solution anyway. Thanks. – Máthé Endre-Botond Apr 22 '11 at 22:44
  • 2
    Using PyQt5, this solution does not work. `setScaledContents` does not seem to have any effect on the displayed image size. – ely May 15 '17 at 20:40
  • 4
    `ui->label->setPixmap( pix.scaled( ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) );` worked like charm for me. `pix` is a `QPixmap` object with the original pixel count (e.g. constructed with `QPixmap::fromImage(path)`). – Paulo Carvalho May 17 '20 at 11:32
25

Keep a copy of your original pixmap around. Then connect the resized signal to a slot (or override the resizeEvent() function) that implements this :

lblImage->setPixmap(pixmap.scaled(lblImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Tim MB
  • 4,413
  • 4
  • 38
  • 48
  • pixmap.scaled - creates a scaled copy, thus additional memory will be used. – iperov Mar 21 '20 at 19:14
  • 2
    Yes, you need your scaled version to be a copy. If you just resized the same image then a shrinking followed by an enlarging would cause the quality to deterioriate. – Tim MB Apr 27 '20 at 15:50
15

I will also answer my own question, but won't mark it as solution, because I requested a simple one which was given above. I ended up using a not too simple solution after all, so anyone who also needs to do something similar and has the time to play with it here's my final working code. The idea is to expand the QLabel and overload the setPixmap and the drawEvent methods.

QPictureLabel.hpp (header file)

#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 (implementation)

#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);
}

Usage : the same as using a normal label for displaying image wirthout the 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);
Máthé Endre-Botond
  • 4,826
  • 2
  • 29
  • 48
  • great reply, im only having problem with my parent widget, it bugs – fredcrs May 03 '12 at 19:49
  • it bugs when I change the pixmap and resize screen – fredcrs May 03 '12 at 20:12
  • This was pretty long ago (1 year is just too much for me), I don't even have Qt installed anymore, but I might be able to help. What error are you getting and at which line? Have you tried debugging? Or how does it "bug"? The best thing you could do here, is to post a followup question here on stackoverflow, thus you'll get a bigger chance for an answer from the whole user-base. – Máthé Endre-Botond May 04 '12 at 20:09
  • I am sorry, I had a label that was resized when my main window was resized too, so the label´s background was having a problem, not the image inside the label itself - your code- I fixed that making my label with a background- label->setAutoFillBackground(true); – fredcrs May 06 '12 at 04:52
  • 2
    Thank you so much for this QLabel extension! Saved me hours of work. – Greg Kramida Oct 27 '12 at 21:05
3

In Qt Designer, you must set two things:

  1. Set the pixmap as the Image you want
  2. Check scaledContents

As per image below:

enter image description here

Here, the label on the bottom is scaledContents checked and the label on the top is not.

enter image description here

Matheus Torquato
  • 1,293
  • 18
  • 25
2

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

Worked like charm for my case (Qt 5.11). pix is a QPixmap object with the original pixel count (e.g. constructed with QPixmap::fromImage(path)).

Paulo Carvalho
  • 554
  • 5
  • 10
1
QPixmap pic = QPixmap(":/resource/xxx.png").scaled(16,16,Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

ui->yourLable->setPixmap(pic);
oscarz
  • 1,184
  • 11
  • 19
1

The Python Solution

# 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_()
BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28
0

For python users the solution would work like this:

first keep the original "setQPixmap" line

self.thumbnail.setPixmap(QPixmap(image))

Then add the following line:

self.thumbnail.setScaledContents(1)

The "1" sets the "setScaledContents" to true which scales the image to fit the current size of Qlabel at all times.