Qt: cambiar el tamaño de una QLabel que contiene un QPixmap manteniendo su relación de aspecto

Utilizo QLabel para mostrar al usuario el contenido de un QPixmap más grande y dinámicamente cambiante. Sería bueno hacer esta etiqueta más pequeña / más grande dependiendo del espacio disponible. El tamaño de la pantalla no siempre es tan grande como el QPixmap.

¿Cómo puedo modificar QSizePolicy y sizeHint() de QLabel para cambiar el tamaño de QPixmap y mantener la relación de aspecto del QPixmap original?

No puedo modificar sizeHint() de QLabel, establecer el minimumSize() en cero no ayuda. La configuración de hasScaledContents() en QLabel permite crecer, pero rompe la relación de aspecto.

Subclassing QLabel ayudó, pero esta solución agrega demasiado código para un simple problema …

¿Alguna sugerencia inteligente de cómo lograr esto sin subclases?

Para cambiar el tamaño de la etiqueta, puede seleccionar una política de tamaño adecuado para la etiqueta, como expansión o expansión mínima.

Puede escalar el mapa de píxeles manteniendo su relación de aspecto cada vez que cambie:

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

Hay dos lugares donde deberías agregar este código:

  • Cuando se actualiza el mapa de puntos
  • En el resizeEvent del widget que contiene la etiqueta

He pulido esta subclase faltante de QLabel . Es increíble y funciona bien.

aspectratiopixmaplabel.h

 #ifndef ASPECTRATIOPIXMAPLABEL_H #define ASPECTRATIOPIXMAPLABEL_H #include  #include  #include  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  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()); } 

¡Espero que ayude! ( resizeEvent actualizado, por la respuesta de @dmzl)

Solo uso contentsMargin para arreglar la relación de aspecto.

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

Funciona perfectamente para mí hasta ahora. De nada.

Intenté usar la clase AspectRatioPixmapLabel de phyatt, pero experimenté algunos problemas:

  • A veces, mi aplicación entró en un bucle infinito de eventos de cambio de tamaño. Lo QLabel::setPixmap(...) a la llamada de QLabel::setPixmap(...) dentro del método resizeEvent, porque QLabel realmente llama a updateGeometry dentro de setPixmap , lo que puede desencadenar eventos de cambio de tamaño …
  • heightForWidth parecía ser ignorado por el widget contenedor (un QScrollArea en mi caso) hasta que comencé a establecer una política de tamaño para la etiqueta, llamando explícitamente a policy.setHeightForWidth(true)
  • Quiero que la etiqueta nunca crezca más que el tamaño original de pixmap
  • La implementación de minimumSizeHint() de minimumSizeHint() hace algo de magia para las tags que contienen texto, pero siempre restablece la política de tamaño a la predeterminada, por lo que tuve que sobrescribirla

Dicho eso, aquí está mi solución. Descubrí que solo podía usar setScaledContents(true) y dejar que QLabel manejara el cambio de tamaño. Por supuesto, esto depende del widget / diseño que contiene el heightForWidth .

aspectratiopixmaplabel.h

 #ifndef ASPECTRATIOPIXMAPLABEL_H #define ASPECTRATIOPIXMAPLABEL_H #include  #include  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(); } }