Qt:アスペクト比を維持しながらQPixmapを含むQLabelのサイズを変更する


81

QLabelを使用して、動的に変化するより大きなQPixmapのコンテンツをユーザーに表示します。利用可能なスペースに応じて、このラベルを小さくしたり大きくしたりすると便利です。画面サイズは必ずしもQPixmapほど大きくはありません。

どのように私は変更することができますQSizePolicyし、sizeHint()元QPixmapのの縦横比を維持しながら、QLabelのQPixmapのサイズを変更するには?

sizeHint()QLabelを変更できminimumSize()ません。ゼロに設定しても効果がありません。hasScaledContents()QLabelに設定すると、拡大できますが、アスペクト比が崩れます...

QLabelをサブクラス化することは役に立ちましたが、このソリューションは単純な問題に対してコードを追加しすぎます...

サブクラス化せずにこれを達成するための賢いヒントはありますか?


動的に変化するということは、ピクセルデータまたは寸法を意味しますか?
r_ahlskog 2011年

QLabelは現在のレイアウトのの寸法を意味します。QPixmapその大きさ、内容や大きさを維持する必要があります。また、サイズ変更(実際には縮小)が「自動的に」行われ、使用可能なスペースが元のサイズまでいっぱいになると便利ですQPixmap。このすべては...サブクラス化を介して行われました
marvin2k

回答:


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

このコードを追加する必要がある場所は2つあります。

  • ピックスマップが更新されたとき
  • resizeEventラベルが含まれているウィジェットの

はい、これは基本的に私がサブクラス化しQLabelたときのコアでした。しかし、このユースケース(任意のサイズのウィジェットに任意のサイズの画像を表示する)は、既存のコードを介して実装できるようなものを持つのに十分一般的だと思いました...
marvin2k 2011年

この機能はデフォルトでは提供されていません。必要なことを実現するための最もエレガントな方法は、サブクラス化することですQLabel。それ以外の場合は、ピックスマップが変更されるたびに呼び出されるスロット/関数で私の答えのコードを使用できます。
pnezis 2011年

1
QLabelユーザーのサイズ変更QMainWindowと使用可能なスペースに基づいてを自動的に拡張したいので、シグナル/スロットソリューションを使用できません。この方法で拡張ポリシーをモデル化することはできません。
marvin2k 2011年

21
:同様にスケールダウンすることができるようにするために、あなたはこの呼び出しを追加する必要がありますlabel->setMinimumSize(1, 1)
ピーター・ヤンBusschaert

1
これは、ユーザーがラベルのサイズを変更してもアスペクト比を維持したい場合にはあまり役に立ちません。
トマシュザト-モニカを復活させる

33

のこの欠落しているサブクラスを磨きましたQLabel。それは素晴らしく、うまく機能します。

アスペクト比pixmaplabel.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

アスペクト比pixmaplabel.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

あなたが正しいです。最高品質のpixmapを保存し、ラベルのサイズを変更/固定する前に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;これは、通常のスケーリングされた画面でも機能します。
ソール2016

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 2017年

2
直感的ではありませんが、この回答は根本的に異なる質問を解決します。「サイズがすでにわかっているラベルとそのラベルに含まれるピックスマップの間に、そのピックスマップのアスペクト比を維持するために、どのくらいの内部パディングを追加する必要がありますか? 「」他のすべての回答は、元の質問を解決します。「ピックスマップのアスペクト比を維持するために、ピックスマップを含むラベルのサイズをどのサイズに変更する必要がありますか?」この回答では、ラベルのサイズを何らかの方法で事前に決定する必要があります(たとえば、固定サイズのポリシーを使用)。これは、多くのユースケースで望ましくないか、実行不可能ですらあります。
セシルカレー

1
これがHiResolution(別名「retina」)ディスプレイを表示する方法です。QPixmapをダウンスケーリングするよりもはるかに優れています。
JVB

保守性のためにコードに高レベルの意味を表現させることに少し集中しすぎているかもしれませんが、andのQSize代わりに使用する方が理にかなっているのではないでしょうか。他に何もないとしても、それはあなたの早期返品チェックを簡単な呼び出しにするでしょう。そして、の両方を持っているとして、幅と高さを取得する方法を。...Width...HeightQSize::isEmptyQPixmapQWidgetsizeQSize
ssokolow

@ssokolowはい、そうですね。答えを自由に編集してください。
Timmmm

5

phyattのAspectRatioPixmapLabelクラスを使用してみましたが、いくつかの問題が発生しました。

  • 時々私のアプリはサイズ変更イベントの無限ループに入りました。これQLabel::setPixmap(...)は、resizeEventメソッド内の呼び出しにまでさかのぼります。QLabel実際にはupdateGeometryinsideを呼び出すためsetPixmap、サイズ変更イベントがトリガーされる可能性があります...
  • heightForWidthQScrollAreaラベルのサイズポリシーの設定を開始し、明示的に呼び出すまで、含まれているウィジェット(私の場合はa )によって無視されているように見えましたpolicy.setHeightForWidth(true)
  • ラベルが元のピックスマップサイズを超えないようにしたい
  • QLabelの実装はminimumSizeHint()、テキストを含むラベルに対していくつかの魔法を実行しますが、サイズポリシーを常にデフォルトのポリシーにリセットするため、上書きする必要がありました

そうは言っても、これが私の解決策です。サイズ変更をそのまま使用setScaledContents(true)してQLabel処理できることがわかりました。もちろん、これはを尊重するウィジェット/レイアウトに依存しますheightForWidth

アスペクト比pixmaplabel.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

アスペクト比pixmaplabel.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プロパティ。この答えは、フィアット長年の答えよりも好ましいので、これは残念です。
セシルカレー

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のサイズを変更するには、次のようにします。

widthheightに基づいて設定することができるscrollarea.width()scrollarea.height()。このように、QLabelをサブクラス化する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.