Layout management in Qt4

In this part of the Qt4 programming tutorial, we will talk about the layout management of widgets.

A typical application consists of various widgets. Those widgets are placed inside layouts. A programmer must manage the layout of the application. In Qt4 we have two options:

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.

There might be situations where we can possibly use absolute positioning. But mostly, in real world programs, programmers use layout managers.

absolute.cpp
#include <QApplication>
#include <QDesktopWidget>
#include <QTextEdit>

class Absolute : public QWidget {
    
 public:
     Absolute(QWidget *parent = 0);
};

Absolute::Absolute(QWidget *parent)
    : QWidget(parent) {
        
  QTextEdit *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.

QTextEdit *edit = new QTextEdit(this);
edit->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.

Before resizement
Figure: before resizement
After resizement
Figure: after resizement

QVBoxLayout

The QVBoxLayout class lines up widgets vertically. The widgets are added to the layout using the addWidget() method.

verticalbox.h
#pragma once

#include <QWidget>

class VerticalBox : public QWidget {

  public:
    VerticalBox(QWidget *parent = 0);

};

The header file.

verticalbox.cpp
#include "verticalbox.h"
#include <QVBoxLayout>
#include <QPushButton>

VerticalBox::VerticalBox(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout(this);
  vbox->setSpacing(1);
  
  QPushButton *settings = new QPushButton("Settings", this);
  settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *accounts = new QPushButton("Accounts", this);
  accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *loans = new QPushButton("Loans", this);
  loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *cash = new QPushButton("Cash", this);
  cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *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.

QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setSpacing(1);

We create the QVBoxLayout and set 1 px spacing among child widgets.

QPushButton *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.

main.cpp
#include "verticalbox.h"
#include <QApplication>

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.

QVBoxLayout
Figure: QVBoxLayout

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.

buttons.h
#pragma once

#include <QWidget>
#include <QPushButton>

class Buttons : public QWidget {
    
  public:
    Buttons(QWidget *parent = 0);

  private:
    QPushButton *okBtn;
    QPushButton *applyBtn;

};

Header file.

buttons.cpp
#include "buttons.h"
#include <QVBoxLayout>
#include <QHBoxLayout>

Buttons::Buttons(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout(this);
  QHBoxLayout *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.

QVBoxLayout *vbox = new QVBoxLayout(this);
QHBoxLayout *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.

main.cpp
#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.

Buttons
Figure: Buttons

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.

layouts.h
#pragma once

#include <QWidget>

class Layouts : public QWidget {

  public:
    Layouts(QWidget *parent = 0);

};

Header file.

layouts.cpp
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include "layouts.h"

Layouts::Layouts(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout();
  QHBoxLayout *hbox = new QHBoxLayout(this);

  QListWidget *lw = new QListWidget(this);
  lw->addItem("The Omen"); 
  lw->addItem("The Exorcist");
  lw->addItem("Notes on a scandal");
  lw->addItem("Fargo");
  lw->addItem("Capote");

  QPushButton *add = new QPushButton("Add", this);
  QPushButton *rename = new QPushButton("Rename", this);
  QPushButton *remove = new QPushButton("Remove", this);
  QPushButton *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.

QVBoxLayout *vbox = new QVBoxLayout();

The QVBoxLayout will be the column for the buttons.

QHBoxLayout *hbox = new QHBoxLayout(this);

The QHBoxLayout will be the base layout for the widgets.

QListWidget *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.

QPushButton *add = new QPushButton("Add", this);
QPushButton *rename = new QPushButton("Rename", this);
QPushButton *remove = new QPushButton("Remove", this);
QPushButton *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.

main.cpp
#include <QApplication>
#include "layouts.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Layouts window;
  
  window.setWindowTitle("Layouts");
  window.show();

  return app.exec();
}

The main file.

Layouts
Figure: Layouts

QFormLayout

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.

form.h
#pragma once

#include <QWidget>

class FormEx : public QWidget {
    
  public:
    FormEx(QWidget *parent = 0);

};

This is the header filer.

form.cpp
#include "form.h"
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>

FormEx::FormEx(QWidget *parent)
    : QWidget(parent) {
        
  QLineEdit *nameEdit = new QLineEdit(this);
  QLineEdit *addrEdit = new QLineEdit(this);
  QLineEdit *occpEdit = new QLineEdit(this);
  
  QFormLayout *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.

QFormLayout *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.

main.cpp
#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();
}

The main file.

Simple form
Figure: Simple form

QGridLayout

QGridLayout places its widgets in a grid. It is a powerful layout manager.

calculator.h
#pragma once

#include <QWidget>

class Calculator : public QWidget {

  public:
    Calculator(QWidget *parent = 0);
};

This is the header file.

calculator.cpp
#include <QGridLayout>
#include <QPushButton>
#include "calculator.h"

Calculator::Calculator(QWidget *parent)
    : QWidget(parent) {

  QGridLayout *grid = new QGridLayout(this);
  grid->setSpacing(2);

  QList<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++) {
       
     QPushButton *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.

QGridLayout *grid = new QGridLayout(this);
grid->setSpacing(2);

We create the grid layout and set 2 px space among child widgets.

QList<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++) {
    
      QPushButton *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 will have a fixed size.

main.cpp
#include <QApplication>
#include "calculator.h"

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

  QApplication app(argc, argv); 

  Calculator window;

  window.move(300, 300);
  window.setWindowTitle("Calculator");
  window.show();

  return app.exec();
}

This is the main file.

QGridLayout
Figure: QGridLayout

Review

In the next example of this chapter, we create a more complicated window using the QGridLayout manager.

review.h
#pragma once

#include <QWidget>

class Review : public QWidget {
    
  public:
    Review(QWidget *parent = 0);

};

Header file.

review.cpp
#include "review.h"
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>

Review::Review(QWidget *parent)
    : QWidget(parent) {

  QGridLayout *grid = new QGridLayout(this);
  grid->setVerticalSpacing(15);
  grid->setHorizontalSpacing(10);

  QLabel *title = new QLabel("Title:", this);
  grid->addWidget(title, 0, 0, 1, 1);
  title->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

  QLineEdit *edt1 = new QLineEdit(this);
  grid->addWidget(edt1, 0, 1, 1, 1);

  QLabel *author = new QLabel("Author:", this);
  grid->addWidget(author, 1, 0, 1, 1);
  author->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

  QLineEdit *edt2 = new QLineEdit(this);
  grid->addWidget(edt2, 1, 1, 1, 1);

  QLabel *review = new QLabel("Review:", this);
  grid->addWidget(review, 2, 0, 1, 1);
  review->setAlignment(Qt::AlignRight | Qt::AlignTop);

  QTextEdit *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.

QGridLayout *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.

QLabel *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.

QTextEdit *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.

main.cpp
#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.

Review
Figure: Review

This part of the Qt4 tutorial was dedicated to layout management.