aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2024-03-26 11:36:27 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2024-04-12 14:08:03 +0200
commit7c3beafa968280a66a7c554d9e979e17e903cb37 (patch)
tree7595d12a9b54e5fac60a54faad43debfea006318 /sources/pyside6
parent152ec44d1c0ac3da91bc6018f8c039e599f60df3 (diff)
Release dialogs when using QDialog.exec()
In the typical pattern ported from C++ dialog = QDialog(mainWin) dialog.exec() one would expected the dialog to be deleted. However, due to the constructor heuristics creating a parent-child relationship, the dialog leaks. Add a modification to remove the relationship in case exec() is called. Change-Id: Ibc6f8c150cbd80a4085af4a5b713ee7c2c62abf3 Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io>
Diffstat (limited to 'sources/pyside6')
-rw-r--r--sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml4
-rw-r--r--sources/pyside6/PySide6/glue/qtwidgets.cpp8
-rw-r--r--sources/pyside6/tests/QtWidgets/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/QtWidgets/qdialog_test.py52
4 files changed, 64 insertions, 1 deletions
diff --git a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
index 6b4ce7bbe..0669c18f6 100644
--- a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
+++ b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
@@ -491,7 +491,9 @@
<object-type name="QDateEdit"/>
<object-type name="QDialog">
<enum-type name="DialogCode" python-type="IntEnum"/>
- <modify-function signature="exec()" allow-thread="yes"/>
+ <modify-function signature="exec()" allow-thread="yes">
+ <inject-code file="../glue/qtwidgets.cpp" snippet="qdialog-exec-remove-parent-relation"/>
+ </modify-function>
<add-function signature="exec_()" return-type="int">
<inject-code file="../glue/qtwidgets.cpp" snippet="qapplication-exec"/>
</add-function>
diff --git a/sources/pyside6/PySide6/glue/qtwidgets.cpp b/sources/pyside6/PySide6/glue/qtwidgets.cpp
index 18cce00bf..1b3e94016 100644
--- a/sources/pyside6/PySide6/glue/qtwidgets.cpp
+++ b/sources/pyside6/PySide6/glue/qtwidgets.cpp
@@ -803,6 +803,14 @@ const QByteArray signature = QByteArrayLiteral("2") + method.methodSignature();
%END_ALLOW_THREADS
// @snippet qwizardpage-registerfield
+// The constructor heuristics generate setting a parent-child relationship
+// when creating a QDialog with parent. This causes the dialog to leak
+// when it synchronous exec() is used instead of asynchronous show().
+// In that case, remove the parent-child relationship.
+// @snippet qdialog-exec-remove-parent-relation
+Shiboken::Object::removeParent(reinterpret_cast<SbkObject *>(%PYSELF));
+// @snippet qdialog-exec-remove-parent-relation
+
/*********************************************************************
* CONVERSIONS
********************************************************************/
diff --git a/sources/pyside6/tests/QtWidgets/CMakeLists.txt b/sources/pyside6/tests/QtWidgets/CMakeLists.txt
index 54e0d6c3a..01b7d08ea 100644
--- a/sources/pyside6/tests/QtWidgets/CMakeLists.txt
+++ b/sources/pyside6/tests/QtWidgets/CMakeLists.txt
@@ -84,6 +84,7 @@ PYSIDE_TEST(qapp_issue_585.py)
PYSIDE_TEST(qapp_test.py)
PYSIDE_TEST(qapplication_test.py)
PYSIDE_TEST(qapplication_exit_segfault_test.py)
+PYSIDE_TEST(qdialog_test.py)
PYSIDE_TEST(qdynamic_signal.py)
# TODO: This passes, but requires manual button clicking (at least on mac)
#PYSIDE_TEST(qfontdialog_test.py)
diff --git a/sources/pyside6/tests/QtWidgets/qdialog_test.py b/sources/pyside6/tests/QtWidgets/qdialog_test.py
new file mode 100644
index 000000000..cb85ce7e2
--- /dev/null
+++ b/sources/pyside6/tests/QtWidgets/qdialog_test.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+import weakref
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtCore import Slot, QTimer
+from PySide6.QtWidgets import QDialog, QMainWindow
+from helper.timedqapplication import TimedQApplication
+
+
+class Window(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Main")
+ self.dialog = None
+
+ @Slot()
+ def execDialog(self):
+ dialog = QDialog(self)
+ self.dialog = weakref.ref(dialog)
+ dialog.setWindowTitle("Dialog")
+ dialog.setMinimumWidth(200)
+ QTimer.singleShot(500, dialog.reject)
+ dialog.exec()
+ self.close()
+
+
+class DialogExecTest(TimedQApplication):
+ """Test whether the parent-child relationship (dialog/main window) is removed when
+ using QDialog.exec() (instead show()), preventing the dialog from leaking."""
+
+ def setUp(self):
+ super().setUp(10000)
+ self._window = Window()
+
+ def testExec(self):
+ self._window.show()
+ QTimer.singleShot(500, self._window.execDialog)
+ self.app.exec()
+ self.assertTrue(self._window.dialog() is None)
+
+
+if __name__ == '__main__':
+ unittest.main()