Qt: изменение размера QLabel, содержащей QPixmap, сохраняя при этом соотношение сторон

Я использую QLabel для отображения содержимого большего, динамически изменяющегося QPixmap пользователю. Было бы неплохо сделать эту метку меньше/больше в зависимости от доступного пространства. Размер экрана не всегда так велик, как QPixmap.

как я могу изменить QSizePolicy и sizeHint() QLabel для изменения размера QPixmap при сохранении соотношения сторон исходного QPixmap?

Я не могу изменить sizeHint() QLabel, установка minimumSize() до нуля не помогает. Настройка hasScaledContents() на QLabel позволяет расти,но нарушает соотношение сторон...

подклассы QLabel действительно помогли, но это решение добавляет слишком много кода для простой проблемы...

любые умные подсказки, как это сделать без подклассы?

4 ответов


чтобы изменить размер метки, Вы можете выбрать соответствующую политику размера для метки, такую как расширение или минимальное расширение.

вы можете масштабировать pixmap, сохраняя его соотношение сторон при каждом изменении:

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

есть два места, где вы должны добавить этот код:

  • при обновлении pixmap
  • на resizeEvent виджета, содержащего метку

Я отполировал этот подкласс отсутствует 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)


Я просто использовать 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);
    }
}

отлично работает для меня до сих пор. Добро пожаловать.


я попытался с помощью phyatt это AspectRatioPixmapLabel класс, но испытал несколько проблем:

  • иногда мое приложение вошло в бесконечный цикл событий изменения размера. Я проследил это до звонка QLabel::setPixmap(...) внутри метода resizeEvent, потому что QLabel на самом деле называет updateGeometry внутри setPixmap, что может вызвать события изменения размера...
  • heightForWidth казалось, игнорируется содержащим виджет (a QScrollArea в моем случае), пока я не начал, устанавливая размер на этикетке, явный вызов policy.setHeightForWidth(true)
  • я хочу, чтобы метка никогда не росла больше, чем исходный размер pixmap
  • о minimumSizeHint() делает некоторую магию для меток, содержащих текст, но всегда сбрасывает политику размера по умолчанию, поэтому мне пришлось перезаписать ее

тем не менее, вот мое решение. Я обнаружил, что могу просто использовать setScaledContents(true) и пусть QLabel маркер изменения размера. Конечно, это зависит от содержащего виджета / макета 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();
    }
}