aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-04-28 13:00:23 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-05-10 23:10:45 +0200
commitaae2f599e8215e49747c9b5dce698d35b913c6c7 (patch)
treec0bfa7d49817dbb28563bbff3095f222cd6a6d56 /sources
parent92d8353b2e046b226bbcb3da3c4bbc3a2e9cb6d8 (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.txt3
-rw-r--r--sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp140
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml55
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp151
-rw-r--r--sources/pyside6/PySide6/qtcorehelper.h50
-rw-r--r--sources/pyside6/tests/QtCore/qmetaobject_test.py84
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&amp;)"/>
<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()
-