0

I am trying to solve a graphics problem using the latest version of Qt. The image below shows what I managed to get so far and I will use it to explain the expected result.

The result of my work

I'm using a main vertical layout and within the biggest widget of the layout there is a horizontal layout with only one child: the square widget. The expected behavior would be of course to have the square widget centered horizontally and taking up the biggest space available. It is not required to use the same layout configuration, but the look of the interface should be the same.

The image above has been obtained by setting a QSizePolicy of minimumExpanding for both vertical and horizontal to the square widget and by forcing it to be square with the following code:

void SquareWidget::resizeEvent(QResizeEvent *event) {
    //This is an override to the QWidget method

    QSize s = size();

    if (s.height()<s.width()) {
        resize(s.height(), s.height());
    } else {
        resize(s.width(), s.width());
    }
    return;
}

While trying to solve this problem I went through the documentation some of the answers on this website and I couldn't find a clear answer about how to do two tasks.

First problem: how to make the widget square and keep its aspect ratio?

In this question

it is said that the method heightForWidth () doesn't work in newer versions of qt, and after a test it doesn't work for me either. The above override of resizeEvent, on the other hand, causes recursion because there are calls to resize() (and as far as I understand the layout should handle the resizing).

Second problem: how to center the square?

I tried using the layout alignment properties (center horizontally and vertically) but they cause the widget size to be immutable.

Maybe I am not understanding something about how Qt handles the widget placement. Any suggestion or clarification will be greatly appreciated.

Community
  • 1
  • 1
ProTom
  • 27
  • 7
  • Regarding "heightForWidth doesn't work", look the answer's date: 8 years ago. These days QWidget has it: https://doc.qt.io/qt-5/qwidget.html#hasHeightForWidth – peppe Apr 09 '17 at 13:15
  • @peppe Thank you for the info. If I used heightforwidth method, wouldn't that lead to centering issues anyways? – ProTom Apr 09 '17 at 20:14

2 Answers2

2

You can do it with a QGridLayout.

Please see the attached code.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MyWidget final : public QWidget
{
    Q_OBJECT

protected:

    virtual void resizeEvent(QResizeEvent * event) override;
};

class MainWindow final : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow() = default;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

#include <QGridLayout>
#include <QLabel>
#include <QResizeEvent>
#include <QSpacerItem>

void MyWidget::resizeEvent(QResizeEvent * event)
{
    event->accept();

    const QSize current_size = size();
    const int min = std::min(current_size.width(), current_size.height());
    resize(min, min);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    auto main_widget = new QWidget;
    auto header = new QLabel("Hello World");
    auto center_widget = new MyWidget;
    auto footer = new QLabel("Good bye World");
    auto spacer_left = new QSpacerItem(10, 10, QSizePolicy::Expanding);
    auto spacer_right = new QSpacerItem(10, 10, QSizePolicy::Expanding);
    auto grid_layout = new QGridLayout(main_widget);

    auto center_palette = center_widget->palette();
    center_palette.setColor(QPalette::Background, Qt::blue);
    center_widget->setAutoFillBackground(true);
    center_widget->setPalette(center_palette);

    grid_layout->addWidget(header, 0, 1);
    grid_layout->addItem(spacer_left, 1, 0);
    grid_layout->addWidget(center_widget, 1, 1);
    grid_layout->addItem(spacer_right, 1, 2);
    grid_layout->addWidget(footer, 2, 1);

    header->setAlignment(Qt::AlignCenter);
    footer->setAlignment(Qt::AlignCenter);

    setCentralWidget(main_widget);
}

Please see the result here

1

Rather than trying to get the widget to keep itself square and centred it might be simpler to reparent it and put the required logic in the parent widget type.

So, something like...

class keep_child_square_and_centred: public QWidget {
  using super = QWidget;
public:
  explicit keep_child_square_and_centred (QWidget *parent = nullptr)
    : super(parent)
    , m_widget(nullptr)
    {}
  virtual void set_widget (QWidget *widget)
    {
      if ((m_widget = widget))
        m_widget->setParent(this);
    }
  virtual QSize sizeHint () const override
    {
      return(m_widget ? m_widget->sizeHint() : super::sizeHint());
    }
protected:
  virtual void resizeEvent (QResizeEvent *event) override
    {
      super::resizeEvent(event);
      fixup();
    }
private:
  void fixup ()
    {
      if (m_widget) {
        QRect r(QPoint(), QSize(height(), height()));
        r.moveCenter(rect().center());
        m_widget->setGeometry(r);
      }
    }
  QWidget *m_widget;
};

Then use as...

keep_child_square_and_centred w;
SquareWidget sq;
w.set_widget(&sq);

You may still need to play around with a few settings if the parent is in a layout though.

G.M.
  • 12,232
  • 2
  • 15
  • 18