Qt - 如何创建随窗口缩放并保持纵横比的图像?

She*_*ary 5 qt scale

我正在尝试在 QT (内部标签)中创建图像,该图像会根据窗口大小的变化而改变大小,但也会保持宽高比。

最好的方法是什么?

Sch*_*eff 6

您用linux标记了这个问题。我在 Windows 10 上进行开发 - 我手头上最接近 Linux 的是 cygwin。因此,我在 VS2013 中解决了这个问题,但是,嘿,这是带有 Qt 的 C++。应该是便携的...

实际上,QPixmap::scaled()内置了通过保持纵横比进行缩放所需的所有内容。因此,我的解决方案是将QLabel和插入QPixmap在一起的时间相当短。

// standard C++ header:
#include <iostream>
#include <string>

// Qt header:
#include <QApplication>
#include <QResizeEvent>
#include <QLabel>
#include <QMainWindow>
#include <QPixmap>
#include <QTimer>

using namespace std;

class LabelImage: public QLabel {

  private:
    QPixmap _qPixmap, _qPixmapScaled;

  public:
    void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }

  protected:
    virtual void resizeEvent(QResizeEvent *pQEvent);

  private:
    void setPixmap(const QPixmap &qPixmap, const QSize &size);
};

void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
  QLabel::resizeEvent(pQEvent);
  setPixmap(_qPixmap, pQEvent->size());
}

void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
  _qPixmap = qPixmap;
  _qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
  QLabel::setPixmap(_qPixmapScaled);
}

int main(int argc, char **argv)
{
  cout << QT_VERSION_STR << endl;
  // main application
#undef qApp // undef macro qApp out of the way
  QApplication qApp(argc, argv);
  // setup GUI
  QMainWindow qWin;
#if 0 // does not consider aspect ratio
  QLabel qLblImg;
  qLblImg.setScaledContents(true);
#else // (not) 0
  LabelImage qLblImg;
#endif // 0
  qLblImg.setAlignment(Qt::AlignCenter);
  qLblImg.setSizePolicy(
    QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
  QPixmap qPM;
  if (qPM.load("cats.jpg")) qLblImg.setPixmap(qPM);
  else {
    qLblImg.setText(
      QString::fromLatin1("Sorry. Cannot find file 'cats.jpg'."));
  }
  qWin.setCentralWidget(&qLblImg);
  qWin.show();
  // run application
  return qApp.exec();
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 我重载了该QLabel::setPixmap()方法并实际上存储了像素图的两个版本 - 原始版本和缩放版本。我不确定这是否有必要 - 这是我第一次使用QPixmap

  2. 在阅读 Qt 文档时,我发现了QLabel::setScaledContents()。我尝试了一下,但它没有考虑像素图的纵横比。我找不到将其设置为额外选项的方法。(可能是,我搜索得不够。我禁用了此代码,但将其保留以记住这是“错误的方向”。)

  3. 在中间版本中,我能够扩大应用程序(并且缩放效果很好),但我无法缩小它。谷歌搜索了一下,我发现这样:启用 QLabel 收缩,即使它截断文本。这解决了这个问题。

  4. 为了使示例简短,我对图像文件名进行了硬编码。其实不用说,应用程序的当前目录一定是该文件所在的目录。(这在 Linux 中可能不是问题,但我必须适当调整 VS2013 中的调试设置。)

下面是我的测试应用程序的快照:

testQLabelImage.exe 的快照

(当然)这应该适用于任何可以加载到 Qt 中的图像文件。然而,为了使示例完整(并且因为互联网和猫的图片确实是一体的),我还提供了示例图像(供下载)。

猫.jpg

左边是麦克斯,右边是莫里茨。(或相反亦然?)

编辑:

根据 Shefy Gur-ary 的反馈,这在QLayout. 因此,我修改了原始版本并QGridLayout在示例代码中添加了一个来检查该主题:

// standard C++ header:
#include <iostream>
#include <string>

// Qt header:
#include <QApplication>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QMainWindow>
#include <QPixmap>
#include <QResizeEvent>
#include <QTimer>

using namespace std;

class LabelImage: public QLabel {

  private:
    QPixmap _qPixmap, _qPixmapScaled;

  public:
    void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }

  protected:
    virtual void resizeEvent(QResizeEvent *pQEvent);

  private:
    void setPixmap(const QPixmap &qPixmap, const QSize &size);
};

void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
  QLabel::resizeEvent(pQEvent);
  setPixmap(_qPixmap, pQEvent->size());
}

