
Let’s talk about aspect ratio handling in QLabel.
Displaying a big image on a QLabel is not always straight forward. For instance, we have an image of size 1920 x 1080 pixels, the label size may or may not be sufficient to show the whole image in the size of QLabel itself.
In this tutorial, we will address 2 issues:
QLabel?QLabel follow the aspect ratio of the image it is showing?This is where, aspect ratio comes into the picture. Aspect ratio helps us to keep the entire image inside the size we have available with us.
To preserve aspect ratio, we need to calculate new dimensions for the original height and width of the image.
Lets we have an image of 1920 x 1080 pixels and our width of our QLabel is only 400 pixels. New height of our image, preserving the aspect ratio would be:
1(original height of the image) / (original width of the image) x width of QLabel
2
3(1080 / 1920) x 400 = 225
Usually, i use this awesome and simple aspect ratio calculator.
For this tutorial, we will create a QLabel and load a QPixmap on QPushButton clicked. We will use QDialog as the container and QtDesigner to setup the UI. This is how our simple UI looks like:

We have a button Swap and a QLabel. I have added a stylesheet to our QLabel to make it more obvious. Otherwise, a QLabel without its default text is plain transparent. We will load a QPixmap whenever the button Swap is clicked.
Lets dig into code.
Our main.cpp doesn’t look any special. Plain file created by QtCreator for us.
1#include "Dialog.h"
2#include <QApplication>
3
4int main(int argc, char* argv[])
5{
6 QApplication a(argc, argv);
7 Dialog w;
8 w.show();
9 return a.exec();
10}
To achieve our objective, we need to subclass QLabel and we call it PixmapLabel.
Lets see our PixmapLabel.h
1#ifndef PIXMAPLABEL_H
2#define PIXMAPLABEL_H
3
4#include <QLabel>
5#include <QPixmap>
6
7
8class PixmapLabel : public QLabel
9{
10 public:
11
12 PixmapLabel(QWidget* parent = nullptr);
13 virtual QSize sizeHint() const override;
14
15 void setImage(const QPixmap& image);
16 int heightForWidth(int width) const override;
17
18 protected:
19
20 void paintEvent(QPaintEvent* event) override;
21
22 private:
23
24 QPixmap m_pixmap;
25 int m_cols;
26 int m_rows;
27};
28#endif // PIXMAPLABEL_H
Here we are going to re-implement 3 functions sizeHint(), heightForWidth() and paintEvent().
Here is the implementation file PixmapLabel.cpp:
1#include "PixmapLabel.h"
2#include <QPainter>
3#include <QPaintEvent>
4
5
6
7PixmapLabel::PixmapLabel(QWidget* parent) :
8 QLabel(parent),
9 m_cols(0),
10 m_rows(0)
11{
12 QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
13 sizePolicy.setHeightForWidth(true);
14 setSizePolicy(sizePolicy);
15
16 setMinimumSize(sizeHint());
17
18 // set borders on the QLabel
19 setStyleSheet("QLabel{border: 1px solid black; background: gray;}");
20}
21
22
23
24void PixmapLabel::setImage(const QPixmap& image)
25{
26 m_pixmap = image;
27
28 m_cols = m_pixmap.width();
29 m_rows = m_pixmap.height();
30
31 update();
32}
33
34
35
36/* virtual */ QSize PixmapLabel::sizeHint() const
37{
38 if (m_cols != 0)
39 {
40 int width = this->width();
41 return QSize(width, heightForWidth(width));
42 }
43 else
44 {
45 return QSize(200, 200);
46 }
47}
48
49
50
51/* virtual */ int PixmapLabel::heightForWidth(int width) const
52{
53 return (m_cols != 0) ? width * m_rows / m_cols : this->height();
54}
55
56
57
58/* virtual */ void PixmapLabel::paintEvent(QPaintEvent* event)
59{
60 QLabel::paintEvent(event);
61
62 // if there is no pixmap loaded yet
63 if(m_pixmap.isNull())
64 {
65 return;
66 }
67
68 // Create painter
69 QPainter painter(this);
70
71 // Get current pixmap size
72 QSize pixmapSize = m_pixmap.size();
73
74 // Scale size to painted rect
75 pixmapSize.scale(event->rect().size(), Qt::KeepAspectRatio);
76
77 // Scale the pixmap
78 QPixmap pixmapScaled = m_pixmap.scaled(pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
79
80 // Draw the pixmap
81 painter.drawPixmap(QPoint(), pixmapScaled);
82}
Notice we are not using setPixmap() available in QLabel class but rather we are using paintEvent(). This allows us to resize QLabel and also scale its contents.
paintEvent() is automatically called whenever QLabel is resized or Qt feels like it needs to repaint the contents of the QLabel as in our case a QPixmap. This solves our first issue as to how do we keep the aspect ratio while resizing and we do it without the need to explicitly implementing resizeEvent().
To fix our second issue, we need to manage the width() and height() of the QLabel explicitly in the container of our label i.e. QDialog.
Lets have a look at our Dialog.h
1#ifndef DIALOG_H
2#define DIALOG_H
3
4#include <QDialog>
5
6QT_BEGIN_NAMESPACE
7namespace Ui
8{
9 class Dialog;
10}
11QT_END_NAMESPACE
12
13class Dialog : public QDialog
14{
15 Q_OBJECT
16
17 public:
18
19 Dialog(QWidget* parent = nullptr);
20 ~Dialog();
21
22 private slots:
23
24 void on_pushButton_clicked();
25
26 protected:
27
28 virtual void resizeEvent(QResizeEvent* event) override;
29
30 private:
31
32 Ui::Dialog* ui;
33 bool m_swap;
34 QImage m_image1;
35 QImage m_image2;
36};
37#endif // DIALOG_H
Here is our implementation Dialog.cpp
1#include "Dialog.h"
2#include "ui_Dialog.h"
3
4Dialog::Dialog(QWidget* parent)
5 : QDialog(parent)
6 , ui(new Ui::Dialog)
7 , m_swap(true)
8{
9 ui->setupUi(this);
10 m_image1 = QImage(":/images/images/1.png");
11 m_image2 = QImage(":/images/images/2.jpg");
12}
13
14
15
16Dialog::~Dialog()
17{
18 delete ui;
19}
20
21
22
23void Dialog::on_pushButton_clicked()
24{
25 if(m_swap)
26 {
27 ui->label->setImage(QPixmap::fromImage(m_image1));
28
29 m_swap = false;
30 }
31 else
32 {
33 ui->label->setImage(QPixmap::fromImage(m_image2));
34
35 m_swap = true;
36 }
37
38 // force QLabel to follow fixed dimensions
39 // based on the loaded QPixmap aspect ratio
40 int w = ui->label->width();
41 int h = ui->label->heightForWidth(w);
42
43 ui->label->setFixedHeight(h);
44}
45
46
47
48void Dialog::resizeEvent(QResizeEvent* event)
49{
50 Q_UNUSED(event);
51
52 // force QLabel to follow fixed dimensions
53 // based on the loaded QPixmap aspect ratio
54 int w = ui->label->width();
55 int h = ui->label->heightForWidth(w);
56
57 ui->label->setFixedHeight(h);
58}
We force the QLabel to follow fixed dimensions.
Here we have used it on 2 occasions, first when we set an image and second is in resizeEvent() of our QDialog. Whenever our QDialog is resized we resize our QLabel based on the formula we derived above. This not only allows us to preserve the aspect ratio of the image but also forces the QLabel to follow along.
This whole project can be found on my GitHub account.
Feel free to share this blog, if you feel it helped you. If you have any comments or suggestions, post it in the comments section below.
Photo by Daniil Kuželev on Unsplash