<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - qmdiarea</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/qmdiarea.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2021-05-27T09:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>How is QMdiArea supposed to be used? — Working with QMdiArea and QMdiSubWindow in PyQt6, including how to handle subwindow closing and visibility</title><link href="https://www.pythonguis.com/faq/how-is-qmdiarea-supposed-to-be-used/" rel="alternate"/><published>2021-05-27T09:00:00+00:00</published><updated>2021-05-27T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2021-05-27:/faq/how-is-qmdiarea-supposed-to-be-used/</id><summary type="html">&lt;code&gt;QMdiArea&lt;/code&gt; provides a Multiple Document Interface &amp;mdash; a workspace area inside your main window where you can open, tile, and cascade multiple child windows. Each child window is a &lt;code&gt;QMdiSubWindow&lt;/code&gt;. It's a useful pattern when your application needs to display several independent panels or documents side by side (think of an IDE with multiple open files, or a trading terminal with several chart windows).</summary><content type="html">
            &lt;p&gt;&lt;code&gt;QMdiArea&lt;/code&gt; provides a Multiple Document Interface &amp;mdash; a workspace area inside your main window where you can open, tile, and cascade multiple child windows. Each child window is a &lt;code&gt;QMdiSubWindow&lt;/code&gt;. It's a useful pattern when your application needs to display several independent panels or documents side by side (think of an IDE with multiple open files, or a trading terminal with several chart windows).&lt;/p&gt;
