diff options
Diffstat (limited to 'sources/pyside6')
-rw-r--r-- | sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml | 5 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidemetafunction.cpp | 8 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysideproperty.cpp | 25 | ||||
-rw-r--r-- | sources/pyside6/libpyside/signalmanager.cpp | 71 | ||||
-rw-r--r-- | sources/pyside6/libpyside/signalmanager.h | 8 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/qobject_property_test.py | 34 | ||||
-rw-r--r-- | sources/pyside6/tests/QtWidgets/bug_433.py | 2 | ||||
-rw-r--r-- | sources/pyside6/tests/QtWidgets/bug_919.py | 9 | ||||
-rw-r--r-- | sources/pyside6/tests/QtWidgets/qapp_issue_585.py | 10 | ||||
-rw-r--r-- | sources/pyside6/tests/QtWidgets/qmenu_test.py | 18 |
10 files changed, 143 insertions, 47 deletions
diff --git a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml index a15527c03..c6e93cfd7 100644 --- a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml +++ b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml @@ -1489,6 +1489,7 @@ <enum-type name="InputMode"/> <modify-function signature="getInt(QWidget*,const QString&,const QString&,int,int,int,int,bool*,QFlags<Qt::WindowType>)" allow-thread="yes"> + <modify-argument index="1" pyi-type="Optional[PySide6.QtWidgets.QWidget]"/> <modify-argument index="return" pyi-type="Tuple[int, bool]"/> <modify-argument index="8"> <remove-default-expression/> @@ -1500,6 +1501,7 @@ </modify-function> <modify-function signature="getItem(QWidget*,const QString&,const QString&,const QStringList&,int,bool,bool*,QFlags<Qt::WindowType>,QFlags<Qt::InputMethodHint>)" allow-thread="yes"> + <modify-argument index="1" pyi-type="Optional[PySide6.QtWidgets.QWidget]"/> <modify-argument index="return" pyi-type="Tuple[str, bool]"/> <modify-argument index="7"> <remove-default-expression/> @@ -1511,6 +1513,7 @@ </modify-function> <modify-function signature="getMultiLineText(QWidget*,const QString&,const QString&,const QString&,bool*,QFlags<Qt::WindowType>,QFlags<Qt::InputMethodHint>)" allow-thread="yes"> + <modify-argument index="1" pyi-type="Optional[PySide6.QtWidgets.QWidget]"/> <modify-argument index="return" pyi-type="Tuple[str, bool]"/> <modify-argument index="5"> <remove-default-expression/> @@ -1522,6 +1525,7 @@ </modify-function> <modify-function signature="getText(QWidget*,const QString&,const QString&,QLineEdit::EchoMode,const QString&,bool*,QFlags<Qt::WindowType>,QFlags<Qt::InputMethodHint>)" allow-thread="yes"> + <modify-argument index="1" pyi-type="Optional[PySide6.QtWidgets.QWidget]"/> <modify-argument index="return" pyi-type="Tuple[str, bool]"/> <modify-argument index="6"> <remove-default-expression/> @@ -1533,6 +1537,7 @@ </modify-function> <modify-function signature="getDouble(QWidget*,const QString&,const QString&,double,double,double,int,bool*,QFlags<Qt::WindowType>,double)" allow-thread="yes"> + <modify-argument index="1" pyi-type="Optional[PySide6.QtWidgets.QWidget]"/> <modify-argument index="return" pyi-type="Tuple[float, bool]"/> <modify-argument index="8"> <remove-default-expression/> diff --git a/sources/pyside6/libpyside/pysidemetafunction.cpp b/sources/pyside6/libpyside/pysidemetafunction.cpp index 48aba3c7b..7a496c4b7 100644 --- a/sources/pyside6/libpyside/pysidemetafunction.cpp +++ b/sources/pyside6/libpyside/pysidemetafunction.cpp @@ -4,6 +4,8 @@ #include "pysidemetafunction.h" #include "pysidemetafunction_p.h" +#include <signalmanager.h> + #include <autodecref.h> #include <basewrapper.h> #include <sbkconverter.h> @@ -12,6 +14,8 @@ #include <QtCore/qmetaobject.h> +using namespace Qt::StringLiterals; + extern "C" { @@ -164,6 +168,10 @@ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal) QString tmp; converter.toCpp(obj, &tmp); methValues[i] = tmp; + } else if (metaType.id() == PyObjectWrapper::metaTypeId()) { + // Manual conversion, see PyObjectWrapper converter registration + methValues[i] = QVariant::fromValue(PyObjectWrapper(obj.object())); + methArgs[i] = methValues[i].data(); } else { converter.toCpp(obj, methArgs[i]); } diff --git a/sources/pyside6/libpyside/pysideproperty.cpp b/sources/pyside6/libpyside/pysideproperty.cpp index e8ea2fa03..69d347043 100644 --- a/sources/pyside6/libpyside/pysideproperty.cpp +++ b/sources/pyside6/libpyside/pysideproperty.cpp @@ -6,6 +6,7 @@ #include "pysideproperty_p.h" #include "pysidesignal.h" #include "pysidesignal_p.h" +#include "signalmanager.h" #include <autodecref.h> #include <pep384ext.h> @@ -17,6 +18,8 @@ using namespace Shiboken; +using namespace Qt::StringLiterals; + extern "C" { @@ -148,16 +151,20 @@ void PySidePropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call, v switch (call) { case QMetaObject::ReadProperty: { AutoDecRef value(getValue(source)); - auto *obValue = value.object(); - if (obValue) { - Conversions::SpecificConverter converter(typeName); - if (converter) { - converter.toCpp(obValue, args[0]); - } else { - // PYSIDE-2160: Report an unknown type name to the caller `qtPropertyMetacall`. - PyErr_SetObject(PyExc_StopIteration, obValue); - } + if (value.isNull()) + return; + if (typeName == "PyObject"_ba) { + // Manual conversion, see PyObjectWrapper converter registration + auto *pw = reinterpret_cast<PySide::PyObjectWrapper *>(args[0]); + pw->reset(value.object()); + return; + } + if (Conversions::SpecificConverter converter(typeName); converter) { + converter.toCpp(value.object(), args[0]); + return; } + // PYSIDE-2160: Report an unknown type name to the caller `qtPropertyMetacall`. + PyErr_SetObject(PyExc_StopIteration, value.object()); } break; diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp index 933edd318..305d2f5c3 100644 --- a/sources/pyside6/libpyside/signalmanager.cpp +++ b/sources/pyside6/libpyside/signalmanager.cpp @@ -24,9 +24,12 @@ #include <QtCore/qcoreevent.h> #include <QtCore/qdebug.h> #include <QtCore/qhash.h> +#include <QtCore/qmetatype.h> #include <QtCore/qscopedpointer.h> +#include <climits> #include <memory> +#include <utility> using namespace Qt::StringLiterals; @@ -36,6 +39,8 @@ using namespace Qt::StringLiterals; static PyObject *metaObjectAttr = nullptr; +static int pyObjectWrapperMetaTypeId = QMetaType::UnknownType; + static void destroyMetaObject(PyObject *obj) { void *ptr = PyCapsule_GetPointer(obj, nullptr); @@ -125,6 +130,17 @@ PyObjectWrapper::PyObjectWrapper(const PyObjectWrapper &other) Py_XINCREF(m_me); } +PyObjectWrapper::PyObjectWrapper(PyObjectWrapper &&other) noexcept + : m_me{std::exchange(other.m_me, nullptr)} +{ +} + +PyObjectWrapper &PyObjectWrapper::operator=(PyObjectWrapper &&other) noexcept +{ + m_me = std::exchange(other.m_me, nullptr); + return *this; +} + PyObjectWrapper::~PyObjectWrapper() { // Check that Python is still initialized as sometimes this is called by a static destructor @@ -156,6 +172,10 @@ PyObjectWrapper::operator PyObject *() const return m_me; } +int PyObjectWrapper::metaTypeId() +{ + return pyObjectWrapperMetaTypeId; +} int PyObjectWrapper::toInt() const { @@ -221,7 +241,29 @@ QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj) return in; } -}; +PYSIDE_API QDebug operator<<(QDebug debug, const PyObjectWrapper &myObj) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + // Do not repeat the type name as it is typically called from the QVariant debug + // operator, which outputs the type. + debug << '<'; + if (PyObject *ob = myObj) { + const auto refs = Py_REFCNT(ob); + debug << Py_TYPE(ob)->tp_name << " at " << ob; + if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT + debug << ", immortal"; + else + debug << ", refs=" << refs; + } else { + debug << '0'; + } + debug << '>'; + return debug; +} + +} // namespace PySide using namespace PySide; @@ -238,19 +280,11 @@ struct SignalManagerPrivate SignalManager::QmlMetaCallErrorHandler SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr; -static void PyObject_PythonToCpp_PyObject_PTR(PyObject *pyIn, void *cppOut) -{ - *reinterpret_cast<PyObject **>(cppOut) = pyIn; -} -static PythonToCppFunc is_PyObject_PythonToCpp_PyObject_PTR_Convertible(PyObject * /* pyIn */) -{ - return PyObject_PythonToCpp_PyObject_PTR; -} -static PyObject *PyObject_PTR_CppToPython_PyObject(const void *cppIn) +static PyObject *CopyCppToPythonPyObject(const void *cppIn) { - auto *pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn)); - if (pyOut) - Py_INCREF(pyOut); + const auto *wrapper = reinterpret_cast<const PyObjectWrapper *>(cppIn); + PyObject *pyOut = *wrapper; + Py_XINCREF(pyOut); return pyOut; } @@ -260,13 +294,16 @@ void SignalManager::init() using namespace Shiboken; // Register PyObject type to use in queued signal and slot connections - qRegisterMetaType<PyObjectWrapper>("PyObject"); + pyObjectWrapperMetaTypeId = qRegisterMetaType<PyObjectWrapper>("PyObject"); // Register QVariant(enum) conversion to QVariant(int) QMetaType::registerConverter<PyObjectWrapper, int>(&PyObjectWrapper::toInt); - SbkConverter *converter = Shiboken::Conversions::createConverter(&PyBaseObject_Type, nullptr); - Shiboken::Conversions::setCppPointerToPythonFunction(converter, PyObject_PTR_CppToPython_PyObject); - Shiboken::Conversions::setPythonToCppPointerFunctions(converter, PyObject_PythonToCpp_PyObject_PTR, is_PyObject_PythonToCpp_PyObject_PTR_Convertible); + // Register a shiboken converter for PyObjectWrapper->Python (value conversion). + // Python->PyObjectWrapper is not registered since the converters do not work for + // non-SbkObject types (falling back to plain pointer pass through). + // This conversion needs to be done manually via QVariant. + SbkConverter *converter = Shiboken::Conversions::createConverter(&PyBaseObject_Type, + CopyCppToPythonPyObject); Shiboken::Conversions::registerConverterName(converter, "PyObject"); Shiboken::Conversions::registerConverterName(converter, "object"); Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper"); diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h index d5c007dbe..9fe56efc2 100644 --- a/sources/pyside6/libpyside/signalmanager.h +++ b/sources/pyside6/libpyside/signalmanager.h @@ -14,6 +14,7 @@ #include <optional> QT_FORWARD_DECLARE_CLASS(QDataStream) +QT_FORWARD_DECLARE_CLASS(QDebug) namespace PySide { @@ -22,13 +23,13 @@ namespace PySide class PYSIDE_API PyObjectWrapper { public: - PyObjectWrapper(PyObjectWrapper&&) = delete; - PyObjectWrapper& operator=(PyObjectWrapper &&) = delete; PyObjectWrapper(); explicit PyObjectWrapper(PyObject* me); PyObjectWrapper(const PyObjectWrapper &other); PyObjectWrapper& operator=(const PyObjectWrapper &other); + PyObjectWrapper(PyObjectWrapper&&) noexcept; + PyObjectWrapper &operator=(PyObjectWrapper &&) noexcept; void reset(PyObject *o); @@ -43,12 +44,15 @@ public: // The proper fix would be to associate PyObjectWrapper to the corresponding C++ Enum. int toInt() const; + static int metaTypeId(); + private: PyObject* m_me; }; PYSIDE_API QDataStream &operator<<(QDataStream& out, const PyObjectWrapper& myObj); PYSIDE_API QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj); +PYSIDE_API QDebug operator<<(QDebug debug, const PyObjectWrapper &myObj); class PYSIDE_API SignalManager { diff --git a/sources/pyside6/tests/QtCore/qobject_property_test.py b/sources/pyside6/tests/QtCore/qobject_property_test.py index 37936205e..80387ec77 100644 --- a/sources/pyside6/tests/QtCore/qobject_property_test.py +++ b/sources/pyside6/tests/QtCore/qobject_property_test.py @@ -32,6 +32,26 @@ class MyObjectWithNotifyProperty(QObject): myProperty = Property(int, readP, fset=writeP, notify=notifyP) +class OtherClass: + """Helper for QObjectWithOtherClassPropertyTest.""" + pass + + +class MyObjectWithOtherClassProperty(QObject): + """Helper for QObjectWithOtherClassPropertyTest.""" + def __init__(self, parent=None): + super().__init__(parent) + self._otherclass = None + + def _get_otherclass(self): + return self._otherclass + + def _set_otherclass(self, o): + self._otherclass = o + + otherclass = Property(OtherClass, fget=_get_otherclass, fset=_set_otherclass) + + class PropertyWithNotify(unittest.TestCase): def called(self): self.called_ = True @@ -50,5 +70,19 @@ class PropertyWithNotify(unittest.TestCase): self.assertEqual(o.property("myProperty"), 10) +class QObjectWithOtherClassPropertyTest(unittest.TestCase): + """PYSIDE-2193: For properties of custom classes not wrapped by shiboken, + QVariant<PyObjectWrapper> is used, which had refcount issues causing crashes. + Exercise the QVariant conversion by setting and retrieving via the + QVariant-based property()/setProperty() API.""" + def testNotify(self): + obj = MyObjectWithOtherClassProperty() + obj.setProperty("otherclass", OtherClass()) + for i in range(10): + pv = obj.property("otherclass") + print(pv) # Exercise repr + self.assertTrue(type(pv) is OtherClass) + + if __name__ == '__main__': unittest.main() diff --git a/sources/pyside6/tests/QtWidgets/bug_433.py b/sources/pyside6/tests/QtWidgets/bug_433.py index 5adcacccc..6d5c4333a 100644 --- a/sources/pyside6/tests/QtWidgets/bug_433.py +++ b/sources/pyside6/tests/QtWidgets/bug_433.py @@ -26,4 +26,4 @@ a = QApplication(sys.argv) t = Test() t.show() QTimer.singleShot(0, t.close) -sys.exit(a.exec_()) +sys.exit(a.exec()) diff --git a/sources/pyside6/tests/QtWidgets/bug_919.py b/sources/pyside6/tests/QtWidgets/bug_919.py index 6f2fc128d..a95d3aa37 100644 --- a/sources/pyside6/tests/QtWidgets/bug_919.py +++ b/sources/pyside6/tests/QtWidgets/bug_919.py @@ -26,10 +26,11 @@ class MyWidget(QPushButton): self.paintReceived.emit() def paintEvent(self, e): - p = QPainter(self) - style = QApplication.style() - option = QStyleOptionButton() - style.drawControl(QStyle.ControlElement.CE_PushButton, option, p) + with QPainter(self) as p: + style = QApplication.style() + option = QStyleOptionButton() + self.initStyleOption(option) + style.drawControl(QStyle.ControlElement.CE_PushButton, option, p) self._painted = True QTimer.singleShot(0, self._emitPainted) diff --git a/sources/pyside6/tests/QtWidgets/qapp_issue_585.py b/sources/pyside6/tests/QtWidgets/qapp_issue_585.py index ec8a47569..1300ea4aa 100644 --- a/sources/pyside6/tests/QtWidgets/qapp_issue_585.py +++ b/sources/pyside6/tests/QtWidgets/qapp_issue_585.py @@ -40,15 +40,15 @@ import sys from pathlib import Path sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) sys.path.append(os.fspath(Path(__file__).resolve().parents[1] / "util")) -from init_paths import init_test_paths +from init_paths import init_test_paths # noqa: E402 init_test_paths() -from PySide6.QtCore import QTimer -from PySide6.QtWidgets import QApplication +from PySide6.QtCore import QTimer # noqa: E402 +from PySide6.QtWidgets import QApplication # noqa: E402 app_instance = QApplication([]) # If the following line is commented, application doesn't crash on exit anymore. app_instance2 = app_instance -QTimer.singleShot(0, qApp.quit) -app_instance.exec_() +QTimer.singleShot(0, qApp.quit) # noqa: F821 +app_instance.exec() diff --git a/sources/pyside6/tests/QtWidgets/qmenu_test.py b/sources/pyside6/tests/QtWidgets/qmenu_test.py index 7d1d262e4..8bd5d1624 100644 --- a/sources/pyside6/tests/QtWidgets/qmenu_test.py +++ b/sources/pyside6/tests/QtWidgets/qmenu_test.py @@ -33,16 +33,16 @@ class QMenuAddAction(UsesQApplication): def testAddActionWithoutKeySequenceCallable(self): # bug #280 - action = self.menu.addAction(self.app.tr('aaa'), lambda: 1) + action = self.menu.addAction(self.app.tr('aaa'), lambda: 1) # noqa: F841 def testAddActionKeySequenceCallable(self): # bug #228 - action = self.menu.addAction(self.app.tr('aaa'), lambda: 1, + action = self.menu.addAction(self.app.tr('aaa'), lambda: 1, # noqa: F841 QKeySequence(self.app.tr('Ctrl+O'))) def testAddActionKeySequenceSlot(self): - action = self.menu.addAction('Quit', self.app, SLOT('quit()'), - QKeySequence('Ctrl+O')) + action = self.menu.addAction('Quit', QKeySequence('Ctrl+O'), # noqa: F841 + self.app, SLOT('quit()')) class QMenuAddActionWithIcon(UsesQApplication): @@ -61,16 +61,16 @@ class QMenuAddActionWithIcon(UsesQApplication): def testAddActionWithoutKeySequenceCallable(self): # bug #280 - action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1) + action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1) # noqa: F841 def testAddActionKeySequenceCallable(self): # bug #228 - action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1, - QKeySequence(self.app.tr('Ctrl+O'))) + action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1, # noqa: F841 + QKeySequence(self.app.tr('Ctrl+O'))) # noqa: F841 def testAddActionKeySequenceSlot(self): - action = self.menu.addAction(self.icon, 'Quit', self.app, SLOT('quit()'), - QKeySequence('Ctrl+O')) + action = self.menu.addAction(self.icon, 'Quit', QKeySequence('Ctrl+O'), # noqa: F841 + self.app, SLOT('quit()')) # noqa: F841 if __name__ == '__main__': |