diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-04-28 13:00:23 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-05-10 23:10:45 +0200 |
commit | aae2f599e8215e49747c9b5dce698d35b913c6c7 (patch) | |
tree | c0bfa7d49817dbb28563bbff3095f222cd6a6d56 /sources | |
parent | 92d8353b2e046b226bbcb3da3c4bbc3a2e9cb6d8 (diff) |
Implement Q_ARG/Q_RETURN_ARG for QMetaObject.invokeMethod() with arguments
Introduce an internal type QGeneric(Return)ArgumentHolder which casts
to QGeneric(Return)Argument and stores a QMetaType along with a data
pointer. It is returned by Q_ARG/Q_RETURN_ARG and then handled in
QMetaObject.invokeMethod().
Fixes: PYSIDE-1898
Change-Id: I229cb03d5d71c3b32a2e1eb4040f7641b8e49000
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources')
-rw-r--r-- | sources/pyside6/PySide6/QtCore/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp | 140 | ||||
-rw-r--r-- | sources/pyside6/PySide6/QtCore/typesystem_core_common.xml | 55 | ||||
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 151 | ||||
-rw-r--r-- | sources/pyside6/PySide6/qtcorehelper.h | 50 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/qmetaobject_test.py | 84 |
6 files changed, 476 insertions, 7 deletions
diff --git a/sources/pyside6/PySide6/QtCore/CMakeLists.txt b/sources/pyside6/PySide6/QtCore/CMakeLists.txt index b1bae43f9..5cb3a0ad5 100644 --- a/sources/pyside6/PySide6/QtCore/CMakeLists.txt +++ b/sources/pyside6/PySide6/QtCore/CMakeLists.txt @@ -3,6 +3,7 @@ project(QtCore) set(QtCore_static_sources "${QtCore_SOURCE_DIR}/glue/qeasingcurve_glue.cpp" "${QtCore_SOURCE_DIR}/glue/core_snippets.cpp" + "${QtCore_SOURCE_DIR}/glue/qtcorehelper.cpp" ) if(ENABLE_WIN) @@ -151,6 +152,8 @@ ${QtCore_GEN_DIR}/qstringlistmodel_wrapper.cpp ${QtCore_GEN_DIR}/qsysinfo_wrapper.cpp ${QtCore_GEN_DIR}/qsystemsemaphore_wrapper.cpp ${QtCore_GEN_DIR}/qt_wrapper.cpp +${QtCore_GEN_DIR}/qtcorehelper_qgenericargumentholder_wrapper.cpp +${QtCore_GEN_DIR}/qtcorehelper_qgenericreturnargumentholder_wrapper.cpp ${QtCore_GEN_DIR}/qtcorehelper_qmutexlocker_wrapper.cpp ${QtCore_GEN_DIR}/qtemporarydir_wrapper.cpp ${QtCore_GEN_DIR}/qtemporaryfile_wrapper.cpp diff --git a/sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp b/sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp new file mode 100644 index 000000000..01430251d --- /dev/null +++ b/sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtcorehelper.h> + +#include <QtCore/qdebug.h> + +namespace QtCoreHelper { + +// Data classes for the generic argument data classes. The argument is freed +// via QMetaType +class QGenericArgumentData +{ +public: + explicit QGenericArgumentData(const QMetaType &type, const void *aData) : + m_type(type), m_argument(m_type.name(), aData) + { + } + + ~QGenericArgumentData() + { + if (m_type.isValid()) + m_type.destroy(m_argument.data()); + } + + const QMetaType m_type; + const QGenericArgument m_argument; +}; + +class QGenericReturnArgumentData +{ +public: + explicit QGenericReturnArgumentData(const QMetaType &type, void *aData) : + m_type(type), m_argument(m_type.name(), aData) + { + } + + ~QGenericReturnArgumentData() + { + if (m_type.isValid()) + m_type.destroy(m_argument.data()); + } + + const QMetaType m_type; + const QGenericReturnArgument m_argument; +}; + +QGenericArgumentHolder::QGenericArgumentHolder() +{ +} + +QGenericArgumentHolder::QGenericArgumentHolder(const QMetaType &type, const void *aData) : + d(new QGenericArgumentData(type, aData)) +{ +} + +QGenericArgumentHolder::QGenericArgumentHolder(const QGenericArgumentHolder &) = default; +QGenericArgumentHolder::QGenericArgumentHolder(QGenericArgumentHolder &&) = default; +QGenericArgumentHolder &QGenericArgumentHolder::operator=(const QGenericArgumentHolder &) = default; +QGenericArgumentHolder &QGenericArgumentHolder::operator=(QGenericArgumentHolder &&) = default; +QGenericArgumentHolder::~QGenericArgumentHolder() = default; + +QGenericArgumentHolder::operator QGenericArgument() const +{ + return d.isNull() ? QGenericArgument{} : d->m_argument; +} + +QMetaType QGenericArgumentHolder::metaType() const +{ + return d.isNull() ? QMetaType{} : d->m_type; +} + +const void *QGenericArgumentHolder::data() const +{ + return d.isNull() ? nullptr : d->m_argument.data(); +} + +QGenericReturnArgumentHolder::QGenericReturnArgumentHolder(const QMetaType &type, void *aData) : + d(new QGenericReturnArgumentData(type, aData)) +{ +} + +QGenericReturnArgumentHolder::QGenericReturnArgumentHolder(const QGenericReturnArgumentHolder &) = default; +QGenericReturnArgumentHolder::QGenericReturnArgumentHolder(QGenericReturnArgumentHolder &&) = default; +QGenericReturnArgumentHolder &QGenericReturnArgumentHolder::operator=(const QGenericReturnArgumentHolder &) = default; +QGenericReturnArgumentHolder &QGenericReturnArgumentHolder::operator=(QGenericReturnArgumentHolder &&) = default; +QGenericReturnArgumentHolder::~QGenericReturnArgumentHolder() = default; + +QGenericReturnArgumentHolder::operator QGenericReturnArgument() const +{ + return d->m_argument; +} + +QMetaType QGenericReturnArgumentHolder::metaType() const +{ + return d->m_type; +} + +const void *QGenericReturnArgumentHolder::data() const +{ + return d->m_argument.data(); +} + +} // namespace QtCoreHelper diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index 8c717c268..d0e176c2e 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -50,6 +50,7 @@ <function signature="qChecksum(QByteArrayView data, Qt::ChecksumType)"/> --> <extra-includes> + <include file-name="pysidemetatype.h" location="global"/> <include file-name="pysideutils.h" location="global"/> <!-- QString conversion --> <include file-name="signalmanager.h" location="global"/> </extra-includes> @@ -73,7 +74,8 @@ <function signature="qSetMessagePattern(const QString&)"/> <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="include-pyside"/> - + <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" + snippet="qarg_helper"/> <add-function signature="qDebug(const char*)"> <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/> @@ -88,6 +90,24 @@ <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/> </add-function> + <add-function signature="Q_ARG(PyTypeObject *@type@, PyObject *@value@)" + return-type="QtCoreHelper::QGenericArgumentHolder"> + <inject-code file="../glue/qtcore.cpp" snippet="q_arg"/> + <inject-documentation format="target" mode="append"> + This function takes a type and a value of that type and returns an internal + object that can be passed to QMetaObject.invokeMethod(). + See also Q_RETURN_ARG(). + </inject-documentation> + </add-function> + <add-function signature="Q_RETURN_ARG(PyTypeObject *@type@)" + return-type="QtCoreHelper::QGenericReturnArgumentHolder"> + <inject-code file="../glue/qtcore.cpp" snippet="q_return_arg"/> + <inject-documentation format="target" mode="append"> + This macro takes a type a value of which is then returned by + QMetaObject::invokeMethod(). See also Q_ARG(). + </inject-documentation> + </add-function> + <!-- TODO: We do not support void* or const void* as arg --> <rejection class="QMetaObject" function-name="activate"/> <rejection class="QMetaObject" function-name="metacall"/> @@ -2344,6 +2364,8 @@ <inject-code file="../glue/qtcore.cpp" snippet="unlock"/> </add-function> </object-type> + <value-type name="QGenericArgumentHolder"/> + <value-type name="QGenericReturnArgumentHolder"/> </namespace-type> <!-- Qt5 addition --> @@ -2928,6 +2950,9 @@ <include file-name="QtCore/qfuturewatcher.h" location="global"/> </object-type> + <!--// FIXME PYSIDE 7: Remove in favor of QtCoreHelper::QGenericArgumentHolder for + QMetaObject.invokeMethod? It was left as is in case someone has some hack + with a void pointer for this, but it does not really make sense (PYSIDE-1898). --> <value-type name="QGenericArgument"> <include file-name="qobjectdefs.h" location="global"/> </value-type> @@ -2952,7 +2977,13 @@ <object-type name="QMetaObject"> <enum-type name="Call"/> <include file-name="qobjectdefs.h" location="global"/> - <include file-name="dynamicqmetaobject.h" location="global"/> + <extra-includes> + <include file-name="dynamicqmetaobject.h" location="global"/> + <include file-name="pysidemetatype.h" location="global"/> + <include file-name="pysideutils.h" location="global"/> <!-- QString conversion --> + </extra-includes> + <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" + snippet="qmetaobject-invokemethod-helpers"/> <!-- This isn't part of Qt public API --> <modify-function signature="connect(const QObject*,int,const QObject*,int,int,int*)" remove="all"/> <value-type name="Connection" operator-bool="true"> @@ -2961,6 +2992,26 @@ <declare-function signature="operator bool() const" return-type="bool"/> </value-type> <modify-function signature="^invokeMethod\(" allow-thread="yes"/> + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + static="yes" return-type="bool"> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" + snippet="qmetaobject-invokemethod-conn-type-arg"/> + </add-function> + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + static="yes" return-type="bool"> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" + snippet="qmetaobject-invokemethod-arg"/> + </add-function> + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@,QtCoreHelper::QGenericReturnArgumentHolder@ret@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + static="yes" return-type="PyObject*"> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" + snippet="qmetaobject-invokemethod-conn-type-return-arg"/> + </add-function> + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,QtCoreHelper::QGenericReturnArgumentHolder@ret@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + static="yes" return-type="PyObject*"> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" + snippet="qmetaobject-invokemethod-return-arg"/> + </add-function> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-repr"/> </add-function> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index c6d1192ae..c81fc2aea 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -47,6 +47,39 @@ #include "glue/core_snippets_p.h" // @snippet include-pyside +// @snippet qarg_helper + +// Helper for the Q_ARG/Q_RETURN_ARG functions, creating a meta type +// and instance. +struct QArgData +{ + operator bool() const { return metaType.isValid() && data != nullptr; } + + QMetaType metaType; + void *data = nullptr; +}; + +QArgData qArgDataFromPyType(PyObject *t) +{ + auto *pyType = reinterpret_cast<PyTypeObject *>(t); + QArgData result; + result.metaType = PySide::qMetaTypeFromPyType(pyType); + if (!result.metaType.isValid()) { + PyErr_Format(PyExc_RuntimeError, "%s: Unable to find a QMetaType for \"%s\".", + __FUNCTION__, pyType->tp_name); + return result; + } + + result.data = result.metaType.create(); + if (result.data == nullptr) { + PyErr_Format(PyExc_RuntimeError, "%s: Unable to create an instance of \"%s\" (%s).", + __FUNCTION__, pyType->tp_name, result.metaType.name()); + return result; + } + return result; +} +// @snippet qarg_helper + // @snippet qsettings-value // If we enter the kwds, means that we have a defaultValue or // at least a type. @@ -1582,3 +1615,121 @@ if (dataChar == nullptr) { Shiboken::Conversions::pythonToCppPointer(SbkPySide6_QtCoreTypes[SBK_QLOGGINGCATEGORY_IDX], pyArgs[0], &(category)); // @snippet qloggingcategory_to_cpp + +// Q_ARG()-equivalent +// @snippet q_arg +const QArgData qArgData = qArgDataFromPyType(%1); +if (!qArgData) + return nullptr; + +switch (qArgData.metaType.id()) { + case QMetaType::Bool: + *reinterpret_cast<bool *>(qArgData.data) = %2 == Py_True; + break; + case QMetaType::Int: + *reinterpret_cast<int *>(qArgData.data) = int(PyLong_AsLong(%2)); + break; + case QMetaType::Double: + *reinterpret_cast<double *>(qArgData.data) = PyFloat_AsDouble(%2); + break; + case QMetaType::QString: + *reinterpret_cast<QString *>(qArgData.data) = PySide::pyUnicodeToQString(%2); + break; + default: { + Shiboken::Conversions::SpecificConverter converter(qArgData.metaType.name()); + const auto type = converter.conversionType(); + // Copy for values, Pointer for objects + if (type == Shiboken::Conversions::SpecificConverter::InvalidConversion) { + PyErr_Format(PyExc_RuntimeError, "%s: Unable to find converter for \"%s\".", + __FUNCTION__, qArgData.metaType.name()); + return nullptr; + } + converter.toCpp(%2, qArgData.data); + } +} + +QtCoreHelper::QGenericArgumentHolder result(qArgData.metaType, qArgData.data); +%PYARG_0 = %CONVERTTOPYTHON[QtCoreHelper::QGenericArgumentHolder](result); +// @snippet q_arg + +// Q_RETURN_ARG()-equivalent +// @snippet q_return_arg +const QArgData qArgData = qArgDataFromPyType(%1); +if (!qArgData) + return nullptr; + +QtCoreHelper::QGenericReturnArgumentHolder result(qArgData.metaType, qArgData.data); +%PYARG_0 = %CONVERTTOPYTHON[QtCoreHelper::QGenericReturnArgumentHolder](result); +// @snippet q_return_arg + +// invokeMethod(QObject *,const char *, QGenericArgument a0, a1, a2 ) +// @snippet qmetaobject-invokemethod-arg +const bool result = %CPPSELF.invokeMethod(%1, %2, %3, %4, %5); +%PYARG_0 = %CONVERTTOPYTHON[bool](result); +// @snippet qmetaobject-invokemethod-arg + +// invokeMethod(QObject *,const char *,Qt::ConnectionType, QGenericArgument a0, a1, a2 ) +// @snippet qmetaobject-invokemethod-conn-type-arg +qDebug() << __FUNCTION__ << %2; +const bool result = %CPPSELF.invokeMethod(%1, %2, %3, %4, %5, %6); +%PYARG_0 = %CONVERTTOPYTHON[bool](result); +// @snippet qmetaobject-invokemethod-conn-type-arg + +// @snippet qmetaobject-invokemethod-helpers +static PyObject *invokeMethodHelper(QObject *obj, const char *member, Qt::ConnectionType type, + const QtCoreHelper::QGenericReturnArgumentHolder &returnArg, + const QtCoreHelper::QGenericArgumentHolder &v1, + const QtCoreHelper::QGenericArgumentHolder &v2, + const QtCoreHelper::QGenericArgumentHolder &v3) + +{ + const bool callResult = QMetaObject::invokeMethod(obj, member, type, + returnArg, v1, v2, v3); + if (!callResult) { + PyErr_Format(PyExc_RuntimeError, "QMetaObject::invokeMethod(): Invocation of %s::%s() failed.", + obj->metaObject()->className(), member); + return nullptr; + } + + PyObject *result = nullptr; + const void *retData = returnArg.data(); + const QMetaType metaType = returnArg.metaType(); + switch (metaType.id()) { + case QMetaType::Bool: + result = *reinterpret_cast<const bool *>(retData) ? Py_True : Py_False; + Py_INCREF(result); + break; + case QMetaType::Int: + result = PyLong_FromLong(*reinterpret_cast<const int *>(retData)); + break; + case QMetaType::Double: + result = PyFloat_FromDouble(*reinterpret_cast<const double *>(retData)); + break; + case QMetaType::QString: + result = PySide::qStringToPyUnicode(*reinterpret_cast<const QString *>(retData)); + break; + default: { + Shiboken::Conversions::SpecificConverter converter(metaType.name()); + const auto type = converter.conversionType(); + if (type == Shiboken::Conversions::SpecificConverter::InvalidConversion) { + PyErr_Format(PyExc_RuntimeError, "%s: Unable to find converter for \"%s\".", + __FUNCTION__, metaType.name()); + return nullptr; + } + result = converter.toPython(retData); + } + } + return result; +} +// @snippet qmetaobject-invokemethod-helpers + +// invokeMethod(QObject *,const char *, Qt::ConnectionType, QGenericReturnArgument,QGenericArgument a0, a1, a2 ) +// @snippet qmetaobject-invokemethod-conn-type-return-arg +%PYARG_0 = invokeMethodHelper(%1, %2, %3, %4, %5, %6, %7); +// @snippet qmetaobject-invokemethod-conn-type-return-arg + +// invokeMethod(QObject *,const char *, QGenericReturnArgument,QGenericArgument a0, a1, a2 ) +// @snippet qmetaobject-invokemethod-return-arg +%PYARG_0 = invokeMethodHelper(%1, %2, Qt::AutoConnection, %3, %4, %5, %6); +// @snippet qmetaobject-invokemethod-return-arg + diff --git a/sources/pyside6/PySide6/qtcorehelper.h b/sources/pyside6/PySide6/qtcorehelper.h index 22b87fc0e..8b0614839 100644 --- a/sources/pyside6/PySide6/qtcorehelper.h +++ b/sources/pyside6/PySide6/qtcorehelper.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -40,7 +40,10 @@ #ifndef QTCOREHELPER_H #define QTCOREHELPER_H +#include <QtCore/qsharedpointer.h> #include <QtCore/qmutex.h> +#include <QtCore/qobjectdefs.h> + namespace QtCoreHelper { @@ -101,6 +104,51 @@ namespace QtCoreHelper { RecursiveMutexLocker *m_recursiveMutexLocker = nullptr; }; + class QGenericArgumentData; + + // Return value of function Q_ARG() to be passed to QMetaObject::invokeMethod. + // Frees the data if it is an allocated, primitive type. + class QGenericArgumentHolder { + public: + QGenericArgumentHolder(); + explicit QGenericArgumentHolder(const QMetaType &type, const void *aData); + QGenericArgumentHolder(const QGenericArgumentHolder &); + QGenericArgumentHolder(QGenericArgumentHolder &&); + QGenericArgumentHolder &operator=(const QGenericArgumentHolder &); + QGenericArgumentHolder &operator=(QGenericArgumentHolder &&); + ~QGenericArgumentHolder(); + + operator QGenericArgument () const; + + QMetaType metaType() const; + const void *data() const; + + private: + QSharedPointer<QGenericArgumentData> d; + }; + + class QGenericReturnArgumentData; + + // Return value of function Q_RETURN_ARG() to be passed to QMetaObject::invokeMethod. + // Frees the data if it is an allocated, primitive type. + class QGenericReturnArgumentHolder { + public: + explicit QGenericReturnArgumentHolder(const QMetaType &type, void *aData); + QGenericReturnArgumentHolder(const QGenericReturnArgumentHolder &); + QGenericReturnArgumentHolder(QGenericReturnArgumentHolder &&); + QGenericReturnArgumentHolder &operator=(const QGenericReturnArgumentHolder &); + QGenericReturnArgumentHolder &operator=(QGenericReturnArgumentHolder &&); + ~QGenericReturnArgumentHolder(); + + operator QGenericReturnArgument () const; + + QMetaType metaType() const; + const void *data() const; + + private: + QSharedPointer<QGenericReturnArgumentData> d; + }; + } // namespace QtCoreHelper #endif // QTCOREHELPER_H diff --git a/sources/pyside6/tests/QtCore/qmetaobject_test.py b/sources/pyside6/tests/QtCore/qmetaobject_test.py index a65a9937d..be51c9640 100644 --- a/sources/pyside6/tests/QtCore/qmetaobject_test.py +++ b/sources/pyside6/tests/QtCore/qmetaobject_test.py @@ -3,7 +3,7 @@ ############################################################################# ## -## Copyright (C) 2016 The Qt Company Ltd. +## Copyright (C) 2022 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the test suite of Qt for Python. @@ -40,8 +40,9 @@ 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 (QCoreApplication, QFile, QObject, QTimer, - QSemaphore, Qt, Signal, Slot, SIGNAL) +from PySide6.QtCore import (QCoreApplication, QFile, QMetaObject, QObject, + QPoint, QTimer, QSemaphore, Qt, Signal, Slot, + SIGNAL, Q_ARG, Q_RETURN_ARG) class Foo(QFile): @@ -53,6 +54,35 @@ class DynObject(QObject): pass +class InvokeTester(QObject): + def __init__(self, parent=None): + super().__init__(parent) + + @Slot(int, int, result=int) + def add(self, a, b): + return a + b + + @Slot(str, str, result=str) + def concatenate(self, a, b): + return a + b + + @Slot(QPoint, result=int) + def manhattan_length(self, point): + return abs(point.x()) + abs(point.y()) + + @Slot(QPoint, QPoint, result=QPoint) + def add_points(self, point1, point2): + return point1 + point2 + + @Slot(QObject, result=str) + def object_name(self, o): + return o.objectName() + + @Slot(QObject, result=QObject) + def first_child(self, o): + return o.children()[0] + + class SemaphoreSender(QObject): signal = Signal(QSemaphore) @@ -130,7 +160,53 @@ class qmetaobject_test(unittest.TestCase): QCoreApplication.processEvents() self.assertEqual(sender.semaphore, receiver.semaphore) + # PYSIDE-1898, + def test_Invoke(self): + tester = InvokeTester() + + # Primitive types + sum = QMetaObject.invokeMethod(tester, "add", Q_RETURN_ARG(int), + Q_ARG(int, 2), Q_ARG(int, 3)) + self.assertEqual(sum, 5) + + concatenated = QMetaObject.invokeMethod(tester, "concatenate", + Q_RETURN_ARG(str), + Q_ARG(str, "bla"), + Q_ARG(str, "bla")) + self.assertEqual(concatenated, "blabla") + + # Wrapped type as in-parameter + point = QPoint(10, 20) + len = QMetaObject.invokeMethod(tester, "manhattan_length", + Q_RETURN_ARG(int), + Q_ARG(QPoint, point)) + self.assertEqual(len, point.manhattanLength()) + + # Wrapped type as result + point2 = QPoint(30, 30) + point3 = QMetaObject.invokeMethod(tester, "add_points", + Q_RETURN_ARG(QPoint), + Q_ARG(QPoint, point), + Q_ARG(QPoint, point2)) + self.assertEqual(point + point2, point3) + + # Pass an object + o = QObject() + o.setObjectName("Foo") + name = QMetaObject.invokeMethod(tester, "object_name", + Q_RETURN_ARG(str), + Q_ARG(QObject, o)) + self.assertEqual(name, o.objectName()) + + # Return an object + child = QObject(o) + child.setObjectName("Child") + c = QMetaObject.invokeMethod(tester, "first_child", + Q_RETURN_ARG(QObject), + Q_ARG(QObject, o)) + self.assertTrue(c) + self.assertEqual(c, child) + if __name__ == '__main__': unittest.main() - |