topic logo
Qt ⇢ Unit 8

Matroska

nested layouts and widgets

Summary

With this method complex application layouts can be realized while keeping the code readable.

Background

One of the previously introduced layouts was the grid layout that allows flexible item arrangement, but it has the downside of using index based addressing. These layouts are quickly getting hard to maintain if not built by a loop (or use the GUI designer). There is a good alternative though.

The Fourth Layout App

This unit comes with a simpler example. The application window is derived again from QWidget and it has only a constructor. It shows how to create two layouts and nest one into the other to achieve a similar result than the previous grid example that used spans.

Lower Row is a Separate Layout
Lower Row is a Separate Layout

The Constructor

Layouts4.cpp - constructor
Layouts4::Layouts4()
{
    setGeometry(100, 100, 300, 200);

    QList<QLabel*> labels;
    for (int i=0; i<3; i++) {
        auto label = new QLabel();
        labels << label;
        label->setAlignment(Qt::AlignCenter);
        label->setStyleSheet("background-color: lightgray");
    }

    labels.at(0)->setText("Outer");
    labels.at(1)->setText("Inner 1");
    labels.at(2)->setText("Inner 2");

    auto layoutInner = new QHBoxLayout();
    layoutInner->addWidget(labels.at(1));
    layoutInner->addWidget(labels.at(2));

    auto layoutOuter = new QVBoxLayout(this);
    layoutOuter->addWidget(labels.at(0));
    layoutOuter->addLayout(layoutInner);
}

The previous examples used the QWidget::resize(w, h) method to resize the application window. That method asks the underlying window manager (basically the desktop) to put the window with a specific size to wherever it’s convenient (preferably in some area that is not already covered by a window).

setGeometry(100, 100, 300, 200);

This example uses the QWidget::setGeometry(x, y, w, h) method to accomplish a similar effect, but instead of placing the window just somewhere convenient, it asks the window manager to place it at a specific spot on the screen. In this case that spot is exactly one hundred pixels from the top and the same distance from the left. This is where the upper-left corner of the window will end up. The third and fourth parameters tell what size it should be just as “QWidget::resize()” did. Generally resizing is preferable unless there is a good reason to insist on a particular, fixed placing.

After the amenities the constructor creates three labels and places them in a local list and sets the text of the labels.

auto layoutInner = new QHBoxLayout();
layoutInner->addWidget(labels.at(1));
layoutInner->addWidget(labels.at(2));

It goes on to create a horizontal layout and adds the two “inner” labels. This layout has no parent argument, so it’s not applied to any widget. Instead..

auto layoutOuter = new QVBoxLayout(this);
layoutOuter->addWidget(labels.at(0));
layoutOuter->addLayout(layoutInner);

It creates a second layout. This one is getting the parent argument. As before a widget is added to it, but then it adds the previous layout with the “QBoxLayout::addLayout()” method, just as if it were a normal widget. This works with horizontal and vertical layouts that inherit the “QBoxLayout” class.

Alternative Method

There is another option to compose the layout of the user interface: the application may have nested container widgets that each have their individual layouts applied. This requires a bit more code, but it allows customization of the containers with the full capability of the QWidget class. For complex applications this has the additional advantage of user interface compartmentalization. This way parts of the user interface can go into their own classes and source files, allowing better code organization and transparency. The root window just needs to compose those. This highly improves maintainability of complex applications.

standalone example of nested widget layout
auto container = new QWidget();
auto w1 = new QPushButton("w1");
auto w2 = new QPushButton("w2");
auto w3 = new QPushButton("w3");

auto parentLayout = new QVBoxLayout(this);
parentLayout->addWidget(container);
parentLayout->addWidget(w1);

auto containerLayout = new QHBoxLayout(container);
containerLayout->addWidget(w2);
containerLayout->addWidget(w3);
Layouts on Nested Widgets get Default Margins
Layouts on Nested Widgets get Default Margins

Note that with this method the default margins are added for each nested widget and need to be managed individually.

This is not part of the unit example download, but can be reproduced by replacing the content of the constructor with the above code.

Conclusion

Nesting layouts is a versatile and maintainable technique for implementing application interfaces. Combined with container widgets they form the foundation of any serious UI implementation.