web-dev-qa-db-fra.com

Qt: redimensionner un QLabel contenant un QPixmap tout en conservant son format

J'utilise un QLabel pour afficher le contenu d'un QPixmap de plus grande taille et qui change de manière dynamique pour l'utilisateur. Il serait bien de rendre cette étiquette plus petite/plus grande en fonction de l'espace disponible. La taille de l'écran n'est pas toujours aussi grande que celle du QPixmap.

Comment puis-je modifier les QSizePolicy et sizeHint() du QLabel pour redimensionner le QPixmap tout en conservant le rapport de format du QPixmap d'origine?

Je ne peux pas modifier sizeHint() du QLabel, régler le minimumSize() sur zéro n'aide pas. La définition de hasScaledContents() sur le QLabel permet de développer, mais casse le ratio d'aspect ...

Sous-classer QLabel a aidé, mais cette solution ajoute trop de code pour un simple problème ...

Des astuces astucieuses pour accomplir ceci sans sous-classement?

67
marvin2k

Afin de changer la taille de l'étiquette, vous pouvez sélectionner une stratégie de taille appropriée pour l'étiquette, telle que l'extension ou l'extension minimale.

Vous pouvez redimensionner la pixmap en conservant ses proportions à chaque changement:

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

Il y a deux endroits où vous devriez ajouter ce code:

  • Quand le pixmap est mis à jour
  • Dans le resizeEvent du widget qui contient l'étiquette
84
pnezis

J'ai poli cette sous-classe manquante de QLabel. C'est génial et ça marche bien.

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

J'espère que ça t'as aidé! (Mise à jour resizeEvent, réponse de @ dmzl)

29
phyatt

Je viens d'utiliser contentsMargin pour corriger les proportions.

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

Fonctionne parfaitement pour moi jusqu'à présent. De rien.

11
Timmmm

J'ai essayé d'utiliser la classe AspectRatioPixmapLabel de phyatt, mais j'ai rencontré quelques problèmes:

  • Parfois, mon application entrait dans une boucle infinie d'événements de redimensionnement. J'ai retracé cela jusqu'à l'appel de QLabel::setPixmap(...) dans la méthode resizeEvent, car QLabel appelle en fait updateGeometry à l'intérieur setPixmap, ce qui peut déclencher des événements de redimensionnement ...
  • heightForWidth semblait être ignoré par le widget qui le contenait (un QScrollArea dans mon cas) jusqu'à ce que je commence à définir une politique de taille pour l'étiquette, appelant explicitement policy.setHeightForWidth(true)
  • Je veux que l'étiquette ne grandisse jamais plus que la taille de la pixmap d'origine
  • L'implémentation de minimumSizeHint() par QLabel fait de la magie pour les étiquettes contenant du texte, mais remet toujours la politique de taille à celle par défaut, je devais donc l'écraser

Ceci dit, voici ma solution. J'ai découvert que je pouvais simplement utiliser setScaledContents(true) et laisser QLabel gérer le redimensionnement. Bien entendu, cela dépend du widget/de la mise en page qui respecte le 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();
    }
}
6
Alexander Schlüter

Merci de partager cela. Avez-vous des suggestions sur la manière de définir une taille "préférée" pour le QPixmap afin qu'il ne prenne pas la résolution maximale lors du premier lancement de l'application?

0
Julien M