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

后端 未结 6 2007
长情又很酷
长情又很酷 2020-11-28 03:35

I use a QLabel to display the content of a bigger, dynamically changing QPixmap to the user. It would be nice to make this label smaller/larger depending on the space availa

相关标签:
6条回答
  • 2020-11-28 04:12

    Adapted from Timmmm to 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)
    
    0 讨论(0)
  • 2020-11-28 04:13

    I have polished this missing subclass of QLabel. 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 that helps! (Updated resizeEvent, per @dmzl's answer)

    0 讨论(0)
  • 2020-11-28 04:20

    The Qt documentations has an Image Viewer example which demonstrates handling resizing images inside a QLabel. The basic idea is to use QScrollArea as a container for the QLabel and if needed use label.setScaledContents(bool) and scrollarea.setWidgetResizable(bool) to fill available space and/or ensure QLabel inside is resizable. Additionally, to resize QLabel while honoring aspect ratio use:

    label.setPixmap(pixmap.scaled(width, height, Qt::KeepAspectRatio, Qt::FastTransformation));
    

    The width and height can be set based on scrollarea.width() and scrollarea.height(). In this way there is no need to subclass QLabel.

    0 讨论(0)
  • 2020-11-28 04:23

    In order to change the label size you can select an appropriate size policy for the label like expanding or minimum expanding.

    You can scale the pixmap by keeping its aspect ratio every time it changes:

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

    There are two places where you should add this code:

    • When the pixmap is updated
    • In the resizeEvent of the widget that contains the label
    0 讨论(0)
  • 2020-11-28 04:24

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

    • Sometimes my app entered an infinite loop of resize events. I traced this back to the call of QLabel::setPixmap(...) inside the resizeEvent method, because QLabel actually calls updateGeometry inside setPixmap, which may trigger resize events...
    • heightForWidth seemed to be ignored by the containing widget (a QScrollArea in my case) until I started setting a size policy for the label, explicitly calling policy.setHeightForWidth(true)
    • I want the label to never grow more than the original pixmap size
    • QLabel's implementation of minimumSizeHint() does some magic for labels containing text, but always resets the size policy to the default one, so I had to overwrite it

    That said, 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 containing widget / layout honoring the 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();
        }
    }
    
    0 讨论(0)
  • 2020-11-28 04:26

    I just use contentsMargin to fix 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 perfectly for me so far. You're welcome.

    0 讨论(0)
提交回复
热议问题