问题 如何在Qt中调整图像大小?


我创建了一个名为ImageLabel的类,它扩展了QLabel。我希望它保持显示的图像的大小比例,无论它多么伸展。当你使窗户变大时,它工作正常。当你试图让窗口变小时会出现问题:它没有调整高度,它会让它伸展开来。我该如何解决?


int ImageLabel::heightForWidth(int width) const {
    int height = (this->size.height()*width)/this->size.width();
    return height;
}

QSize ImageLabel::sizeHint() const {
    return this->size;
}

QSize ImageLabel::minimumSizeHint() const {
    return QSize(0, 0);
}

void ImageLabel::setSizePolicy(){
    QSizePolicy policy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    policy.setHeightForWidth(true);
    QLabel::setSizePolicy(policy);
    QLabel::setScaledContents(true);
}

void ImageLabel::setPixmap ( const QPixmap &pixmap ){
    this->size = pixmap.size();
    QLabel::setPixmap(pixmap);
}

int main(int argc, char *argv[]){
    QApplication a(argc, argv);

    QFrame *frame = new QFrame;
    QVBoxLayout *layout = new QVBoxLayout;
    frame->setLayout(layout);

    QPixmap map;
    map.load("test.png");
    ImageLabel *label = new ImageLabel;
    label->setSizePolicy();
    label->setPixmap(map);
    layout->addWidget(label);
    frame->show();

    return a.exec();
}


4845
2018-01-01 00:55


起源



答案:


有几种方法可以做到这一点,但最重要的是我建议也许不要通过试图提示方面来对抗布局系统。如您所见,您必须尝试实现一些尝试帮助布局的方法。

我可以提供两个例子。他们都没有使用布局......

第一个使用孩子 QLabel 显示图像,并从resize事件中驱动其固定大小:

// imagelabel.h

class ImageLabel : public QWidget
{
    Q_OBJECT

public:
    explicit ImageLabel(QWidget *parent = 0);
    const QPixmap* pixmap() const;

public slots:
    void setPixmap(const QPixmap&);

protected:
    void resizeEvent(QResizeEvent *);

private slots:
    void resizeImage();

private:
    QLabel *label;
};

// imagelabel.cpp

ImageLabel::ImageLabel(QWidget *parent) :
    QWidget(parent)
{
    label = new QLabel(this);
    label->setScaledContents(true);
    label->setFixedSize(0,0);
}

void ImageLabel::resizeEvent(QResizeEvent *event) {
    QWidget::resizeEvent(event);
    resizeImage();
}

const QPixmap* ImageLabel::pixmap() const {
    return label->pixmap();
}

void ImageLabel::setPixmap (const QPixmap &pixmap){
    label->setPixmap(pixmap);
    resizeImage();
}

void ImageLabel::resizeImage() {
    QSize pixSize = label->pixmap()->size();
    pixSize.scale(size(), Qt::KeepAspectRatio);
    label->setFixedSize(pixSize);
}

第二个例子是基于@Arnold_Spence给出的答案。它更短,因为它不使用儿童QLabel。它只是在paint事件中绘制pixmap:

// imagelabel2.h

class ImageLabel2 : public QWidget
{
    Q_OBJECT

public:
    explicit ImageLabel2(QWidget *parent = 0);
    const QPixmap* pixmap() const;

public slots:
    void setPixmap(const QPixmap&);

protected:
    void paintEvent(QPaintEvent *);

private:
    QPixmap pix;
};

// imagelabel2.cpp

ImageLabel2::ImageLabel2(QWidget *parent) :
    QWidget(parent)
{
}

void ImageLabel2::paintEvent(QPaintEvent *event) {
    QWidget::paintEvent(event);

    if (pix.isNull())
        return;

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QSize pixSize = pix.size();
    pixSize.scale(event->rect().size(), Qt::KeepAspectRatio);

    QPixmap scaledPix = pix.scaled(pixSize,
                                   Qt::KeepAspectRatio,
                                   Qt::SmoothTransformation
                                   );

    painter.drawPixmap(QPoint(), scaledPix);

}

const QPixmap* ImageLabel2::pixmap() const {
    return &pix;
}

void ImageLabel2::setPixmap (const QPixmap &pixmap){
    pix = pixmap;
}

13
2018-01-01 03:57



该 paintEvent 方法看起来很简单,我只是不明白它如何与布局系统交互。它如何告诉布局系统只为高度分配正确的宽度?看起来它并没有告诉它什么,它只是利用它可用的东西。在这种情况下,你可能会有令人讨厌的利润,对吧? - chacham15
就像我说的,它不使用布局。你可以放置它 ImageLabel  成 一个将驱动其大小的布局。但当时 ImageLabel 对这些尺寸变化做出反应,它将控制像素图的精确显示。是的,如果您使父窗口小部件非常宽,则可以使用剩余边距。如果你想让它居中,那么只需要一点点数学就可以得到一个正确的 QPoint 为了 drawPixmap 呼叫。 - jdi
真的,这里重要的是你是否需要它有一些对齐选项。除此之外,您只需将其与其他小部件一起放入布局中即可。所有这个小部件应该做的是保持像素图正确缩放,与小部件中可用空间的某个区域对齐。 - jdi
我不喜欢任何利润。我想制作一张图片拼贴画,每张图片紧挨着另一张图片(想象像pinterest)。我想知道的是父布局如何知道给它多少空间,以便它没有疯狂的边缘? - chacham15
为了澄清,这两个对我有用,但只有在我对这个实例做了这个(在pyQt中)之后: imageLabelSizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding); imageLabelSizePolicy.setHeightForWidth(True); imageLabel.setSizePolicy(imageLabelSizePolicy)。这可能是Qt n00b评论,但我确定将来某个时候另一个Qt n00b将登陆此页面。 - Michael Scheper


