How is the size of Qt widgets determined?

I have been wondering how the size of Qt widgets is determined. I read a lot of articles about sizeHint, minimumSizeHint, minimumSize,resize, sizePolicy, geometry, layout, etc. Guess what? I am totally confused about the algorithm of calculating the widget size. I did not care too much about the size of the widgets. I usually put not many widgets in a layout and their size can adapt to the size change of the main window. If user drags the bounds of main window, or maximize/restore the main window, the widgets will adjust their size accordingly so the whole GUI looks not bad. No need to set their size manually. So there is no urgent need to understand how the widget size is calculated in detail. The need only arises for fine control of the size of the widgets.

default size of widgets

It seems every kind of widgets has a default size. You can use the following code to get the default size of the widgets:

//example 1
#include "widget.h"
#include <QApplication>
#include <QDebug>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    //QPushButton w("hello");
    //QLabel w("hello");
    w.show();
    qDebug()<<w.size();
    qDebug()<<w.sizeHint();
    qDebug()<<w.layout();
    qDebug()<<w.minimumSize();
    qDebug()<<w.minimumSizeHint();
    qDebug()<<w.sizePolicy();
    return a.exec();
}

In my system, the default size of QWidget is (640,480)(640 is the widget’s default width, 480 is the widget’s default height). The default size of QPushButton(without text on the button) is (32,23). The default size of QLabel(empty content) is (5,13). The default size of QTextEdit is (256,192). The default size of QLineEdit is (200,20). The default size of QMainWindow is (200,100). The default size of QDialog is (640,480). I think the default size may be determined according to your screen resolution.

sizeHint

Every qt widget has a sizeHint function. You can print the returned value of this function to see what it is, as done in the above code snippet. This value, according to the official document, is the “recommended size for the widget”. Of course, you cannot get anything by just reading this statement. The document also says the sizeHint returns an invalid size if the widget has no layout set, and if the widget has a layout, returns the preferred size of the layout. According to my finds, this is partially true. For QWidget,QDialog that has no layout, the returned value is (-1,-1) which is an invalid size. For QMainWindow, the sizeHint is (0,0), which is a valid size, because QMainWindow does have a default layout QMainWindowLayout even you do not set it manually. For QPushButton, QLabel, QTextEdit, the returned value of sizeHint is the same as its size. If the size changes, e.g., for increased content on the control widget, the sizeHint also changes accordingly. The sizeHint of QLineEdit is not the same(less than) as its size. So for these control widgets, the official statement about the sizeHint is not correct, because these widgets do not have a layout but their sizeHint is a valid size.

The sizeHint function of QWidget is a virtual function that can be re-implemented by inherited classes. Let’s study how sizeHint is used if a widget has no layout set.

 

//example 2
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

    QSize sizeHint() const;
    QSize minimumSizeHint() const;
};

QSize Widget::sizeHint() const
{
    return QSize(500, 500);
}


QSize Widget::minimumSizeHint() const
{
    return QSize(300, 300);
}

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

    Widget w;

    w.show();
    qDebug()<<w.size();
    qDebug()<<w.sizeHint();
    qDebug()<<w.layout();
    qDebug()<<w.minimumSize();
    qDebug()<<w.minimumSizeHint();
    qDebug()<<w.sizePolicy();
    return a.exec();
}

In the above example, we show a QWidget derived widget, which re-implements the sizeHint to return a size of (500,500). You can see the window displayed has just the size of (500,500). That means the returned value of sizeHint is the initial size of the window(i.e., the size of the window when it is just created). So we can use sizeHint to control the initial window size of your app. Of course, later, users may adjust the size of the window, and the returned value of the size function would change accordingly but sizeHint keeps the same.

Now let’s consider the situation where widget has a layout. We have known that if a widget has no layout, its sizeHint will return an invalid size (-1,-1), and its size is set to a default one:(640,480). We can also find its minimumSize function returns (0,0) and its minimumSizeHint function returns (-1,-1). In the following example, the top window(tw, a QWidget object) has a layout which has nothing in it.

//example 3
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget tw(0, Qt::FramelessWindowHint);

    QVBoxLayout layout(&tw);
    

    tw.show();


    qDebug()<<layout.sizeHint();
    qDebug()<<layout.minimumSize();

    qDebug()<<tw.size();
    qDebug()<<tw.sizeHint();
    qDebug()<<tw.layout();

    qDebug()<<tw.minimumSize();
    qDebug()<<tw.minimumSizeHint();
    qDebug()<<tw.sizePolicy();
    return a.exec();
}

Even the layout has no widget laid on it at present, it still has a minimum size: layout.minimumSize=(22,22). This is the minimum space reserved for this layout. The sizeHint of the layout is also set to this minimum size (22,22). Now the widget containing this layout uses this information to set its size and minimumSize to (22,22), and its sizeHint and minimumSizeHint are also set to (22,22).

Now let’s add our QWidget-derived widget to this layout and see what happens on the top-level widget tw.