&lt;p&gt;However, &lt;code&gt;QMdiArea&lt;/code&gt; has a few quirks that can trip you up, especially when you're designing your UI in Qt Designer. A common frustration: you close a subwindow, and it gets &lt;em&gt;deleted&lt;/em&gt;, so you can't reopen it. Let's walk through how &lt;code&gt;QMdiArea&lt;/code&gt; works, why this happens, and how to set things up so your subwindows behave the way you'd expect.&lt;/p&gt;
&lt;h2 id="the-problem-with-closing-subwindows"&gt;The problem with closing subwindows&lt;/h2&gt;
&lt;p&gt;When you add a widget to a &lt;code&gt;QMdiArea&lt;/code&gt; using &lt;code&gt;addSubWindow()&lt;/code&gt;, the MDI area wraps it in a &lt;code&gt;QMdiSubWindow&lt;/code&gt; container. That container has a close button by default. When the user clicks that close button, the default behavior is to &lt;strong&gt;close and delete&lt;/strong&gt; the child widget.&lt;/p&gt;
&lt;p&gt;This means if you try to show it again later &amp;mdash; for example, by calling &lt;code&gt;.show()&lt;/code&gt; &amp;mdash; the underlying widget is gone. Your reference to it now points to a deleted C++ object, which usually results in a crash or simply nothing happening.&lt;/p&gt;
&lt;p&gt;This is the root cause of the behavior described in the question: subwindows designed in Qt Designer get instantiated as plain &lt;code&gt;QWidget&lt;/code&gt; instances, wrapped automatically by &lt;code&gt;addSubWindow()&lt;/code&gt;, and then destroyed when closed.&lt;/p&gt;
&lt;h2 id="preventing-deletion-on-close"&gt;Preventing deletion on close&lt;/h2&gt;
&lt;p&gt;The fix is straightforward. You need to set the &lt;code&gt;Qt.WA_DeleteOnClose&lt;/code&gt; widget attribute to &lt;code&gt;False&lt;/code&gt; on the subwindow. This tells Qt: "When this window is closed, just hide it &amp;mdash; don't destroy it."&lt;/p&gt;
&lt;p&gt;Here's a minimal example that demonstrates the problem and the fix:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QMdiArea,
    QMdiSubWindow, QTextEdit, QAction
)
from PyQt6.QtCore import Qt


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QMdiArea Example")
        self.resize(800, 600)

        # Create the MDI area and set it as the central widget.
        self.mdi_area = QMdiArea()
        self.setCentralWidget(self.mdi_area)

        # Create a subwindow with a text editor inside.
        self.editor = QTextEdit()
        self.editor.setPlainText("Hello from the subwindow!")
        self.sub_window = self.mdi_area.addSubWindow(self.editor)
        self.sub_window.setWindowTitle("My Editor")

        # This is the fix &amp;mdash; prevent deletion on close.
        self.sub_window.setAttribute(Qt.WA_DeleteOnClose, False)

        # Hide the subwindow initially.
        self.sub_window.hide()

        # Add a menu action to toggle the subwindow.
        menu = self.menuBar().addMenu("&amp;amp;Windows")
        show_action = QAction("Show Editor", self)
        show_action.triggered.connect(self.toggle_editor)
        menu.addAction(show_action)

    def toggle_editor(self):
        if self.sub_window.isVisible():
            self.sub_window.hide()
        else:
            self.sub_window.show()


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this, and you'll find you can close the subwindow with its close button, then reopen it from the &lt;strong&gt;Windows&lt;/strong&gt; menu as many times as you like. Without the &lt;code&gt;setAttribute(Qt.WA_DeleteOnClose, False)&lt;/code&gt; line, the subwindow would be destroyed the first time you closed it.&lt;/p&gt;
&lt;h2 id="the-qt-designer-wrinkle"&gt;The Qt Designer wrinkle&lt;/h2&gt;
&lt;p&gt;When you design a &lt;code&gt;QMdiArea&lt;/code&gt; in &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-first-steps-qt-designer/"&gt;Qt Designer&lt;/a&gt; and add child widgets inside it, the generated UI code creates those children as plain &lt;code&gt;QWidget&lt;/code&gt; instances &amp;mdash; not as &lt;code&gt;QMdiSubWindow&lt;/code&gt; instances. Qt Designer doesn't have full support for &lt;code&gt;QMdiSubWindow&lt;/code&gt; as a designable container.&lt;/p&gt;
&lt;p&gt;This means two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The widgets aren't properly registered as subwindows.&lt;/strong&gt; Calling &lt;code&gt;self.mdi_area.subWindowList()&lt;/code&gt; may return an empty list because the widgets were placed as children in the designer but not added through &lt;code&gt;addSubWindow()&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When you do call &lt;code&gt;addSubWindow()&lt;/code&gt; yourself&lt;/strong&gt;, the widget gets wrapped in a &lt;code&gt;QMdiSubWindow&lt;/code&gt; &amp;mdash; but that wrapper has &lt;code&gt;WA_DeleteOnClose&lt;/code&gt; set to &lt;code&gt;True&lt;/code&gt; by default. Closing it destroys your widget.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The practical solution is to &lt;strong&gt;not rely on Qt Designer for the subwindow arrangement inside the MDI area&lt;/strong&gt;. Instead, design each subwindow's &lt;em&gt;content&lt;/em&gt; as a separate widget (or even a separate &lt;code&gt;.ui&lt;/code&gt; file), and then add them to the MDI area programmatically in your &lt;code&gt;__init__&lt;/code&gt; method.&lt;/p&gt;
&lt;h2 id="adding-sub-windows-in-code"&gt;Adding sub-windows in code&lt;/h2&gt;
&lt;p&gt;Here's a pattern that works well. You design your subwindow content as standalone &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-own-custom-widgets/"&gt;custom widgets&lt;/a&gt; (either in code or in separate &lt;code&gt;.ui&lt;/code&gt; files), and then wire everything up in your main window class:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QMdiArea,
    QMdiSubWindow, QTextEdit, QLabel,
    QVBoxLayout, QWidget, QAction
)
from PyQt6.QtCore import Qt


class InfoPanel(QWidget):
    """A custom widget that will be placed inside a subwindow."""
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        layout.addWidget(QLabel("This is an info panel."))
        layout.addWidget(QLabel("It shows some read-only data."))
        self.setLayout(layout)


