Qt : 종횡비를 유지하면서 QPixmap을 포함하는 QLabel 크기 조정


81

QLabel을 사용하여 더 크고 동적으로 변경되는 QPixmap의 내용을 사용자에게 표시합니다. 사용 가능한 공간에 따라이 레이블을 더 작게 / 더 크게 만드는 것이 좋습니다. 화면 크기가 항상 QPixmap만큼 크지는 않습니다.

원본 QPixmap의 종횡비를 유지하면서 QPixmap의 크기를 조정하기 위해 QLabel QSizePolicysizeHint()의 수정하려면 어떻게 해야합니까?

sizeHint()QLabel을 수정할 수 없습니다 minimumSize(). 0으로 설정해도 도움이되지 않습니다. hasScaledContents()QLabel을 설정 하면 성장이 허용되지만 종횡비가 깨집니다.

QLabel을 서브 클래 싱하는 것이 도움이되었지만이 솔루션은 단순한 문제에 너무 많은 코드를 추가합니다.

서브 클래 싱 없이 이를 수행하는 방법에 대한 현명한 힌트가 있습니까?


동적으로 변경된다는 것은 픽셀 데이터 또는 치수를 의미합니까?
r_ahlskog 2011

QLabel현재 레이아웃 의 치수를 의미합니다 . 는 QPixmap크기, 내용과 차원을 유지해야합니다. 또한 크기 조정 (실제로는 축소)이 "자동으로"발생하여 사용 가능한 공간을 최대 원본 크기까지 채우는 것이 QPixmap좋습니다. 이 모든 작업은 서브 클래 싱을 통해 수행되었습니다 ...
marvin2k 2011

답변:


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

AFAIK이 기능은 기본적으로 제공되지 않습니다. 원하는 것을 달성하는 가장 우아한 방법은 하위 클래스를 만드는 것 QLabel입니다. 그렇지 않으면 pixmap이 변경 될 때마다 호출되는 슬롯 / 함수에서 내 답변의 코드를 사용할 수 있습니다.
pnezis

1
QLabel사용자의 크기 조정 QMainWindow및 사용 가능한 공간 에 따라 자동으로 확장 되기를 원 하기 때문에 신호 / 슬롯 솔루션을 사용할 수 없습니다 .이 방식으로 확장 정책을 모델링 할 수 없습니다 .
marvin2k 2011

21
뿐만 아니라 아래로 확장 할 수 있도록하기 위해, 당신은이 호출을 추가해야합니다 :label->setMinimumSize(1, 1)
피터-월 Busschaert

1
사용자가 레이블의 크기를 변경하더라도 가로 세로 비율을 유지하려는 경우에는별로 유용하지 않습니다.
Tomáš Zato-Monica 복원

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

당신이 옳습니다. 픽스맵의 최고 품질 버전을 저장하고 레이블 크기를 조정 / 고정하기 전에 setPixmap을 호출한다고 가정했습니다. 코드 중복을 줄이려면 아마도 함수의 this->resize(width(), height());끝에 넣어야 할 setPixmap것입니다.
phyatt 2014

공유 해주셔서 감사합니다. 응용 프로그램을 처음 시작할 때 최대 해상도를 사용하지 않도록 QPixmap에 "선호"크기를 설정하는 방법에 대한 제안이 있습니까?
Julien M

레이아웃과 스트레치 규칙을 사용합니다.
phyatt 2015 년

3
좋은 대답입니다! 높은 DPI 화면에서 작업해야하는 사람은 scaledPixmap ()을 변경하면됩니다. auto scaled = pix.scaled(this->size() * devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation); scaled.setDevicePixelRatio(devicePixelRatioF()); return scaled;이는 일반적으로 크기가 조정 된 화면에서도 작동합니다.
Saul

18

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

유지 관리를 위해 코드를 높은 수준의 의미로 표현하는 데 너무 집중하고 있지만 and QSize대신 사용하는 것이 더 합리적이지 않을까요? 다른 것이 없다면 조기 반환 수표를 간단한 전화 로 만들 것 입니다. 및 모두가 A와 폭과 높이를 취득하는 방법 . ...Width...HeightQSize::isEmptyQPixmapQWidgetsizeQSize
ssokolow

@ssokolow 예, 더 좋게 들립니다. 자유롭게 답변을 수정하세요.
Timmmm

5

phyatt의 AspectRatioPixmapLabel수업을 사용해 보았지만 몇 가지 문제가 발생했습니다.

  • 때때로 내 앱이 크기 조정 이벤트의 무한 루프에 들어갔습니다. 실제로 내부를 호출하여 크기 조정 이벤트를 트리거 할 수 QLabel::setPixmap(...)있기 때문에이 함수를 다시 resizeEvent 메서드 내부 호출로 추적했습니다 .QLabelupdateGeometrysetPixmap
  • heightForWidthQScrollArea레이블에 대한 크기 정책을 설정하기 시작할 때까지 포함하는 위젯 ( 제 경우에는)에서 무시한 것처럼 보였습니다.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

3

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 문서에는 내부에서 크기 조정 이미지 처리를 보여주는 이미지 뷰어 예제QLabel있습니다. 기본 아이디어는 필요한 경우 사용 QScrollArea을위한 컨테이너로 QLabel사용 label.setScaledContents(bool)하고 사용 scrollarea.setWidgetResizable(bool)가능한 공간을 채우거나 내부의 QLabel 크기를 조정할 수 있는지 확인하는 것입니다. 또한 가로 세로 비율을 유지하면서 QLabel의 크기를 조정하려면 다음을 사용하십시오.

width과은 height에 따라 설정할 수 있습니다 scrollarea.width()scrollarea.height(). 이런 식으로 QLabel을 하위 클래스화할 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.