void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
  _qPixmap = qPixmap;
  _qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
  QLabel::setPixmap(_qPixmapScaled);
}

int main(int argc, char **argv)
{
  cout << QT_VERSION_STR << endl;
  // main application
#undef qApp // undef macro qApp out of the way
  QApplication qApp(argc, argv);
  // setup GUI
  QMainWindow qWin;
  QGroupBox qBox;
  QGridLayout qGrid;
  // a macro for the keyboard lazy:
#define Q_LBL_WITH_POS(ROW, COL) \
  QLabel qLbl##ROW##COL(QString::fromLatin1(#ROW", "#COL)); \
  /*qLbl##ROW##COL.setFrameStyle(QLabel::Raised | QLabel::Box);*/ \
  qGrid.addWidget(&qLbl##ROW##COL, ROW, COL, Qt::AlignCenter)
  Q_LBL_WITH_POS(0, 0);
  Q_LBL_WITH_POS(0, 1);
  Q_LBL_WITH_POS(0, 2);
  Q_LBL_WITH_POS(1, 0);
  LabelImage qLblImg;
  qLblImg.setFrameStyle(QLabel::Raised | QLabel::Box);
  qLblImg.setAlignment(Qt::AlignCenter);
  //qLblImg.setMinimumSize(QSize(1, 1)); // seems to be not necessary
  qLblImg.setSizePolicy(
    QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
  QPixmap qPM;
  if (qPM.load("cats.jpg")) qLblImg.setPixmap(qPM);
  else {
    qLblImg.setText(
      QString::fromLatin1("Sorry. Cannot find file 'cats.jpg'."));
  }
  qGrid.addWidget(&qLblImg, 1, 1, Qt::AlignCenter);
  qGrid.setRowStretch(1, 1); // tell QGridLayout to stretch this cell...
  qGrid.setColumnStretch(1, 1); // ...prior to other cells (w/ stretch 0)
  Q_LBL_WITH_POS(1, 2);
  Q_LBL_WITH_POS(2, 0);
  Q_LBL_WITH_POS(2, 1);
  Q_LBL_WITH_POS(2, 2);
  qBox.setLayout(&qGrid);
  qWin.setCentralWidget(&qBox);
  qWin.show();
  // run application
  return qApp.exec();
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 图像的长宽比仍然正确,但调整大小不再起作用。因此,我添加了QGrid::setRowStretch()QGrid::setColumnStretch()。不幸的是,这并没有改变太多。

  2. 我用谷歌搜索了这个主题,发现SO: Change resize behavior in Qt layouts。实际上,这也没有什么帮助,但让我怀疑这QGridLayout可能是这个布局问题的实际根源。

  3. 为了更好地可视化此布局问题,我向所有小部件添加了框架。令我惊讶的是,它突然起作用了。

我认为作品中的布局QGridLayout在某种程度上与预期不符(尽管我不敢称其为错误)。然而,在图像周围制作框架的解决方法是我可以接受的。(实际上,看起来没那么糟糕。)

更新后的代码示例的快照:

testQLabelImage.exe (V2.0) 的快照

  • @ShefyGur-ary 刚刚发现似乎没有关于已回答问题的自动通知。就是这样:我更新了我的示例,并以某种方式让它也适用于“QGridLayout”。我想知道这是否也适用于 Linux 端口。 (2认同)