Why Widgets Appear as Separate Windows

Understanding widget parenting in Qt and how to fix widgets that float outside your main window
Heads up! You've already completed this tutorial.

If you're dynamically adding widgets to your PyQt6 application and finding that they pop out as separate floating windows instead of appearing neatly inside your application, you're running into one of Qt's most common gotchas: widget parenting.

This problem typically shows up when widgets are added from a callback, event listener, or signal handler — but work fine when added directly in your setup code. Let's look at why this happens and how to fix it.

How Qt decides what's a window

In Qt, every widget can optionally have a parent widget. The parent determines where a widget lives visually — a widget with a parent is drawn inside that parent. A widget without a parent becomes a top-level window, floating independently on your desktop.

This is the root cause of widgets appearing outside your main window. When you create a widget and it doesn't have a parent — either because you didn't set one, or because the parent was lost somehow — Qt treats it as a standalone window.

Three things that cause parentless widgets

Here are the most common reasons widgets end up floating:

Creating widgets without a parent

python
# This widget has no parent — it will be a floating window
tabs = QTabWidget()

# This widget has a parent — it will appear inside parent_widget
tabs = QTabWidget(parent_widget)

When you add a widget to a layout, the layout assigns the parent automatically. But if something goes wrong between creation and layout insertion (like an exception, or the widget being shown prematurely), the widget stays parentless.

The safest approach is to pass a parent when creating widgets:

python
def create_new_tab(self):
    wdg = QWidget()
    layout = QGridLayout(wdg)

    tabs = QTabWidget(wdg)  # Explicitly set parent
    tab1 = QWidget(tabs)     # Explicitly set parent
    tab2 = QWidget(tabs)     # Explicitly set parent
    tabs.addTab(tab1, "Start")
    tabs.addTab(tab2, "Profile")
    layout.addWidget(tabs)

    return wdg

Accidentally recreating a widget

If you have a tab widget stored as self.w and somewhere in your code you do:

python
self.w = QTabWidget()

...the original tab widget is replaced. If the old widget gets garbage collected, all the tabs that had it as their parent suddenly become orphans — parentless widgets that float as independent windows.

Be careful not to reassign widget attributes unintentionally, especially in callbacks that might run multiple times.

Losing the parent reference

If you explicitly set a widget's parent to None, it becomes a top-level window:

python
widget.setParent(None)  # This widget is now a floating window

This sometimes happens indirectly. For example, removing a widget from a layout in certain ways can clear its parent.

A clean approach to dynamic tabs

Here's a complete, working example that dynamically adds tabs without any floating-window issues. It demonstrates the correct way to set up a QTabWidget with a "+" button that adds new tabs:

python
import sys
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QTabWidget,
    QWidget, QVBoxLayout, QLabel
)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Dynamic Tabs")
        self.setFixedSize(600, 400)

        self.tabs = QTabWidget(self)
        self.tabs.currentChanged.connect(self.on_tab_changed)

        # Add an initial tab
        self.add_content_tab("Tab 1")

        # Add the "+" tab for creating new tabs
        self.tabs.addTab(QWidget(self.tabs), "+")

        self.setCentralWidget(self.tabs)

    def on_tab_changed(self, index):
        # Check if the "+" tab was clicked
        if self.tabs.tabText(index) == "+":
            self.add_new_tab()

    def add_new_tab(self):
        # Count existing content tabs (exclude the "+" tab)
        tab_count = self.tabs.count()  # includes "+"
        new_title = f"Tab {tab_count}"

        # Insert the new tab before the "+" tab
        new_tab = self.create_tab_content(new_title)
        insert_index = self.tabs.count() - 1
        self.tabs.insertTab(insert_index, new_tab, new_title)

        # Switch to the newly created tab (avoid retriggering)
        self.tabs.blockSignals(True)
        self.tabs.setCurrentIndex(insert_index)
        self.tabs.blockSignals(False)

    def add_content_tab(self, title):
        """Add a content tab before the + tab."""
        tab = self.create_tab_content(title)
        # Insert before the last tab if "+" exists, otherwise just add
        plus_index = None
        for i in range(self.tabs.count()):
            if self.tabs.tabText(i) == "+":
                plus_index = i
                break

        if plus_index is not None:
            self.tabs.insertTab(plus_index, tab, title)
        else:
            self.tabs.addTab(tab, title)

    def create_tab_content(self, title):
        """Create the widget content for a tab."""
        widget = QWidget(self.tabs)  # Parent is the tab widget
        layout = QVBoxLayout(widget)
        label = QLabel(f"Content for {title}", widget)
        layout.addWidget(label)
        return widget


app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

A few things to notice in this example:

  • The main window inherits from QMainWindow, and QApplication is created separately.
  • Every widget is created with an explicit parent: QWidget(self.tabs), QLabel(text, widget), etc.
  • blockSignals(True) is used when programmatically changing the current tab to prevent the currentChanged signal from firing recursively.
  • New tabs are inserted before the "+" tab using insertTab, so the "+" always stays at the end.

Summary

Widget parenting is one of those things in Qt that works invisibly when everything is correct — and causes confusing visual glitches the moment something is slightly off. The good news is that once you understand the pattern, the fix is almost always the same: make sure every widget has the right parent.

If you're new to PyQt6, our guide to creating your first window covers the basics of setting up a QMainWindow, while the widgets tutorial walks through the most common widgets and how to use them correctly.

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

Create GUI Applications with Python & Qt6 by Martin Fitzpatrick

(PyQt6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!

More info Get the book

Martin Fitzpatrick

Why Widgets Appear as Separate Windows 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.