2011-11-21 808 views
48

我使用QLabel将更大,动态变化的QPixmap的内容显示给用户。根据可用空间的不同,将此标签更小/更大将会很好。屏幕尺寸并不总是和QPixmap一样大。Qt:在保持宽高比的同时调整包含QPixmap的QLabel的大小

如何修改QLabel的QSizePolicysizeHint()以调整QPixmap的大小,同时保持原始QPixmap的纵横比?

我无法修改QLabel的sizeHint(),因此将minimumSize()设置为零不起作用。在QLabel设置hasScaledContents()允许增长,但打破了长宽比啄...

子类QLabel没有帮助,但这种方法增加了只是一个简单的问题,太多的代码......

任何智能提示如何完成这个没有子类?

+0

通过动态变化你是指像素数据还是维度? –

+0

我指的是当前布局中'QLabel'的尺寸。 QPixmap应该保持其大小,内容和维度。此外,调整大小(实际上缩小)会“自动地”发生,以填充可用空间 - 最初为“QPixmap”的大小。所有这些都是通过子类化来完成的...... – marvin2k

回答

58

为了更改标签尺寸,您可以为标签选择适当的尺寸策略,如展开或最小展开。

您可以通过每次发生变化时保持其长宽比缩放像素图:

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
+0

嗯是的,这基本上是我将子类“QLabel”的核心。但我认为这个用例(在任意大小的Widgets中显示具有任意大小的图像)通常足以具有通过现有代码实现的功能...... – marvin2k

+0

AFAIK默认情况下不提供此功能。实现你想要的最优雅的方式是继承'QLabel'。否则,您可以在插槽/函数中使用我的答案的代码,每次像素图更改时都会调用它。 – pnezis

+1

因为我希望'QLabel'能够根据用户调整'QMainWindow'的大小和可用空间自动扩展,所以我不能使用信号/插槽解决方案 - 我无法用这种方法建模_expanding_策略。 – marvin2k

23

我已经抛光了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

谢谢,效果很好。我还会在''setPixmap()''方法中加入''QLabel :: setPixmap(pix.scaled(this-> size(), Qt :: KeepAspectRatio,Qt :: SmoothTransformation));''。 – Hyndrix

+0

你说得对。我假设你想存储最高质量的pixmap版本,并且在调整/锚定标签之前调用setPixmap。为了减少代码重复,我应该在'setPixmap'函数的尾部放置'this-> resize(width(),height());'。 – phyatt

+0

感谢分享。对于如何为QPixmap设置“首选”大小,您是否有任何建议,以便在首次启动应用程序时不会获得最高分辨率? –

4

我试着用phyatt的AspectRatioPixmapLabel类,但在经历了几个问题:

  • 有时我的应用程序进入调整事件的无限循环。我追溯到QLabel::setPixmap(...)调用resizeEvent方法内,因为QLabel实际上调用updateGeometry里面setPixmap,这可能会触发调整大小事件...
  • heightForWidth似乎是由含有小部件(在我的情况下,QScrollArea)被忽略,直到我开始设置大小政策的标签,显式调用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(); 
    } 
} 
+0

虽然对于包含此标签的父控件和/或布局尊重“heightForWidth”属性的边缘情况,此答案在包含此标签的父控件和/或布局不*遵守* 'heightForWidth'属性。这是不幸的,因为这个答案否则更可取[phyatt](https://stackoverflow.com/users/999943/phyatt)的[长期回答](https://stackoverflow.com/a/22618496/ 2809027)。 –

3

我只是用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); 
    } 
} 

完美的作品对我来说至今。别客气。

+1

刚刚使用这个,它的作品就像一个魅力!此外,非常巧妙地使用布局管理器。应该是被接受的答案,因为所有其他人在角落案件中都有缺陷。 – thokra

+0

尽管非直观聪明,但这个答案解决了一个基本上不同的问题:“我们应该在大小已知的标签和包含在该标签中的像素图之间添加多少内部填充以便保持宽高比那个pixmap?“其他答案解决了原始问题:“我们应该调整包含像素图的标签的大小以保持该像素图的高宽比?”该答案要求以某种方式预先确定标签的尺寸(例如,具有固定尺寸的策略),这在许多使用情况中是不希望的或者甚至是不可行的。 –

相关问题