//example 4
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget tw(0, Qt::FramelessWindowHint);

    QVBoxLayout layout(&tw);

    Widget w;
    layout.addWidget(&w);

    tw.show();


    qDebug()<<layout.sizeHint();
    qDebug()<<layout.minimumSize();

    qDebug()<<tw.size();
    qDebug()<<tw.sizeHint();
    qDebug()<<tw.layout();

    qDebug()<<tw.minimumSize();
    qDebug()<<tw.minimumSizeHint();
    qDebug()<<tw.sizePolicy();
    return a.exec();
}

Now the sizeHint of the layout becomes (522,522), which is the sum of the sizeHint of the containing widget w and the minimumSize of the layout itself. The minimumSize of the layout becomes (322,322), i.e., the sum of the minimumSizeHint of the containing widget and the minimumSize  of the layout itself. Note that there is NO minimumSizeHint function for layout. The sizeHint of the top-level  tw is set to the sizeHint of its layout, i.e.,  (522,522). The minimumSize of tw is set to the minimumSize of its layout (322,322). The minimumSizeHint of tw is  set to its minimumSize (322,322). The sizeHint is just a reference. In our example, the real size of w is (500,490) and the real size of tw is(522,512).

You should pay attention to the difference between minimumSize and minimumHintSize. You can use the setMinimumSize member function to set the minimum size of a widget, but you should provide your own minimumHintSize (re-implemented virtual function) if you want to give custom hint size. The size of the widget cannot be smaller than minimumSize , i.e., you cannot shrink the widget smaller than that size by dragging the boundaries of the widget. But you can squeeze the widget smaller than minimumHintSize. The value of minimumHintSize  does not affect the value of minimumSize.

Now back to example 4. If you change

QWidget tw(0, Qt::FramelessWindowHint);

to:

QWidget tw;

i.e., restore the window title and bar, you will be able to drag the boundaries of tw to adjust its size. But you cannot squeeze tw smaller than (322,322) because its minimumSize is set to (322,322) according to the minimumSize of its layout. You cannot squeeze the QWidget-derived w smaller than (300,300), either, but that is not because its minimumSize is set to (300,300)(in fact, the minimumSize  of w is still (0,0)). It is just because there is no frame around w for you to drag.

It is time to talk about sizePolicy…

You can use setSizePolicy to set the horizontal and vertical size policy of a widget. If not set, QWidget,QDialog,QMainWindow have a QSizePolicy::Preferred policy, which means the window can be expanded or shrunk  freely in a layout. Widgets of control type such as QPushButton, QTextEdit have  special/different size policies. The function sizePolicy returns the current size policy of a widget. Let’s consider the following example:

//example 5
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget tw(0, Qt::FramelessWindowHint);

    QVBoxLayout layout(&tw);

    QTexEdit w1;
    QPushButton w2;
    layout.addWidget(&w1);
    layout.addWidget(&w2);

    tw.show();


    qDebug()<<layout.sizeHint();
    qDebug()<<layout.minimumSize();

    qDebug()<<tw.size();
    qDebug()<<tw.sizeHint();
    qDebug()<<tw.layout();

    qDebug()<<tw.minimumSize();
    qDebug()<<tw.minimumSizeHint();
    qDebug()<<tw.sizePolicy();
    return a.exec();
}

The main window tw has two widgets on it: a QTextEdit and a QPushButton. It is amazing that when you expand the main window vertically, the text edit becomes larger but the button keeps the same size, which is exactly what you want. How is that implemented? This is where the sizePolicy comes in handy. QTextEdit has a QSizePolicy::Expanding policy in both horizontal and vertical direction, which means it will expand as largely as possible.  QPushButton has a QSizePolicy::Fixed vertical size policy, which means its height is always the value got from sizeHint. The horizontal size policy of QPushButton is QSizePolicy::Minimum, which means its width can not be shrunk less than what sizeHint specifies(but can be expanded horizontally). Note that the size policy only takes effect when the widget is in a layout, otherwise, does not affect the size of the widget.

Conclusion

The sizeHint is calculated as follows:

  • bottom-level widgets have an invalid sizeHint or provide a custom one.
  • layout containing widgets uses the sizeHint of the widgets and the minimumSize of its own to calculate its own sizeHint.
  • Widget containing layout uses the sizeHint of the layout as its own sizeHint

The minimumSize is calculated as follows:

  • Buttom-level widgets has a minimumSize of(0,0) or you can set it to a custom one.
  • Layout containing the bottom-level widgets uses the minumumSizeHint(NOT  minimumSize) of the containing widgets and its own minimumSize to recalculate its own minimumSize .
  • Widget containing the layout uses the minimumSize of the layout as its own minimumSize .

The minimumSizeHint is calculated as follows:

  • Bottom-level widgets provide custom minimumSizeHint  or an invalid one.
  • Layout containing bottom-level widgets pluses the minimumSizeHint  of the containing widgets and the minimumSize of its own to form its own minimumSizeHint .
  • Widget having a layout uses the layout’s minimumSize(also the layout’s minimumHintSize and its own  minimumSize) as its own minimumSizeHint .

 

Posted in

Leave a Reply