0

Firstly, I apologise for my ugly drawing skills. I have an application where i load 4 images of size 640x480in the QLabels as shown in the image. image

Letters V, H and G here mean Vertical, Horizontal and Grid layouts respectively.

Implemented sizeHint(), heightForWidth() and widthForHeight()for the QLabel. In the resizeEvent() of my QMainwindow i call setFixedWidth() on QLabelwhich solves the issue of fitting the size of QLabel to the image keeping the aspect ratio. I want to maintain the aspect ratio of the image at all times.

Issues:

  1. Setting a fixed width on QLabel pushes the widget3 out of view in smaller resolution screens like my laptop.

I tried setting no fixed widths for the QLabel and tried to load the images keeping the aspect ratio. Size policy is set to expanding

QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

I get something like this

image2

Issues:

Loading the image while keeping the aspect ratio leads to unwanted expanding of QLabelbeyond the image horizontally which looks ugly.

What do i want:

  1. QLabel should fit to the image all the time irrespective of its own size.
  2. There should not be any weird empty spaces between widget1 and QLabel
  3. The whole application should fit on any resolution by changing the size of the QLabel while keeping the aspect ratio of the image.

I feel it is kind of definition/design problem than being a technical problem. I tried all sorts of constraints and policies but couldn't achieve what i wanted. I believe i could get some suggestions here.

MarKS
  • 454
  • 3
  • 20
  • I once fiddled with images, `QLabel`s, aspect-ratio and such. May be, this helps you too: [SO: Qt - How to create Image that scale with window, and keeps aspect ratio?](https://stackoverflow.com/a/42852010/7478597). – Scheff's Cat Sep 04 '19 at 09:17

1 Answers1

1

I'm not 100 % sure how to achieve the precise layout requirements of the OP.

This didn't became better after fiddling a bit with stretch factors in the samples I have prepared.

So, my answer elaborates the IMHO two essential aspects which have to considered.

Setting up Stretch Factors

This is my experience about layout of widget sets which I first collected in OSF/Motif, then in GTK+ (which has the IMHO best layout management) and finally in Qt:

Don't try to fight against layout manager – you will end up in frustration (like Don Quixote). Instead, try to provide the right hints (however this is possible) to let the layout manager do something which does match your expectations most.

(Or just write an own which provides fixed widget positions and sizes to the underlying widget set. This can be enlighting to realize why the layout manager are that damn complicated to use.)

So, my first attempt was to set up the stretch factors right:

#include <QtWidgets>

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QImage img("cats.jpg");
  QPixmap pixmap; 
  // setup GUI
  QWidget winMain;
  winMain.setWindowTitle("Layout Sample");
  QVBoxLayout v;
  QHBoxLayout h;
  QLabel widget1("Widget1");
  widget1.setFrameStyle(QLabel::Box | QLabel::Plain);
  h.addWidget(&widget1, 1); // remark widget1 for growing (horizontally in HBox h)
  QGridLayout g;
  QLabel img1;
  img1.setPixmap(QPixmap::fromImage(img));
  g.addWidget(&img1, 0, 0);
  QLabel img2;
  img2.setPixmap(QPixmap::fromImage(img));
  g.addWidget(&img2, 0, 1);
  QLabel img3;
  img3.setPixmap(QPixmap::fromImage(img));
  g.addWidget(&img3, 1, 0);
  QLabel img4;
  img4.setPixmap(QPixmap::fromImage(img));
  g.addWidget(&img4, 1, 1);
  h.addLayout(&g, 0); // remark g for not growing (horizontally in HBox h)
  v.addLayout(&h, 0); // remark h for not growing (vertically in VBox v)
  QLabel widget2("Widget2");
  widget2.setFrameStyle(QLabel::Box | QLabel::Plain);
  widget2.setAlignment(Qt::AlignLeft | Qt::AlignTop);
  v.addWidget(&widget2, 1); // remark widget2 for growing (vertically in VBox v)
  QLabel widget3("Widget3");
  widget3.setFrameStyle(QLabel::Box | QLabel::Plain);
  widget3.setAlignment(Qt::AlignLeft | Qt::AlignTop);
  v.addWidget(&widget3, 1); // remark widget3 for growing (vertically in VBox v)
  winMain.setLayout(&v);
  winMain.show();
  // runtime loop
  return app.exec();
}

