Qt:调整包含QPixmap的QLabel的大小,同时保持其宽高比


80

我使用QLabel向用户显示更大,动态变化的QPixmap的内容。根据可用空间的不同,将此标签缩小/放大会很好。屏幕尺寸并不总是和QPixmap一样大。

如何在保持原始QPixmap的纵横比的同时修改QLabel的QSizePolicysizeHint()以调整QPixmap的大小?

我无法修改sizeHint()QLabel,将设置minimumSize()为零没有帮助。hasScaledContents()在QLabel上进行设置可以增长,但会破坏长宽比。

子类化QLabel确实有帮助,但是此解决方案为一个简单的问题添加了过多的代码...

有什么聪明的提示如何在不进行子类化的情况下实现这一目标?


通过动态更改,您是指像素数据还是尺寸?
r_ahlskog,2011年

我的意思是QLabel当前布局中的尺寸。本QPixmap应该保持它的大小,内容和尺寸。同样,“自动”调整大小(实际上是缩小)以填充可用空间(达到原始大小)也很好QPixmap。所有这些都是通过子类化完成的
marvin2k 2011年

Answers:


98

为了更改标签尺寸,您可以为标签选择适当的尺寸策略,例如展开或最小展开。

您可以通过每次更改像素图的纵横比来缩放它:

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

您应该在两个地方添加此代码:

  • 像素图更新时
  • resizeEvent包含标签的小部件的中

嗯,是的,这基本上是我子类化时的核心QLabel。但是我认为这个用例(在任意大小的小部件中显示任意大小的图像)将很常见,可以通过现有代码实现类似的功能……
marvin2k 2011年

AFAIK默认情况下不提供此功能。实现您想要的最优雅的方法是继承QLabel。否则,您可以在插槽/功能中使用我的答案代码,每次像素图更改时都会调用该代码。
pnezis 2011年

1
由于我想QLabel根据用户调整大小QMainWindow和可用空间来自动扩展,因此我不能使用信号/插槽解决方案-我无法以此方式为扩展策略建模。
marvin2k 2011年

21
为了能够按比例缩小,您需要添加以下调用:label->setMinimumSize(1, 1)
Pieter-Jan Busschaert 2013年

1
如果我想保留长宽比,即使用户更改标签的大小,这也不是很有用。
托马什Zato -恢复莫妮卡

33

我已经完善了这个缺少的子类QLabel。很棒,效果很好。

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

希望有帮助!(resizeEvent根据@dmzl的答案更新)


1
谢谢,效果很好。我也将添加QLabel::setPixmap(pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));到该setPixmap()方法。
Hyndrix 2014年

你是对的。我假设您要存储最高质量的像素图版本,并在调整大小/固定标签之前调用setPixmap。为了减少代码重复,我可能应该放在函数的this->resize(width(), height());末尾setPixmap
phyatt 2014年

感谢分享。您对我如何为QPixmap设置“首选”大小有什么建议,以便在首次启动该应用程序时不会使用最大分辨率?
朱利安M

使用布局和拉伸规则。
phyatt 2015年

3
好答案!对于需要在高DPI屏幕上工作的任何人,只需更改scaledPixmap()即可:auto scaled = pix.scaled(this->size() * devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation); scaled.setDevicePixelRatio(devicePixelRatioF()); return scaled;这也适用于正常缩放的屏幕。
扫罗

17

我只是contentsMargin用来固定长宽比。

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

到目前为止,对我来说非常完美。别客气。


4
刚刚使用它,它就像一个魅力!此外,还非常巧妙地使用了布局管理器。应该接受的答案,因为其他所有情况在极端情况下都有缺陷。
thokra

2
尽管不是很直观,但是这个答案解决了一个根本不同的问题:“我们应该在已经众所周知的大小的标签和该标签中包含的像素图之间添加多少内部填充,以保持该像素图的长宽比? ” 其他每个答案都解决了原始问题:“我们应将包含像素图的标签调整到什么大小,以保持该像素图的长宽比?” 该答案要求标签的尺寸以某种方式预先确定(例如,使用固定尺寸策略),这在许多用例中是不希望的,甚至是不可行的。
Cecil Curry

1
这就是HiResolution(又称“视网膜”)显示的方法-比缩小QPixmap更好。
jvb

出于维护性的考虑,也许我过于专注于使代码表达高级含义,但是使用QSize替代...Widthand并不是更有意义...Height。如果没有别的,那将使您的提早退货检查变得简单QSize::isEmptyQPixmapQWidget两者都具有size的方法来检索的宽度和高度作为QSize
ssokolow

@ssokolow是的,听起来更好-请随时编辑答案。
Timmmm

5

我尝试使用phyatt的AspectRatioPixmapLabel课程,但遇到了一些问题:

  • 有时我的应用程序进入了大小调整事件的无限循环。我将此追溯到QLabel::setPixmap(...)resizeEvent方法内部的调用,因为QLabel实际上是调用updateGeometryinside setPixmap,这可能会触发调整大小事件...
  • heightForWidth似乎被包含的小部件(QScrollArea在我的情况下为a)所忽略,直到我开始为标签设置尺寸策略,显式调用policy.setHeightForWidth(true)
  • 我希望标签永远不要超过原始像素图的大小
  • QLabel的实现对minimumSizeHint()包含文本的标签起到了神奇的作用,但是始终将大小策略重置为默认策略,因此我不得不覆盖它

就是说,这是我的解决方案。我发现,我可以只使用setScaledContents(true),让QLabel处理进行调整。当然,这取决于包含的小部件/布局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();
    }
}

虽然对于其中包含此标签的父窗口小部件和/或布局尊重heightForWidth属性的边缘情况比较可取,但对于其中包含此标签的父窗口小部件和/或布局尊重heightForWidth属性的一般情况,此答案失败。不幸的是,因为此答案比phyatt长期答案更可取。
Cecil Curry

2

从Timmmm适应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

Qt文档中有一个Image Viewer示例,该示例演示了如何在.html中调整图像大小QLabel。基本思想是QScrollArea用作容器(QLabel如果需要),label.setScaledContents(bool)scrollarea.setWidgetResizable(bool)填充可用空间和/或确保内部的QLabel可调整大小。此外,要在遵守宽高比的同时调整QLabel的大小,请使用:

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

widthheight可以根据设置scrollarea.width()scrollarea.height()。这样,就无需继承QLabel。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.