Widget size and placement in the tabs of a QTabWidget

How to control widget positioning in tab layouts using addStretch
Heads up! You've already completed this tutorial.

I have a QTabWidget with several tabs. Some tabs have many widgets and look fine, but tabs with only a few widgets look strange — the widgets are spread out across the entire tab. How do I keep widgets positioned at the top of a tab instead of having them stretched apart?

When building tabbed interfaces with QTabWidget, you'll often have tabs with very different amounts of content. A tab with just a couple of widgets can look a bit awkward — the widgets spread out vertically to fill the available space, leaving large gaps between them. Meanwhile, a tab packed with many widgets looks perfectly fine because the content naturally fills the area.

This is a common situation when using QVBoxLayout (or any box layout) inside your tabs. The layout distributes available space evenly among its child widgets, so with only a couple of widgets, each one gets stretched across a large portion of the tab.

The fix is simple: add a stretch to the layout. Let's walk through the problem and solution.

The problem: widgets spread across the tab

Imagine you have a QTabWidget with multiple tabs. Your first tab only has a label and a text input, while your second tab is full of widgets. Here's what that might look like in code:

python
import sys

from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QLineEdit,
    QTabWidget,
    QVBoxLayout,
    QWidget,
)


class MainWindow(QTabWidget):
    def __init__(self):
        super().__init__()

        # First tab — only two widgets
        title = QLabel("<b>Load model file</b>")
        loader = QLineEdit()
        loader.setPlaceholderText("Enter file path...")

        tab1 = QWidget()
        layout1 = QVBoxLayout()
        layout1.addWidget(title)
        layout1.addWidget(loader)
        tab1.setLayout(layout1)
        self.addTab(tab1, "Load model")

        # Second tab — many widgets
        tab2 = QWidget()
        layout2 = QVBoxLayout()
        layout2.addWidget(QLabel("<b>Dynamic simulation</b>"))
        layout2.addWidget(
            QLabel(
                "Use time-domain simulation to predict model evolution.\n"
                "Start with defining your 2nd-order dynamics simulator here."
            )
        )
        for i in range(6):
            layout2.addWidget(QLineEdit(f"Parameter {i + 1}"))
        tab2.setLayout(layout2)
        self.addTab(tab2, "Run simulations")


app = QApplication(sys.argv)
window = MainWindow()
window.setMinimumSize(500, 400)
window.show()
app.exec()

If you run this, you'll see that the first tab's label and text input are spread apart vertically, floating in the middle of a large empty space. The layout is doing exactly what it's supposed to — distributing the available vertical space equally among its widgets. But visually, it looks odd. You'd probably prefer those two widgets sitting neatly at the top of the tab.

The solution: addStretch()

The QVBoxLayout (and QHBoxLayout) class has a method called addStretch(). When you add a stretch to a layout, it inserts an invisible, expandable spacer that absorbs any extra space. By placing this stretch after your widgets, you push them up to the top of the layout while the stretch fills the remaining space below.

Here's the change — just one line:

python
layout1 = QVBoxLayout()
layout1.addWidget(title)
layout1.addWidget(loader)
layout1.addStretch()  # Pushes widgets to the top
tab1.setLayout(layout1)

The stretch acts like an invisible spring at the bottom of the layout. It expands to take up all the leftover vertical space, which keeps your actual widgets compact and positioned at the top of the tab.

Complete working example

Here's the full example with addStretch() applied to the first tab:

python
import sys

from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QLineEdit,
    QTabWidget,
    QVBoxLayout,
    QWidget,
)


class MainWindow(QTabWidget):
    def __init__(self):
        super().__init__()

        # First tab — only two widgets
        title = QLabel("<b>Load model file</b>")
        loader = QLineEdit()
        loader.setPlaceholderText("Enter file path...")

        tab1 = QWidget()
        layout1 = QVBoxLayout()
        layout1.addWidget(title)
        layout1.addWidget(loader)
        layout1.addStretch()  # Absorbs extra space below the widgets
        tab1.setLayout(layout1)
        self.addTab(tab1, "Load model")

        # Second tab — many widgets fill the space naturally
        tab2 = QWidget()
        layout2 = QVBoxLayout()
        layout2.addWidget(QLabel("<b>Dynamic simulation</b>"))
        layout2.addWidget(
            QLabel(
                "Use time-domain simulation to predict model evolution.\n"
                "Start with defining your 2nd-order dynamics simulator here."
            )
        )
        for i in range(6):
            layout2.addWidget(QLineEdit(f"Parameter {i + 1}"))
        tab2.setLayout(layout2)
        self.addTab(tab2, "Run simulations")


app = QApplication(sys.argv)
window = MainWindow()
window.setMinimumSize(500, 400)
window.show()
app.exec()

Run this and switch between the two tabs. On the first tab, the label and text input sit snugly at the top, just as they would if there were more widgets below them. On the second tab, the widgets fill the space as before.

Over 15,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Get the book

Downloadable ebook (PDF, ePub) & Complete Source code

Placing the stretch in different positions

You can place the stretch anywhere in the layout, and the result changes accordingly:

  • Stretch at the bottom (addStretch() after all widgets) — pushes widgets to the top.
  • Stretch at the top (addStretch() before all widgets) — pushes widgets to the bottom.
  • Stretch on both sides (one before and one after the widgets) — centers the widgets vertically.

For example, to center your widgets:

python
layout1 = QVBoxLayout()
layout1.addStretch()       # Spring above
layout1.addWidget(title)
layout1.addWidget(loader)
layout1.addStretch()       # Spring below
tab1.setLayout(layout1)

Both stretches share the extra space equally, keeping the widgets centered in the tab.

Using addStretch() with a stretch factor

The addStretch() method accepts an optional stretch factor argument. This controls how much space the stretch takes relative to other stretches in the same layout. By default the stretch factor is 0, which means the stretch takes only the space that no other widget or stretch needs.

If you have two stretches and want one to take more space than the other, you can assign different factors:

python
layout1 = QVBoxLayout()
layout1.addStretch(1)      # Takes 1 part of available space
layout1.addWidget(title)
layout1.addWidget(loader)
layout1.addStretch(3)      # Takes 3 parts of available space
tab1.setLayout(layout1)

In this case, the widgets would sit closer to the top, because the bottom stretch absorbs three times more space than the top one.

Summary

When a tab in a QTabWidget has fewer widgets than other tabs, the layout will spread those widgets across the full available space. Adding layout.addStretch() after your widgets inserts an expandable spacer that pushes the widgets to the top, giving your tab a clean, consistent look. You can place stretches before, after, or around your widgets to control vertical positioning — top-aligned, bottom-aligned, or centered.

This technique works the same way in QHBoxLayout for horizontal positioning. Wherever you have extra space in a box layout that you want to control, addStretch() is the tool to reach for. For a deeper dive into how box layouts and other layout managers work in PyQt6, see the PyQt6 layouts tutorial. If you're looking to build more complex interfaces with widgets like QLabel and QLineEdit, the PyQt6 widgets guide covers the essentials. You can also use Qt Designer to visually arrange your GUI layouts including tab widgets, which makes experimenting with stretches and spacers much easier.

Bring Your PyQt/PySide Application to Market — Specialized launch support for scientific and engineering software built using Python & Qt.

Find out More

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak
Martin Fitzpatrick

Widget size and placement in the tabs of a QTabWidget was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.