Layout management in Qt5
last modified October 18, 2023
In this part of the Qt5 programming tutorial, we talk about the layout
management of widgets. We mention QHBoxLayout
, QVBoxLayout
,
QFormLayout
, and QGridLayout
managers.
A typical application consists of various widgets. Those widgets are placed inside layouts. A programmer must manage the layout of the application. In Qt5 we have two options:
- absolute positioning
- layout managers
Absolute Positioning
The programmer specifies the position and the size of each widget in pixels. When we use absolute positioning, we have to understand several things.
- The size and the position of a widget do not change if we resize a window.
- Applications look different (often poorly) on various platforms.
- Changing fonts in our application might spoil the layout.
- If we decide to change our layout, we must completely redo our layout, which is tedious and time consuming.
There might be situations where we can possibly use absolute positioning. But mostly, in real world programs, programmers use layout managers.
#include <QApplication> #include <QDesktopWidget> #include <QTextEdit> class Absolute : public QWidget { public: Absolute(QWidget *parent = nullptr); }; Absolute::Absolute(QWidget *parent) : QWidget(parent) { auto *ledit = new QTextEdit(this); ledit->setGeometry(5, 5, 200, 150); } int main(int argc, char *argv[]) { QApplication app(argc, argv); Absolute window; window.setWindowTitle("Absolute"); window.show(); return app.exec(); }
The setGeometry
method is used to position the widget on the
window in absolute coordinates.
auto *edit = new QTextEdit(this); ledit->setGeometry(5, 5, 200, 150);
We create a QTextEdit
widget and manually position it. The
setGeometry
method does two things: it positions the widget to
absolute coordinates and resizes the widget.
Qt5 QVBoxLayout
The QVBoxLayout
class lines up widgets vertically. The widgets are
added to the layout using the addWidget
method.
#pragma once #include <QWidget> class VerticalBox : public QWidget { public: VerticalBox(QWidget *parent = nullptr); };
The header file.
#include <QVBoxLayout> #include <QPushButton> #include "vertical_box.h" VerticalBox::VerticalBox(QWidget *parent) : QWidget(parent) { auto *vbox = new QVBoxLayout(this); vbox->setSpacing(1); auto *settings = new QPushButton("Settings", this); settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); auto *accounts = new QPushButton("Accounts", this); accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); auto *loans = new QPushButton("Loans", this); loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); auto *cash = new QPushButton("Cash", this); cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); auto *debts = new QPushButton("Debts", this); debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); vbox->addWidget(settings); vbox->addWidget(accounts); vbox->addWidget(loans); vbox->addWidget(cash); vbox->addWidget(debts); setLayout(vbox); }
In our example, we have one vertical layout manager. We put five buttons into it. We make all buttons expandable in both directions.
auto *vbox = new QVBoxLayout(this); vbox->setSpacing(1);
We create the QVBoxLayout
and set 1 px spacing among
child widgets.
auto *settings = new QPushButton("Settings", this); settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
We create a button and set a size policy for it. The child widgets are managed by the layout manager. By default, the button is expanded horizontally and has a fixed size vertically. If we want to change it, we set a new size policy. In our case, the button is expandable into both directions.
vbox->addWidget(settings); vbox->addWidget(accounts); ...
We add the child widgets to the layout manager with the addWidget
method.
setLayout(vbox);
We set the QVBoxLayout
manager for the window.
#include <QApplication> #include "vertical_box.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); VerticalBox window; window.resize(240, 230); window.setWindowTitle("VerticalBox"); window.show(); return app.exec(); }
The main file.
Buttons
In the following example, we display two buttons on the client area of the window. They will be positioned in the right bottom corner of the window.
#pragma once #include <QWidget> #include <QPushButton> class Buttons : public QWidget { public: Buttons(QWidget *parent = nullptr); private: QPushButton *okBtn; QPushButton *applyBtn; };
Header file.
#include <QVBoxLayout> #include <QHBoxLayout> #include "buttons.h" Buttons::Buttons(QWidget *parent) : QWidget(parent) { auto *vbox = new QVBoxLayout(this); auto *hbox = new QHBoxLayout(); okBtn = new QPushButton("OK", this); applyBtn = new QPushButton("Apply", this); hbox->addWidget(okBtn, 1, Qt::AlignRight); hbox->addWidget(applyBtn, 0); vbox->addStretch(1); vbox->addLayout(hbox); }
Say we wanted to have two buttons in the right bottom corner of the window.
auto *vbox = new QVBoxLayout(this); auto *hbox = new QHBoxLayout();
We create two box layout managers: one vertical and one horizontal box layout manager.
okBtn = new QPushButton("OK", this); applyBtn = new QPushButton("Apply", this);
We create two push buttons.
hbox->addWidget(okBtn, 1, Qt::AlignRight); hbox->addWidget(applyBtn, 0);
The buttons are placed inside the horizontal layout manager. with the
addWidget
method. These buttons are right aligned. The first
parameter is the child widget. The second parameter is the stretch factor, and
the last parameter is alignment. By setting the stretch factor to 1 for the OK
button, we give it space from the left side to the right side of the window. The
widget does not expand to all space alloted to it. Finally, the
Qt::AlignRight
constant aligns the widget to the right of the
allotted space.
vbox->addStretch(1); vbox->addLayout(hbox);
We put an empty, expandable space into the vertical box by calling the
addStretch
method. Then we add the horizontal box layout to the
vertical box layout.
#include <QApplication> #include "buttons.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Buttons window; window.resize(290, 170); window.setWindowTitle("Buttons"); window.show(); return app.exec(); }
The main file.
Qt5 nesting layouts
The idea of the following example is to show that layout managers can be
combined. By combination of even simple layouts we can create sophisticated
dialogs or windows. To nest layouts, we utilize the addLayout
method.
#pragma once #include <QWidget> class Layouts : public QWidget { public: Layouts(QWidget *parent = nullptr); };
This is the header file.
#include <QVBoxLayout> #include <QPushButton> #include <QListWidget> #include "nesting.h" Layouts::Layouts(QWidget *parent) : QWidget(parent) { auto *vbox = new QVBoxLayout(); auto *hbox = new QHBoxLayout(this); auto *lw = new QListWidget(this); lw->addItem("The Omen"); lw->addItem("The Exorcist"); lw->addItem("Notes on a scandal"); lw->addItem("Fargo"); lw->addItem("Capote"); auto *add = new QPushButton("Add", this); auto *rename = new QPushButton("Rename", this); auto *remove = new QPushButton("Remove", this); auto *removeall = new QPushButton("Remove All", this); vbox->setSpacing(3); vbox->addStretch(1); vbox->addWidget(add); vbox->addWidget(rename); vbox->addWidget(remove); vbox->addWidget(removeall); vbox->addStretch(1); hbox->addWidget(lw); hbox->addSpacing(15); hbox->addLayout(vbox); setLayout(hbox); }
In the example, we create a window that consists of four buttons and one list widget. The buttons are grouped in a vertical column and placed to the right of the list widget. If we resize the window, the list widget is being resized as well.
auto *vbox = new QVBoxLayout();
The QVBoxLayout
is the column for the buttons.
auto *hbox = new QHBoxLayout(this);
The QHBoxLayout
is the base layout for the widgets.
auto *lw = new QListWidget(this); lw->addItem("The Omen"); lw->addItem("The Exorcist"); lw->addItem("Notes on a scandal"); lw->addItem("Fargo"); lw->addItem("Capote");
The QListWidget
is created.
auto *add = new QPushButton("Add", this); auto *rename = new QPushButton("Rename", this); auto *remove = new QPushButton("Remove", this); auto *removeall = new QPushButton("Remove All", this);
Here we create our four buttons.
vbox->setSpacing(3); vbox->addStretch(1); vbox->addWidget(add); vbox->addWidget(rename); vbox->addWidget(remove); vbox->addWidget(removeall); vbox->addStretch(1);
The vertical box with four buttons is created. We put some little space among our buttons. Notice that we add a stretch factor to the top and to the bottom of the vertical box. This way the buttons are vertically centered.
hbox->addWidget(lw); hbox->addSpacing(15); hbox->addLayout(vbox);
The list widget and the vertical box of buttons are placed into the horizontal
box layout. The addLayout
method is used to add a layout to
another layout.
setLayout(hbox);
We set the base layout for the parent window.
#include <QApplication> #include "nesting.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Layouts window; window.setWindowTitle("Layouts"); window.show(); return app.exec(); }
This is the main file.
Qt5 FormLayout
QFormLayout
is a simple layout manager that manages forms of input
widgets and their associated labels. It lays out its children in a two-column
form. The left column consists of labels and the right column consists of input
widgets like QLineEdit
or QSpinBox
.
#pragma once #include <QWidget> class FormEx : public QWidget { public: FormEx(QWidget *parent = nullptr); };
This is the header filer.
#include <QFormLayout> #include <QLabel> #include <QLineEdit> #include "form.h" FormEx::FormEx(QWidget *parent) : QWidget(parent) { auto *nameEdit = new QLineEdit(this); auto *addrEdit = new QLineEdit(this); auto *occpEdit = new QLineEdit(this); auto *formLayout = new QFormLayout; formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); formLayout->addRow("Name:", nameEdit); formLayout->addRow("Email:", addrEdit); formLayout->addRow("Age:", occpEdit); setLayout(formLayout); }
The example creates a form consisting of three labels and three line edits.
auto *formLayout = new QFormLayout;
An instance of the QFormLayout
is created.
formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
With the setLabelAlignment
metho, we set the alignment of the label
widgets.
formLayout->addRow("Name:", nameEdit);
The addRow
method adds a new row to the bottom of the form layout,
with the given label and input widget.
#include <QApplication> #include "form.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); FormEx window; window.setWindowTitle("Form example"); window.show(); return app.exec(); }
This is the main file.
Qt5 QGridLayout
QGridLayout
places its widgets in a grid. It is a powerful layout
manager.
#pragma once #include <QWidget> class Calculator : public QWidget { public: Calculator(QWidget *parent = nullptr); };
Header file.
#include <QGridLayout> #include <QPushButton> #include "calculator.h" Calculator::Calculator(QWidget *parent) : QWidget(parent) { auto *grid = new QGridLayout(this); grid->setSpacing(2); QVector<QString> values({ "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }); int pos = 0; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { auto *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40); grid->addWidget(btn, i, j); pos++; } } setLayout(grid); }
We create a skeleton of a calculator.
auto *grid = new QGridLayout(this); grid->setSpacing(2);
We create the grid layout and set 2px space among child widgets.
QVector<QString> values({ "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" });
These are the characters that are displayed on the buttons.
for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { auto *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40); grid->addWidget(btn, i, j); pos++; } }
We place sixteen widgets into the grid layout. Each of the buttons has a fixed size.
#include <QApplication> #include "calculator.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Calculator window; window.setWindowTitle("Calculator"); window.show(); return app.exec(); }
This is the main file.
Review
In the next example of this chapter, we create a more complicated window using
the QGridLayout
manager.
#pragma once #include <QWidget> class Review : public QWidget { public: Review(QWidget *parent = nullptr); };
Header file.
#include <QGridLayout> #include <QLabel> #include <QLineEdit> #include <QTextEdit> #include "review.h" Review::Review(QWidget *parent) : QWidget(parent) { auto *grid = new QGridLayout(this); grid->setVerticalSpacing(15); grid->setHorizontalSpacing(10); auto *title = new QLabel("Title:", this); grid->addWidget(title, 0, 0, 1, 1); title->setAlignment(Qt::AlignRight | Qt::AlignVCenter); auto *edt1 = new QLineEdit(this); grid->addWidget(edt1, 0, 1, 1, 1); auto *author = new QLabel("Author:", this); grid->addWidget(author, 1, 0, 1, 1); author->setAlignment(Qt::AlignRight | Qt::AlignVCenter); auto *edt2 = new QLineEdit(this); grid->addWidget(edt2, 1, 1, 1, 1); auto *review = new QLabel("Review:", this); grid->addWidget(review, 2, 0, 1, 1); review->setAlignment(Qt::AlignRight | Qt::AlignTop); auto *te = new QTextEdit(this); grid->addWidget(te, 2, 1, 3, 1); setLayout(grid); }
The code creates a window which could be used to enter an author, title, and a review for a book.
auto *grid = new QGridLayout(this);
The QGridLayout
manager is created.
grid->setVerticalSpacing(15); grid->setHorizontalSpacing(10);
We add vertical spacing with the setVerticalSpacing
method and horizontal spacing with the setHorizontalSpacing
method.
auto *title = new QLabel("Title", this); grid->addWidget(title, 0, 0, 1, 1);
These code lines create a label widget and place it into the grid layout. The
addWidget
method has five parameters. The first parameter is the
child widget, a label in our case. The next two parameters are the row and
column in the grid where we place the label. Finally, the last parameters are
the rowspan and the colspan. These parameters specify how many rows the current
widget will span. In our case, the label will span only one column and one row.
title->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
The setAlignment
method aligns the title label in its cell.
Horizontally, it is right aligned. Vertically, it is centered.
auto *te = new QTextEdit(this); grid->addWidget(te, 2, 1, 3, 1);
The QTextEdit
widget is placed into the third row and second column;
it spans three rows and one column.
#include <QApplication> #include "review.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Review window; window.setWindowTitle("Review"); window.show(); return app.exec(); }
Main file.
This part of the Qt5 tutorial was dedicated to layout management.