Qt: resizing a QLabel containing a QPixmap while maintaining its aspect ratio

I use QLabel to display the contents of a larger, dynamically changing QPixmap for the user. It would be nice to make this label smaller / larger depending on available space. Screen size is not always as large as QPixmap.

How can I change QSizePolicy and sizeHint() QLabel to resize the QPixmap while maintaining the aspect ratio of the original QPixmap?

I cannot change the sizeHint() QLabel, setting minimumSize() to zero does not help. Setting hasScaledContents() on QLabel allows you to grow, but violates the aspect ratio ...

Subclassing QLabel really helped, but this solution adds too much code for a simple problem ...

Any smart hints on how to do this without a subclass?

+43
c ++ qt qt4 qlabel
Nov 21 '11 at 12:43
source share
4 answers

To resize the label, you can select the appropriate size policy for the label, for example, extension or minimum extension.

You can scale the pixmap by preserving its aspect ratio each time it changes:

 QPixmap p; // load pixmap // get label dimensions int w = label->width(); int h = label->height(); // set a scaled pixmap to awxh window keeping its aspect ratio label->setPixmap(p.scaled(w,h,Qt::KeepAspectRatio)); 

There are two places where you should add this code:

  • When updating pixmap
  • In resizeEvent widget containing label
+52
Nov 21 '11 at 12:53
source share

I polished this missing QLabel subclass. It is awesome and works well.

aspectratiopixmaplabel.h

 #ifndef ASPECTRATIOPIXMAPLABEL_H #define ASPECTRATIOPIXMAPLABEL_H #include <QLabel> #include <QPixmap> #include <QResizeEvent> class AspectRatioPixmapLabel : public QLabel { Q_OBJECT public: explicit AspectRatioPixmapLabel(QWidget *parent = 0); virtual int heightForWidth( int width ) const; virtual QSize sizeHint() const; QPixmap scaledPixmap() const; public slots: void setPixmap ( const QPixmap & ); void resizeEvent(QResizeEvent *); private: QPixmap pix; }; #endif // ASPECTRATIOPIXMAPLABEL_H 

aspectratiopixmaplabel.cpp

 #include "aspectratiopixmaplabel.h" //#include <QDebug> AspectRatioPixmapLabel::AspectRatioPixmapLabel(QWidget *parent) : QLabel(parent) { this->setMinimumSize(1,1); setScaledContents(false); } void AspectRatioPixmapLabel::setPixmap ( const QPixmap & p) { pix = p; QLabel::setPixmap(scaledPixmap()); } int AspectRatioPixmapLabel::heightForWidth( int width ) const { return pix.isNull() ? this->height() : ((qreal)pix.height()*width)/pix.width(); } QSize AspectRatioPixmapLabel::sizeHint() const { int w = this->width(); return QSize( w, heightForWidth(w) ); } QPixmap AspectRatioPixmapLabel::scaledPixmap() const { return pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } void AspectRatioPixmapLabel::resizeEvent(QResizeEvent * e) { if(!pix.isNull()) QLabel::setPixmap(scaledPixmap()); } 

Hope this helps! (Updated resizeEvent , per @dmzl answer)

+19
Mar 24 '14 at 19:11
source share

I tried using the phyatt AspectRatioPixmapLabel class, but experienced a few problems:

  • Sometimes my application introduced an endless loop of resizing. I followed this up to calling QLabel::setPixmap(...) inside the resizeEvent method, because QLabel actually calls updateGeometry inside setPixmap , which can cause resize events ...
  • heightForWidth seemed to be ignored by the containing widget (a QScrollArea in my case) until I started setting the size policy for the shortcut, explicitly calling policy.setHeightForWidth(true)
  • I want the label to never get bigger than the original pixmap size
  • QLabel implementation of minimumSizeHint() does some magic for labels containing text, but always resets the default size policy, so I had to rewrite it.

So here is my solution. I found that I could just use setScaledContents(true) and let QLabel handle the resizing. Of course, this depends on the contents of the widget / layout in honor of heightForWidth .

aspectratiopixmaplabel.h

 #ifndef ASPECTRATIOPIXMAPLABEL_H #define ASPECTRATIOPIXMAPLABEL_H #include <QLabel> #include <QPixmap> class AspectRatioPixmapLabel : public QLabel { Q_OBJECT public: explicit AspectRatioPixmapLabel(const QPixmap &pixmap, QWidget *parent = 0); virtual int heightForWidth(int width) const; virtual bool hasHeightForWidth() { return true; } virtual QSize sizeHint() const { return pixmap()->size(); } virtual QSize minimumSizeHint() const { return QSize(0, 0); } }; #endif // ASPECTRATIOPIXMAPLABEL_H 

aspectratiopixmaplabel.cpp

 #include "aspectratiopixmaplabel.h" AspectRatioPixmapLabel::AspectRatioPixmapLabel(const QPixmap &pixmap, QWidget *parent) : QLabel(parent) { QLabel::setPixmap(pixmap); setScaledContents(true); QSizePolicy policy(QSizePolicy::Maximum, QSizePolicy::Maximum); policy.setHeightForWidth(true); this->setSizePolicy(policy); } int AspectRatioPixmapLabel::heightForWidth(int width) const { if (width > pixmap()->width()) { return pixmap()->height(); } else { return ((qreal)pixmap()->height()*width)/pixmap()->width(); } } 
+4
Dec 30 '16 at 10:22
source share

I just use contentsMargin to correct the aspect ratio.

 #pragma once #include <QLabel> class AspectRatioLabel : public QLabel { public: explicit AspectRatioLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~AspectRatioLabel(); public slots: void setPixmap(const QPixmap& pm); protected: void resizeEvent(QResizeEvent* event) override; private: void updateMargins(); int pixmapWidth = 0; int pixmapHeight = 0; }; 
 #include "AspectRatioLabel.h" AspectRatioLabel::AspectRatioLabel(QWidget* parent, Qt::WindowFlags f) : QLabel(parent, f) { } AspectRatioLabel::~AspectRatioLabel() { } void AspectRatioLabel::setPixmap(const QPixmap& pm) { pixmapWidth = pm.width(); pixmapHeight = pm.height(); updateMargins(); QLabel::setPixmap(pm); } void AspectRatioLabel::resizeEvent(QResizeEvent* event) { updateMargins(); QLabel::resizeEvent(event); } void AspectRatioLabel::updateMargins() { if (pixmapWidth <= 0 || pixmapHeight <= 0) return; int w = this->width(); int h = this->height(); if (w <= 0 || h <= 0) return; if (w * pixmapHeight > h * pixmapWidth) { int m = (w - (pixmapWidth * h / pixmapHeight)) / 2; setContentsMargins(m, 0, m, 0); } else { int m = (h - (pixmapHeight * w / pixmapWidth)) / 2; setContentsMargins(0, m, 0, m); } } 

Works great for me. Welcome.

+3
May 12 '17 at 11:21
source share



All Articles