diff options
| -rw-r--r-- | sources/pyside2/PySide2/glue/qtcore.cpp | 4 | ||||
| -rw-r--r-- | sources/pyside2/libpyside/pyside.cpp | 20 | ||||
| -rw-r--r-- | sources/pyside2/libpyside/pyside.h | 1 | ||||
| -rw-r--r-- | sources/pyside2/tests/QtWidgets/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | sources/pyside2/tests/QtWidgets/bug_750.py | 2 | ||||
| -rw-r--r-- | sources/pyside2/tests/QtWidgets/qapplication_singleton_test.py | 39 | ||||
| -rw-r--r-- | sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py | 15 | ||||
| -rw-r--r-- | sources/shiboken2/generator/shiboken2/cppgenerator.cpp | 4 | ||||
| -rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.cpp | 4 | ||||
| -rw-r--r-- | sources/shiboken2/libshiboken/qapp_macro.cpp | 198 | ||||
| -rw-r--r-- | sources/shiboken2/libshiboken/qapp_macro.h | 3 |
11 files changed, 55 insertions, 236 deletions
diff --git a/sources/pyside2/PySide2/glue/qtcore.cpp b/sources/pyside2/PySide2/glue/qtcore.cpp index d99a150ad..e54fa1846 100644 --- a/sources/pyside2/PySide2/glue/qtcore.cpp +++ b/sources/pyside2/PySide2/glue/qtcore.cpp @@ -1430,8 +1430,8 @@ if (qApp) { // this will keep app live after python exit (extra ref) } // PYSIDE-571: make sure that we return the singleton "None" -if (pyApp == Py_None) - Py_DECREF(MakeSingletonQAppWrapper(nullptr)); // here qApp and instance() diverge +if (Py_TYPE(pyApp) == Py_TYPE(Py_None)) + Py_DECREF(MakeQAppWrapper(nullptr)); %PYARG_0 = pyApp; Py_XINCREF(%PYARG_0); // @snippet qcoreapplication-instance diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp index eeb4de037..d22958398 100644 --- a/sources/pyside2/libpyside/pyside.cpp +++ b/sources/pyside2/libpyside/pyside.cpp @@ -94,6 +94,7 @@ void init(PyObject *module) MetaFunction::init(module); // Init signal manager, so it will register some meta types used by QVariant. SignalManager::instance(); + initQApp(); } static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *accept) @@ -214,7 +215,7 @@ void destroyQCoreApplication() delete app; Py_END_ALLOW_THREADS // PYSIDE-571: make sure to create a singleton deleted qApp. - MakeSingletonQAppWrapper(NULL); + Py_DECREF(MakeQAppWrapper(nullptr)); } std::size_t getSizeOfQObject(SbkObjectType *type) @@ -299,6 +300,23 @@ void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds initDynamicMetaObject(type, userData->mo.update(), userData->cppObjSize); } +void initQApp() +{ + /* + * qApp will not be initialized when embedding is active. + * That means that qApp exists already when PySide is initialized. + * We could solve that by creating a qApp variable, but in embedded + * mode, we also have the effect that the first assignment to qApp + * is persistent! Therefore, we can never be sure to have created + * qApp late enough to get the right type for the instance. + * + * I would appreciate very much if someone could explain or even fix + * this issue. It exists only when a pre-existing application exists. + */ + if (!qApp) + Py_DECREF(MakeQAppWrapper(nullptr)); +} + PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name) { PyObject *attr = PyObject_GenericGetAttr(self, name); diff --git a/sources/pyside2/libpyside/pyside.h b/sources/pyside2/libpyside/pyside.h index 1529d79c9..ae400e1fe 100644 --- a/sources/pyside2/libpyside/pyside.h +++ b/sources/pyside2/libpyside/pyside.h @@ -103,6 +103,7 @@ PYSIDE_DEPRECATED(PYSIDE_API void initDynamicMetaObject(SbkObjectType *type, con PYSIDE_API void initDynamicMetaObject(SbkObjectType *type, const QMetaObject *base, std::size_t cppObjSize); PYSIDE_API void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject *kwds); +PYSIDE_API void initQApp(); /// Return the size in bytes of a type that inherits QObject. PYSIDE_API std::size_t getSizeOfQObject(SbkObjectType *type); diff --git a/sources/pyside2/tests/QtWidgets/CMakeLists.txt b/sources/pyside2/tests/QtWidgets/CMakeLists.txt index 6d8645918..6682136ea 100644 --- a/sources/pyside2/tests/QtWidgets/CMakeLists.txt +++ b/sources/pyside2/tests/QtWidgets/CMakeLists.txt @@ -87,7 +87,6 @@ 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(qapplication_singleton_test.py) PYSIDE_TEST(qbrush_test.py) PYSIDE_TEST(qdynamic_signal.py) # TODO: This passes, but requires manual button clicking (at least on mac) diff --git a/sources/pyside2/tests/QtWidgets/bug_750.py b/sources/pyside2/tests/QtWidgets/bug_750.py index 42b6c9843..78d333efd 100644 --- a/sources/pyside2/tests/QtWidgets/bug_750.py +++ b/sources/pyside2/tests/QtWidgets/bug_750.py @@ -36,7 +36,7 @@ from helper.usesqapplication import UsesQApplication from PySide2.QtCore import QTimer from PySide2.QtGui import QPainter, QFont, QFontInfo -from PySide2.QtWidgets import QWidget, qApp +from PySide2.QtWidgets import QWidget class MyWidget(QWidget): def paintEvent(self, e): diff --git a/sources/pyside2/tests/QtWidgets/qapplication_singleton_test.py b/sources/pyside2/tests/QtWidgets/qapplication_singleton_test.py deleted file mode 100644 index e79e303f0..000000000 --- a/sources/pyside2/tests/QtWidgets/qapplication_singleton_test.py +++ /dev/null @@ -1,39 +0,0 @@ -############################################################################# -## -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the test suite of Qt for Python. -## -## $QT_BEGIN_LICENSE:GPL-EXCEPT$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 3 as published by the Free Software -## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -import unittest - -from PySide2.QtWidgets import QApplication - -class TestSingleton(unittest.TestCase): - def testBasic(self): - a = QApplication([]) - self.assertRaises(RuntimeError, QApplication, []) - -if __name__ == '__main__': - unittest.main() diff --git a/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py index 43f455ea9..2cc18c9c9 100644 --- a/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py +++ b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py @@ -32,6 +32,8 @@ import PySide2 # This test tests the new "macro" feature of qApp. # It also uses the qApp variable to finish the instance and start over. +# Note: this test makes qapplication_singleton_test.py obsolete. + class qAppMacroTest(unittest.TestCase): _test_1093_is_first = True @@ -44,13 +46,8 @@ class qAppMacroTest(unittest.TestCase): QtWidgets = QtGui = QtCore # qApp is in the builtins self.assertEqual(bool(qApp), False) - # and also in certain PySide modules - QtCore.qApp, QtGui.qApp, QtWidgets.qApp - # and they are all the same - self.assertTrue(qApp is QtCore.qApp is QtGui.qApp is QtWidgets.qApp) - # and the type is NoneType, but it is not None (cannot work) - self.assertTrue(type(qApp) is type(None)) - self.assertTrue(qApp is not None) + # and the type is None + self.assertTrue(qApp is None) # now we create an application for all cases classes = (QtCore.QCoreApplication, QtGui.QGuiApplication, @@ -63,9 +60,7 @@ class qAppMacroTest(unittest.TestCase): QtCore.QCoreApplication([]) with self.assertRaises(RuntimeError): QtCore.QCoreApplication([]) - self.assertEqual(QtCore.QCoreApplication.instance(), QtCore.qApp) - # and they are again all the same - self.assertTrue(qApp is QtCore.qApp is QtGui.qApp is QtWidgets.qApp) + self.assertEqual(QtCore.QCoreApplication.instance(), qApp) def test_1093(self): # Test that without creating a QApplication staticMetaObject still exists. diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index f9020420f..5697a24e0 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -5818,10 +5818,6 @@ bool CppGenerator::finishGeneration() s << INDENT << "FinishSignatureInitialization(module, " << moduleName() << "_SignatureStrings);\n"; - if (usePySideExtensions()) { - // initialize the qApp module. - s << INDENT << "NotifyModuleForQApp(module, qApp);\n"; - } s << Qt::endl; s << "SBK_MODULE_INIT_FUNCTION_END\n"; diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 71efe12c0..29ee1b185 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -426,7 +426,7 @@ void SbkDeallocQAppWrapper(PyObject *pyObj) { SbkDeallocWrapper(pyObj); // PYSIDE-571: make sure to create a singleton deleted qApp. - MakeSingletonQAppWrapper(nullptr); + Py_DECREF(MakeQAppWrapper(nullptr)); } void SbkDeallocWrapperWithPrivateDtor(PyObject *self) @@ -612,7 +612,7 @@ PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) subtype->tp_free = PyObject_Del; } #endif - auto self = reinterpret_cast<SbkObject *>(MakeSingletonQAppWrapper(subtype)); + auto self = reinterpret_cast<SbkObject *>(MakeQAppWrapper(subtype)); return self == nullptr ? nullptr : _setupNew(self, subtype); } diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp index 979638d17..3ef3a51c6 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.cpp +++ b/sources/shiboken2/libshiboken/qapp_macro.cpp @@ -51,200 +51,50 @@ extern "C" // // qApp is a macro in Qt5. In Python, we simulate that a little by a // variable that monitors Q*Application.instance(). -// This variable is also able to destroy the app by deleting qApp. +// This variable is also able to destroy the app by qApp.shutdown(). // -static const char *mod_names[3] = {"PySide2.QtCore", "PySide2.QtGui", "PySide2.QtWidgets"}; - -static int -qApp_module_index(PyObject *module) -{ - const char *name = PyModule_GetName(module); - for (int idx = 0; idx < 3; idx++) - if (strcmp(name, mod_names[idx]) == 0) - return idx + 1; - return 0; -} - -#define PYTHON_IS_PYTHON3 (PY_VERSION_HEX >= 0x03000000) -#define PYTHON_IS_PYTHON2 (!PYTHON_IS_PYTHON3) -#define Py_NONE_TYPE Py_TYPE(Py_None) - -#if PYTHON_IS_PYTHON3 -# define BRACE_OPEN { -# define BRACE_CLOSE } -#else -# define BRACE_OPEN -# define BRACE_CLOSE -#endif - -static SbkObject _Py_ChameleonQAppWrapper_Struct = { - BRACE_OPEN - _PyObject_EXTRA_INIT - 1, Py_NONE_TYPE - BRACE_CLOSE -}; static PyObject *qApp_var = nullptr; -static PyObject *qApp_content = reinterpret_cast<PyObject *>(&_Py_ChameleonQAppWrapper_Struct); -static PyObject *qApp_moduledicts[5] = {nullptr, nullptr, nullptr, nullptr, nullptr}; +static PyObject *qApp_content = nullptr; -static int -reset_qApp_var(void) +static PyObject * +monitor_qApp_var(PyObject *qApp) { - PyObject **mod_ptr; + static bool init_done; + static PyObject *builtins = PyEval_GetBuiltins(); - for (mod_ptr = qApp_moduledicts; *mod_ptr != nullptr; mod_ptr++) { - // We respect whatever the user may have set. - PyObject *existing = PyDict_GetItem(*mod_ptr, qApp_var); - if (existing == nullptr || Py_TYPE(existing) == Py_NONE_TYPE) { - if (PyDict_SetItem(*mod_ptr, qApp_var, qApp_content) < 0) - return -1; - } + if (!init_done) { + qApp_var = Py_BuildValue("s", "qApp"); + if (qApp_var == nullptr) + return nullptr; + // This is a borrowed reference + Py_INCREF(builtins); + init_done = true; } - return 0; -} -static bool app_created = false; + if (PyDict_SetItem(builtins, qApp_var, qApp) < 0) + return nullptr; + qApp_content = qApp; + Py_INCREF(qApp); + return qApp; +} PyObject * -MakeSingletonQAppWrapper(PyTypeObject *type) +MakeQAppWrapper(PyTypeObject *type) { if (type == nullptr) - type = Py_NONE_TYPE; - if (!(type == Py_NONE_TYPE || Py_TYPE(qApp_content) == Py_NONE_TYPE)) { + type = Py_TYPE(Py_None); + if (!(type == Py_TYPE(Py_None) || Py_TYPE(qApp_content) == Py_TYPE(Py_None))) { const char *res_name = PepType_GetNameStr(Py_TYPE(qApp_content)); const char *type_name = PepType_GetNameStr(type); PyErr_Format(PyExc_RuntimeError, "Please destroy the %s singleton before" " creating a new %s instance.", res_name, type_name); return nullptr; } - if (reset_qApp_var() < 0) - return nullptr; - if (type == Py_NONE_TYPE) { - // PYSIDE-1093: Ignore None when no instance has ever been created. - if (!app_created) - Py_RETURN_NONE; - Py_TYPE(qApp_content) = Py_NONE_TYPE; - } else { - PyObject_Init(qApp_content, type); - app_created = true; - } - Py_INCREF(qApp_content); - return qApp_content; -} - -// PYSIDE-1158: Be clear that the QApp none has the type of None but is a -// different thing. - -static PyObject * -none_repr(PyObject *op) -{ - if (op == qApp_content) - return PyUnicode_FromString("noApp"); - return PyUnicode_FromString("None"); -} - -static void -none_dealloc(PyObject *ignore) -{ - if (ignore == qApp_content) - return; - /* This should never get called, but we also don't want to SEGV if - * we accidentally decref None out of existence. - */ - Py_FatalError("deallocating None"); + PyObject *self = type != Py_TYPE(Py_None) ? PyObject_New(PyObject, type) : Py_None; + return monitor_qApp_var(self); } -#if PYTHON_IS_PYTHON2 - -// Install support in Py_NONE_TYPE for Python 2: 'bool(qApp) == False'. -static int -none_bool(PyObject *v) -{ - return 0; -} - -static PyNumberMethods none_as_number = { - nullptr, /* nb_add */ - nullptr, /* nb_subtract */ - nullptr, /* nb_multiply */ - nullptr, /* nb_divide */ - nullptr, /* nb_remainder */ - nullptr, /* nb_divmod */ - nullptr, /* nb_power */ - nullptr, /* nb_negative */ - nullptr, /* nb_positive */ - nullptr, /* nb_absolute */ - reinterpret_cast<inquiry>(none_bool), /* nb_nonzero */ -}; - -#endif - -static int -setup_qApp_var(PyObject *module) -{ - int module_index; - static int init_done = 0; - - if (!init_done) { - Py_NONE_TYPE->tp_repr = &none_repr; - Py_NONE_TYPE->tp_dealloc = &none_dealloc; -#if PYTHON_IS_PYTHON2 - Py_NONE_TYPE->tp_as_number = &none_as_number; -#endif - qApp_var = Py_BuildValue("s", "qApp"); - if (qApp_var == nullptr) - return -1; - // This is a borrowed reference - qApp_moduledicts[0] = PyEval_GetBuiltins(); - Py_INCREF(qApp_moduledicts[0]); - init_done = 1; - } - - // Initialize qApp. We insert it into __dict__ for "import *" and also - // into __builtins__, to let it appear like a real macro. - module_index = qApp_module_index(module); - if (module_index) { - // This line gets a borrowed reference - qApp_moduledicts[module_index] = PyModule_GetDict(module); - Py_INCREF(qApp_moduledicts[module_index]); - if (reset_qApp_var() < 0) - return -1; - } - return 0; -} - -void -NotifyModuleForQApp(PyObject *module, void *qApp) -{ - /* - * PYSIDE-571: Check if an QApplication instance exists before the import. - * This happens in scriptableapplication and application_test.py . - * - * Crucial Observation - * =================== - * - * A Q*Application object from C++ does not have a wrapper or constructor - * like instances created by Python. It makes no sense to support - * deletion or special features like qApp resurrection. - * - * Therefore, the implementation is very simple and just redirects the - * qApp_contents variable and assigns the instance, instead of vice-versa. - */ - - // PYSIDE-1135: Make sure that at least QtCore gets imported. - // That problem exists when a derived instance is created in C++. - // PYSIDE-1164: Use the highest Q*Application module possible, - // because in embedded mode the instance() seems to be sticky. - - // PYSIDE-1135 again: - // The problem of late initialization is not worth the effort. - // We simply don't create the qApp variable when we are embedded. - if (qApp == nullptr) - setup_qApp_var(module); -} - - } //extern "C" // end of module diff --git a/sources/shiboken2/libshiboken/qapp_macro.h b/sources/shiboken2/libshiboken/qapp_macro.h index be45241de..9abd17c17 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.h +++ b/sources/shiboken2/libshiboken/qapp_macro.h @@ -45,8 +45,7 @@ extern "C" { -LIBSHIBOKEN_API PyObject *MakeSingletonQAppWrapper(PyTypeObject *type); -LIBSHIBOKEN_API void NotifyModuleForQApp(PyObject *module, void *qApp); +LIBSHIBOKEN_API PyObject *MakeQAppWrapper(PyTypeObject *type); } // extern "C" |