Output:

Snapshot of testQLayout (initial) Snapshot of testQLayout (hor. stretched)

Snapshot of testQLayout (vert. stretched) Snapshot of testQLayout (hor. & vert. stretched)

This doesn't look that bad.

The Scaling Image Label

However, the image size in QLabel doesn't change. To fix this, I recalled an old answer of mine SO: Qt - How to create Image that scale with window, and keeps aspect ratio?.

I removed all explicit stretching factors from the above sample and replaced the QLabels with images by instances of the derived ImageLabel with resp. adjustment of QPixmap size:

#include <QtWidgets>

class LabelImage: public QLabel {

  private:
    QPixmap _qPixmap, _qPixmapScaled;

  public:
    LabelImage(const QPixmap &qPixmap, QWidget *pQParent = nullptr):
      QLabel(pQParent), _qPixmap(qPixmap)
    {
      QSizePolicy sizePolicy;
      sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
      sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
      sizePolicy.setHeightForWidth(true);
      sizePolicy.setWidthForHeight(true);
      setSizePolicy(sizePolicy);
    }
    virtual ~LabelImage() = default;
    LabelImage(const LabelImage&) = delete;
    LabelImage& operator=(const LabelImage&) = delete;

  public:
    //virtual QSize sizeHint() const override;
    virtual int heightForWidth(int w) const override;
  protected:
    virtual void resizeEvent(QResizeEvent *pQEvent) override;
};

//QSize LabelImage::sizeHint() const { return _qPixmap.size(); }

int LabelImage::heightForWidth(int w) const
{
  if (_qPixmap.width() == 0 || _qPixmap.height() == 0) return w;
  return _qPixmap.height() * w / _qPixmap.width();
}

void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
  QLabel::resizeEvent(pQEvent);
  _qPixmapScaled = _qPixmap.scaled(pQEvent->size(), Qt::KeepAspectRatio);
  QLabel::setPixmap(_qPixmapScaled);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QImage img("cats.jpg");
  QPixmap pixmap; 
  // setup GUI
  QWidget winMain;
  winMain.setWindowTitle("Layout Sample");
  QVBoxLayout v;
  QHBoxLayout h;
  QLabel widget1("Widget1");
  widget1.setFrameStyle(QLabel::Box | QLabel::Plain);
  h.addWidget(&widget1);
  QGridLayout g;
  LabelImage img1(QPixmap::fromImage(img));
  g.addWidget(&img1, 0, 0);
  LabelImage img2(QPixmap::fromImage(img));
  g.addWidget(&img2, 0, 1);
  LabelImage img3(QPixmap::fromImage(img));
  g.addWidget(&img3, 1, 0);
  LabelImage img4(QPixmap::fromImage(img));
  g.addWidget(&img4, 1, 1);
  h.addLayout(&g);
  v.addLayout(&h);
  QLabel widget2("Widget2");
  widget2.setFrameStyle(QLabel::Box | QLabel::Plain);
  widget2.setAlignment(Qt::AlignLeft | Qt::AlignTop);
  v.addWidget(&widget2);
  QLabel widget3("Widget3");
  widget3.setFrameStyle(QLabel::Box | QLabel::Plain);
  widget3.setAlignment(Qt::AlignLeft | Qt::AlignTop);
  v.addWidget(&widget3);
  winMain.setLayout(&v);
  winMain.show();
  // runtime loop
  return app.exec();
}

Output:

Snapshot of testQLayoutAR

Snapshot of testQLayoutAR (after hor. stretching)

Snapshot of testQLayoutAR (after hor. & vert. stretching) Snapshot of testQLayoutAR (after vert. stretching)

The layout management doesn't play that nicely (as expected – I didn't gave any stretch hint). However, at least, the ImageLabel

  • ensures scaling of image considering aspect-ratio correctly
  • (tries to) resize the QLabel according to the image aspect-ratio (when possible).

The QSizePolicy plays an important role. After having read the doc. downwards and upwards, I must admit I tried some settings which seemed to be promising after I found one which matched my expectations most.


IMHO, OPs solution is the right combination of the techniques in both samples.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • Damn! Didn't knew layouts can be this complicated. I wanna first understand what you did and will try to implement. Will let you know how it looks. Thanks you so much for the effort. – MarKS Sep 04 '19 at 11:37