Utilizo QLabel para mostrar al usuario el contenido de un QPixmap más grande que cambia dinámicamente. Sería bueno hacer esta etiqueta más pequeña / más grande según el espacio disponible. El tamaño de la pantalla no siempre es tan grande como el QPixmap.
¿Cómo puedo modificar QSizePolicyy sizeHint()de QLabel para cambiar el tamaño del QPixmap manteniendo la relación de aspecto del QPixmap original?
No puedo modificar sizeHint()el QLabel, establecer el minimumSize()en cero no ayuda. La configuración hasScaledContents()en QLabel permite crecer, pero rompe la relación de aspecto ...
Subclasificar QLabel ayudó, pero esta solución agrega demasiado código para un simple problema ...
¿Algún consejo inteligente sobre cómo lograr esto sin subclases?

QLabeldiseño actual. ElQPixmapdebe mantener su tamaño, contenido y dimensión. Además, sería bueno que el cambio de tamaño (encogiéndose en realidad) ocurra "automágicamente" para llenar el espacio disponible, hasta el tamaño del originalQPixmap. Todo esto se hizo mediante subclases ...Respuestas:
Para cambiar el tamaño de la etiqueta, puede seleccionar una política de tamaño adecuada 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 cambia:
QPixmap p; // load pixmap // get label dimensions int w = label->width(); int h = label->height(); // set a scaled pixmap to a w x h window keeping its aspect ratio label->setPixmap(p.scaled(w,h,Qt::KeepAspectRatio));Hay dos lugares donde debe agregar este código:
resizeEventdel widget que contiene la etiquetafuente
QLabel. Pero pensé que este caso de uso (que muestra imágenes con tamaño arbitrario en widgets de tamaño arbitrario) sería lo suficientemente común como para tener algo así implementable a través del código existente ...QLabel. De lo contrario, puede usar el código de mi respuesta en una ranura / función que se llamará cada vez que cambie el mapa de píxeles.QLabelque se expanda automáticamente en función del cambio de tamaño de los usuariosQMainWindowy el espacio disponible, no puedo usar la solución de señal / ranura; no puedo modelar una política de expansión de esta manera.label->setMinimumSize(1, 1)He pulido esta subclase faltante de
QLabel. Es impresionante y funciona bien.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_HAspectratiopixmaplabel.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()); }¡Espero que ayude! (Actualizado
resizeEvent, según la respuesta de @ dmzl)fuente
QLabel::setPixmap(pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));alsetPixmap()método.this->resize(width(), height());al final de lasetPixmapfunción.auto scaled = pix.scaled(this->size() * devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation); scaled.setDevicePixelRatio(devicePixelRatioF()); return scaled;Esto también funciona en pantallas normalmente escaladas.Solo uso
contentsMarginpara arreglar la relación de aspecto.#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); } }Funciona perfectamente para mí hasta ahora. De nada.
fuente
QSizelugar de...Widthy...Height? Si nada más, eso haría que sus cheques de devolución anticipada fueran una simpleQSize::isEmptyllamada.QPixmapyQWidgetambos tienensizemétodos para recuperar el ancho y el alto como unQSize.Intenté usar la
AspectRatioPixmapLabelclase de phyatt , pero experimenté algunos problemas:QLabel::setPixmap(...)Rastreé esto hasta la llamada de dentro del método resizeEvent, porque enQLabelrealidad las llamadasupdateGeometrydentrosetPixmap, lo que puede desencadenar eventos de cambio de tamaño ...heightForWidthparecía ser ignorado por el widget contenedor (aQScrollAreaen mi caso) hasta que comencé a configurar una política de tamaño para la etiqueta, llamando explícitamentepolicy.setHeightForWidth(true)QLabelLa implementación deminimumSizeHint()hace algo de magia para las etiquetas que contienen texto, pero siempre restablece la política de tamaño a la predeterminada, así que tuve que sobrescribirlaDicho esto, aquí está mi solución. Descubrí que podía usar
setScaledContents(true)y dejarQLabelmanejar el cambio de tamaño. Por supuesto, esto depende del widget / diseño que lo contenga respetando elheightForWidth.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_HAspectratiopixmaplabel.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(); } }fuente
heightForWidthpropiedad, esta respuesta falla para el caso general en el que el widget principal y / o el diseño que contiene esta etiqueta no respetan laheightForWidthpropiedad. Lo cual es lamentable, ya que esta respuesta es preferible a la respuesta de larga data de phyatt .Adaptado de Timmmm a PYQT5
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QResizeEvent from PyQt5.QtWidgets import QLabel class Label(QLabel): def __init__(self): super(Label, self).__init__() self.pixmap_width: int = 1 self.pixmapHeight: int = 1 def setPixmap(self, pm: QPixmap) -> None: self.pixmap_width = pm.width() self.pixmapHeight = pm.height() self.updateMargins() super(Label, self).setPixmap(pm) def resizeEvent(self, a0: QResizeEvent) -> None: self.updateMargins() super(Label, self).resizeEvent(a0) def updateMargins(self): if self.pixmap() is None: return pixmapWidth = self.pixmap().width() pixmapHeight = self.pixmap().height() if pixmapWidth <= 0 or pixmapHeight <= 0: return w, h = self.width(), self.height() if w <= 0 or h <= 0: return if w * pixmapHeight > h * pixmapWidth: m = int((w - (pixmapWidth * h / pixmapHeight)) / 2) self.setContentsMargins(m, 0, m, 0) else: m = int((h - (pixmapHeight * w / pixmapWidth)) / 2) self.setContentsMargins(0, m, 0, m)fuente
La documentación de Qt tiene un ejemplo de Visor de imágenes que demuestra cómo manejar imágenes de cambio de tamaño dentro de un archivo
QLabel. La idea básica es utilizarQScrollAreacomo un contenedor para elQLabely si es necesario, utilicelabel.setScaledContents(bool)yscrollarea.setWidgetResizable(bool)para llenar el espacio disponible y / o garantizar QLabel interior es de tamaño variable. Además, para cambiar el tamaño de QLabel respetando la relación de aspecto, utilice:El
widthyheightse puede establecer basándose enscrollarea.width()yscrollarea.height(). De esta manera, no es necesario crear una subclase de QLabel.fuente