class EditorPanel(QWidget):
    """Another custom widget for a different subwindow."""
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        layout.addWidget(QLabel("Editor:"))
        self.editor = QTextEdit()
        layout.addWidget(self.editor)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QMdiArea &amp;mdash; Multiple Subwindows")
        self.resize(900, 600)

        self.mdi_area = QMdiArea()
        self.setCentralWidget(self.mdi_area)

        # Create subwindows programmatically.
        self.info_subwindow = self._create_subwindow(
            InfoPanel(), "Info Panel"
        )
        self.editor_subwindow = self._create_subwindow(
            EditorPanel(), "Editor"
        )

        # Hide all subwindows initially.
        self.info_subwindow.hide()
        self.editor_subwindow.hide()

        # Build a menu to show/hide them.
        menu = self.menuBar().addMenu("&amp;amp;Windows")

        info_action = QAction("Info Panel", self)
        info_action.triggered.connect(
            lambda: self._toggle_subwindow(self.info_subwindow)
        )
        menu.addAction(info_action)

        editor_action = QAction("Editor", self)
        editor_action.triggered.connect(
            lambda: self._toggle_subwindow(self.editor_subwindow)
        )
        menu.addAction(editor_action)

    def _create_subwindow(self, widget, title):
        """Add a widget to the MDI area as a persistent subwindow."""
        sub_window = self.mdi_area.addSubWindow(widget)
        sub_window.setWindowTitle(title)
        sub_window.setAttribute(Qt.WA_DeleteOnClose, False)
        return sub_window

    def _toggle_subwindow(self, sub_window):
        """Show or hide a subwindow."""
        if sub_window.isVisible():
            sub_window.hide()
        else:
            sub_window.show()


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This gives you full control. Each subwindow is a proper &lt;code&gt;QMdiSubWindow&lt;/code&gt;, registered with the MDI area, and safe to close and reopen.&lt;/p&gt;
&lt;h2 id="suppressing-the-automatic-show-on-addsubwindow"&gt;Suppressing the automatic show on addSubWindow&lt;/h2&gt;
&lt;p&gt;One more gotcha: calling &lt;code&gt;addSubWindow()&lt;/code&gt; can trigger the subwindow to become visible immediately. If you want all your subwindows hidden at startup, just call &lt;code&gt;.hide()&lt;/code&gt; on each one right after adding it &amp;mdash; as shown in the example above. The order matters: add first, then hide.&lt;/p&gt;
&lt;p&gt;Alternatively, you can call &lt;code&gt;addSubWindow()&lt;/code&gt; before the main window itself is shown. Since the parent isn't visible yet, the subwindows won't flash on screen.&lt;/p&gt;
&lt;h2 id="using-ui-files-for-subwindow-content"&gt;Using .ui files for subwindow content&lt;/h2&gt;
&lt;p&gt;If you prefer to design your subwindow contents in Qt Designer, you absolutely can. Just design each panel as its own widget in a separate &lt;code&gt;.ui&lt;/code&gt; file, load it using &lt;code&gt;uic.loadUi()&lt;/code&gt; or compile it with &lt;code&gt;pyuic5&lt;/code&gt;, and then pass the resulting widget to &lt;code&gt;addSubWindow()&lt;/code&gt;. For a full walkthrough on designing widgets and dialogs with Qt Designer, see the &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-qt-designer-gui-layout/"&gt;Qt Designer GUI layout tutorial&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import uic


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("MDI with .ui files")
        self.resize(900, 600)

        self.mdi_area = QMdiArea()
        self.setCentralWidget(self.mdi_area)

        # Load a widget designed in Qt Designer.
        info_widget = uic.loadUi("info_panel.ui")
        self.info_subwindow = self.mdi_area.addSubWindow(info_widget)
        self.info_subwindow.setWindowTitle("Info Panel")
        self.info_subwindow.setAttribute(Qt.WA_DeleteOnClose, False)
        self.info_subwindow.hide()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This gives you the best of both worlds: visual design in Qt Designer for the content, and reliable programmatic control over the MDI subwindow lifecycle.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;QMdiArea&lt;/code&gt; is a powerful component once you understand its expectations. The main thing is to take ownership of the subwindow lifecycle in your code rather than relying on Qt Designer to set it up for you. If you're new to building PyQt6 applications, you might find it helpful to start with &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-first-window/"&gt;creating your first window&lt;/a&gt; to get comfortable with the fundamentals before working with more advanced components like &lt;code&gt;QMdiArea&lt;/code&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt6"/><category term="qmdiarea"/><category term="qt-designer"/><category term="python"/><category term="qt"/><category term="qt6"/><category term="pyqt6-qt-designer"/></entry></feed>