diff options
author | Maximilian Goldstein <max.goldstein@qt.io> | 2020-08-26 10:26:35 +0200 |
---|---|---|
committer | Maximilian Goldstein <max.goldstein@qt.io> | 2020-08-26 10:26:47 +0000 |
commit | 093bc20a90543e0e4c386083e2d97b98510058b2 (patch) | |
tree | 7218b29b32ee6db1b54dc57c9daa4612f3e767b9 /sources/pyside2 | |
parent | 387624b29c88f06d1cab8c566bc82fdf40c8f804 (diff) |
QtQml: Improve ListProperty
* Support new methods
* Do more type checking
* Avoids segfaults when calling unimplemented methods
Change-Id: Id0cfbc2f9a79c0f916db0bb9e950d707dc5da478
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/pyside2')
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp | 72 | ||||
-rw-r--r-- | sources/pyside2/tests/QtQml/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sources/pyside2/tests/QtQml/listproperty.py | 81 |
3 files changed, 149 insertions, 5 deletions
diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 87d2437ba..a933876e4 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -277,27 +277,51 @@ struct QmlListProperty { PyTypeObject *type; PyObject *append; + PyObject *count; PyObject *at; PyObject *clear; - PyObject *count; + PyObject *replace; + PyObject *removeLast; }; static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds) { - static const char *kwlist[] = {"type", "append", "at", "clear", "count", 0}; + static const char *kwlist[] = {"type", "append", "count", "at", "clear", "replace", "removeLast", 0}; PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self); QmlListProperty *data = new QmlListProperty; memset(data, 0, sizeof(QmlListProperty)); if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|OOO:QtQml.ListProperty", (char **) kwlist, + "O|OOOOOO:QtQml.ListProperty", (char **) kwlist, &data->type, &data->append, + &data->count, &data->at, &data->clear, - &data->count)) { + &data->replace, + &data->removeLast)) { + return -1; + } + + static PyTypeObject *qobjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + assert(qobjectType); + + if (!PySequence_Contains(data->type->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) { + PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.", + qobjectType->tp_name, data->type->tp_name); return -1; } + + if ((data->append && data->append != Py_None && !PyCallable_Check(data->append)) || + (data->count && data->count != Py_None && !PyCallable_Check(data->count)) || + (data->at && data->at != Py_None && !PyCallable_Check(data->at)) || + (data->clear && data->clear != Py_None && !PyCallable_Check(data->clear)) || + (data->replace && data->replace != Py_None && !PyCallable_Check(data->replace)) || + (data->removeLast && data->removeLast != Py_None && !PyCallable_Check(data->removeLast))) { + PyErr_Format(PyExc_TypeError, "Non-callable parameter given"); + return -1; + } + PySide::Property::setMetaCallHandler(pySelf, &propListMetaCall); PySide::Property::setTypeName(pySelf, "QQmlListProperty<QObject>"); PySide::Property::setUserData(pySelf, data); @@ -413,6 +437,38 @@ void propListClear(QQmlListProperty<QObject> * propList) PyErr_Print(); } +// Implementation of QQmlListProperty<T>::ReplaceFunction callback +void propListReplace(QQmlListProperty<QObject> *propList, int index, QObject *value) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(3)); + PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object)); + PyTuple_SET_ITEM(args, 1, Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &index)); + PyTuple_SET_ITEM(args, 2, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], value)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->replace, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + +// Implementation of QQmlListProperty<T>::RemoveLastFunction callback +void propListRemoveLast(QQmlListProperty<QObject> *propList) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(1)); + PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->removeLast, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + // qt_metacall specialization for ListProperties static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, void **args) { @@ -422,7 +478,13 @@ static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Ca auto data = reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pp)); QObject *qobj; Shiboken::Conversions::pythonToCppPointer((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], self, &qobj); - QQmlListProperty<QObject> declProp(qobj, data, &propListAppender, &propListCount, &propListAt, &propListClear); + QQmlListProperty<QObject> declProp(qobj, data, + data->append && data->append != Py_None ? &propListAppender : nullptr, + data->count && data->count != Py_None ? &propListCount : nullptr, + data->at && data->at != Py_None ? &propListAt : nullptr, + data->clear && data->clear != Py_None ? &propListClear : nullptr, + data->replace && data->replace != Py_None ? &propListReplace : nullptr, + data->removeLast && data->removeLast != Py_None ? &propListRemoveLast : nullptr); // Copy the data to the memory location requested by the meta call void *v = args[0]; diff --git a/sources/pyside2/tests/QtQml/CMakeLists.txt b/sources/pyside2/tests/QtQml/CMakeLists.txt index 76f9fec49..e35831f25 100644 --- a/sources/pyside2/tests/QtQml/CMakeLists.txt +++ b/sources/pyside2/tests/QtQml/CMakeLists.txt @@ -11,6 +11,7 @@ PYSIDE_TEST(bug_951.py) PYSIDE_TEST(bug_995.py) PYSIDE_TEST(bug_997.py) PYSIDE_TEST(bug_1029.py) +PYSIDE_TEST(listproperty.py) PYSIDE_TEST(qqmlnetwork_test.py) PYSIDE_TEST(qquickview_test.py) PYSIDE_TEST(connect_python_qml.py) diff --git a/sources/pyside2/tests/QtQml/listproperty.py b/sources/pyside2/tests/QtQml/listproperty.py new file mode 100644 index 000000000..6337e5fa4 --- /dev/null +++ b/sources/pyside2/tests/QtQml/listproperty.py @@ -0,0 +1,81 @@ +############################################################################# +## +## Copyright (C) 2020 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.QtCore import QObject +from PySide2.QtQml import ListProperty + +class InheritsQObject(QObject): + pass + +def dummyFunc(): + pass + +class TestListProperty(unittest.TestCase): + def testIt(self): + + # Verify that type checking works properly + type_check_error = False + + try: + ListProperty(QObject) + ListProperty(InheritsQObject) + except: + type_check_error = True + + self.assertFalse(type_check_error) + + try: + ListProperty(int) + except TypeError: + type_check_error = True + + self.assertTrue(type_check_error) + + # Verify that method validation works properly + method_check_error = False + + try: + ListProperty(QObject, append=None, at=None, count=None, replace=None, clear=None, removeLast=None) # Explicitly setting None + ListProperty(QObject, append=dummyFunc) + ListProperty(QObject, count=dummyFunc, at=dummyFunc) + except: + method_check_error = True + + self.assertFalse(method_check_error) + + try: + ListPropery(QObject, append=QObject()) + except: + method_check_error = True + + self.assertTrue(method_check_error) + +if __name__ == '__main__': + unittest.main() |