Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Вы можете масштабировать 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 виджета, содержащего метку

Ответ 2

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

Ответ 3

Я попытался использовать класс phyatt AspectRatioPixmapLabel, но испытал несколько проблем:

  • Иногда мое приложение вводило бесконечный цикл изменений размера. Я проследил это до вызова QLabel::setPixmap(...) внутри метода resizeEvent, потому что QLabel фактически вызывает updateGeometry внутри setPixmap, что может вызвать события изменения размера...
  • heightForWidth, казалось, игнорировался содержащим виджет (a QScrollArea в моем случае), пока я не начал устанавливать политику размера для ярлыка, явно вызывая policy.setHeightForWidth(true)
  • Я хочу, чтобы метка никогда не становилась больше, чем исходный размер pixmap
  • 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();
    }
}

Ответ 4

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

Прекрасно работает для меня. Добро пожаловать.