3. Qt Widgets Layouts

When you create a Qt widget like QPushButton you need a way to position it inside a window or a container widget. It is possible, but quite uncommon, to position Qt widgets by setting their x and y coordinates - Most Qt applications use layouts to automatically arrange widgets in rows, columns grids, forms, or stacks, to achieve the desired layout1.

3.1 Laying out Widgets Vertically - QVBoxLayout

QVBoxLayout arranges widgets in a column. If you look up the QVBoxLyout parents in the Qt documentation, you’ll notice that QWidget is not a QVBoxLayout’s parent class. This means you cannot show a layout on the screen - it is not a visual element but a geometry manager.

An icon of a clipboard-list1

To add a vertical layout to a QWidget:

  1. Create a QVBoxLayout instance and set it as your window layout with setLayout(). This also works for any QWidget subclass. You can nest layouts inside other layouts to achieve complex layouts.

  2. Create widgets you want to lay out. Here we create three QPushButton instances.

  3. Add them to the layout using addWidget(). For nested layouts you would use addLayout() instead.

Run the script and resize the window: The buttons stack vertically and expand horizontally to fill the width, but their height stays fixed, creating gaps between them. There are two ways you can change this:

  • Add stretchable space with addStretch() at the end of the layout - it pushes the widgets to the top while allowing empty space to grow.
  • Set a widget’s size Expanding for both directions, e.g. button_1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding). This lets it resize in both axes.

We’ve also connected all three buttons’ clicked signals to a single @Slot() method. Inside the slot, you can use QObject.sender() to identify the sender: sender = self.sender()

as we need a way to tell which button emitted the clicked signal. This lets us reuse a single slot to handle signals from different widgets - but keep in mind this warning from the Qt docs:

Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.

When executed, the script shows a window like this:

3.2 Horizontal Layout - QHBoxLayout

An icon of a clipboard-list1

QHBoxLayout arranges widgets horizontally. The setup is the same as for QVBoxLayout:

  1. Create the layout and assign it to a QWidget with setLayout()

  2. Create child widgets. Here, a QPushButton and QLabel. We set the label’s minimum width to fit its text.

  3. Add them to the layout using addWidget() in the desired order.

For interactivity, we’ve connected the button’s clicked signal to update the label with the current time using QTime.currentTime(). This shows an important point: Qt is more than a GUI toolkit and offers tools that overlap with Python’s - for instance the developer needs to decide whether to use time from the Python standard library or QTime from PySide6,QtCore.

After execution, you’ll see a window like this:

Both layout classes arrange widgets in the order you add them with addWidget(), QVBoxLayout top to bottom, and QHBoxLayout left to right. You can change this their setDirection() method with the QBoxLayout.BottomToTop or QBoxLayout.RightToLeft. You can achieve dynamic layouts by using removeWidget() to remove a widget from a layout or insertWidget() to insert a widget out of order.

If you create a widget without setting its parent, as we do in the above examples, adding them to a widget’s layout reparents them to that widget.

3.3 Grid Layout - QGridLayout

QGridLayout positions widgets in a grid. Unlike linear layouts, you specify each widget’s row and column when adding it.

An icon of a clipboard-list1

To use a grid layout:

  1. Create a QGridLayout instance and set it as a QWidget layout.

  2. Create widgets and add them with addWidget(widget, row, col).Leave cells empty for gaps. For spanning, use addWidget(widget, row, col, row_span, col_span) - e.g., addWidget(self.label, 4, 0, 1, 4) for a label spanning one row over four columns.

Here, we populate a 4x4 grid with 16 QPushButtons using two nested loops. Using sender() in the slot lets us identify the clicked button.

After execution, the window looks like this:

3.4 Form Layout - QFormLayout

QFormLayout creates a two-column form layout: labels on the left, fields on the right, similar to HTML forms.

An icon of a clipboard-list1

To implement it:

  1. Create a QFormLayout instance and set it as the QWidget layout.

  2. Create child widgets. Since we need to access the widgets from the slot method, we make the them Windows instance members.

  3. Add child widgets to the form using QFormLayout.addRow(label_text, widget). addRow() is a convenience method that automatically creates a QLabel instance for you, setting its text to label_text.

In this example, three QlineEdit fields handle user input. Their editingFinished is emitted on pressing Enter/Return or when focus leaves the changed field. A single slot updates a summary QLabel with the values. (No sender() needed here since we reference widgets directly.)


  1. If you don’t implement __init__() in your class at all, the parent class gets initalized automatically.↩︎

  2. https://doc.qt.io/qtforpython-6/↩︎

  3. https://doc.qt.io/↩︎

  4. https://doc.qt.io/qt-6/qobject.html↩︎