答案:


有几种方法可以做到这一点,但最重要的是我建议也许不要通过试图提示方面来对抗布局系统。如您所见,您必须尝试实现一些尝试帮助布局的方法。

我可以提供两个例子。他们都没有使用布局......

第一个使用孩子 QLabel 显示图像,并从resize事件中驱动其固定大小:

// imagelabel.h

class ImageLabel : public QWidget
{
    Q_OBJECT

public:
    explicit ImageLabel(QWidget *parent = 0);
    const QPixmap* pixmap() const;

public slots:
    void setPixmap(const QPixmap&);

protected:
    void resizeEvent(QResizeEvent *);

private slots:
    void resizeImage();

private:
    QLabel *label;
};

// imagelabel.cpp

ImageLabel::ImageLabel(QWidget *parent) :
    QWidget(parent)
{
    label = new QLabel(this);
    label->setScaledContents(true);
    label->setFixedSize(0,0);
}

void ImageLabel::resizeEvent(QResizeEvent *event) {
    QWidget::resizeEvent(event);
    resizeImage();
}

const QPixmap* ImageLabel::pixmap() const {
    return label->pixmap();
}

void ImageLabel::setPixmap (const QPixmap &pixmap){
    label->setPixmap(pixmap);
    resizeImage();
}

void ImageLabel::resizeImage() {
    QSize pixSize = label->pixmap()->size();
    pixSize.scale(size(), Qt::KeepAspectRatio);
    label->setFixedSize(pixSize);
}

第二个例子是基于@Arnold_Spence给出的答案。它更短,因为它不使用儿童QLabel。它只是在paint事件中绘制pixmap:

// imagelabel2.h

class ImageLabel2 : public QWidget
{
    Q_OBJECT

public:
    explicit ImageLabel2(QWidget *parent = 0);
    const QPixmap* pixmap() const;

public slots:
    void setPixmap(const QPixmap&);

protected:
    void paintEvent(QPaintEvent *);

private:
    QPixmap pix;
};

// imagelabel2.cpp

ImageLabel2::ImageLabel2(QWidget *parent) :
    QWidget(parent)
{
}

void ImageLabel2::paintEvent(QPaintEvent *event) {
    QWidget::paintEvent(event);

    if (pix.isNull())
        return;

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QSize pixSize = pix.size();
    pixSize.scale(event->rect().size(), Qt::KeepAspectRatio);

    QPixmap scaledPix = pix.scaled(pixSize,
                                   Qt::KeepAspectRatio,
                                   Qt::SmoothTransformation
                                   );

    painter.drawPixmap(QPoint(), scaledPix);

}

const QPixmap* ImageLabel2::pixmap() const {
    return &pix;
}

void ImageLabel2::setPixmap (const QPixmap &pixmap){
    pix = pixmap;
}

13
2018-01-01 03:57



该 paintEvent 方法看起来很简单,我只是不明白它如何与布局系统交互。它如何告诉布局系统只为高度分配正确的宽度?看起来它并没有告诉它什么,它只是利用它可用的东西。在这种情况下,你可能会有令人讨厌的利润,对吧? - chacham15
就像我说的,它不使用布局。你可以放置它 ImageLabel  成 一个将驱动其大小的布局。但当时 ImageLabel 对这些尺寸变化做出反应,它将控制像素图的精确显示。是的,如果您使父窗口小部件非常宽,则可以使用剩余边距。如果你想让它居中,那么只需要一点点数学就可以得到一个正确的 QPoint 为了 drawPixmap 呼叫。 - jdi
真的,这里重要的是你是否需要它有一些对齐选项。除此之外,您只需将其与其他小部件一起放入布局中即可。所有这个小部件应该做的是保持像素图正确缩放,与小部件中可用空间的某个区域对齐。 - jdi
我不喜欢任何利润。我想制作一张图片拼贴画,每张图片紧挨着另一张图片(想象像pinterest)。我想知道的是父布局如何知道给它多少空间,以便它没有疯狂的边缘? - chacham15
为了澄清,这两个对我有用,但只有在我对这个实例做了这个(在pyQt中)之后: imageLabelSizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding); imageLabelSizePolicy.setHeightForWidth(True); imageLabel.setSizePolicy(imageLabelSizePolicy)。这可能是Qt n00b评论,但我确定将来某个时候另一个Qt n00b将登陆此页面。 - Michael Scheper


没有内置设施 QLabel 保持图像的纵横比。以来 Qlabel 可能有你不需要的功能,你应该是子类 QWidget 并覆盖 paintEvent() 以您想要的方式呈现图像。


2
2018-01-01 02:11



是的,这就是为什么我添加了 heightForWidth 缺乏的能力 Qlabel。问题不只是绘画,而是布局。 - chacham15
您可以使用小部件尝试向布局提供关于其想要的大小的反馈,并始终将图像绘制到小部件的完整维度,或者您可以允许布局以任何方式调整小部件的大小并始终绘制具有小部件边界的适当比例和位置的图像。前者可能变得复杂,后者似乎更容易。 - Arnold Spence
@chacham15:我实际上认为它只是在paintEvent中做的好建议,因为它允许你显式绘制你想要的精确像素图,而不是对抗布局。您可以在组合的QLabel上设置固定大小的某些操作,并使用resizeEvent来继续调整子级的大小。 - jdi