// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "pysideqmllistproperty_p.h" #include "pysideqmlregistertype_p.h" #include "pysideqmllistpropertymixin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // This is the user data we store in the property. class QmlListPropertyPrivate : public PySidePropertyPrivate, public QmlListPropertyMixin { public: void metaCall(PyObject *source, QMetaObject::Call call, void **args) override { handleMetaCall(source, call, args); } qsizetype count(QQmlListProperty *propList) const override; QObject *at(QQmlListProperty *propList, qsizetype index) const override; void append(QQmlListProperty *propList, QObject *item) override; void clear(QQmlListProperty * propList) override; void replace(QQmlListProperty *propList, qsizetype index, QObject *value) override; void removeLast(QQmlListProperty *propList) override; PyTypeObject *elementType = nullptr; PyObject *obAppend = nullptr; PyObject *obCount = nullptr; PyObject *obAt = nullptr; PyObject *obClear = nullptr; PyObject *obReplace = nullptr; PyObject *obRemoveLast = nullptr; }; extern "C" { static PyObject *propList_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) { auto *me = PepExt_TypeCallAlloc(subtype, 0); me->d = new QmlListPropertyPrivate; return reinterpret_cast(me); } static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds) { static const char *kwlist[] = {"type", "append", "count", "at", "clear", "replace", "removeLast", "doc", "notify", // PySideProperty "designable", "scriptable", "stored", "user", "constant", "final", nullptr}; auto *pySelf = reinterpret_cast(self); auto *data = static_cast(pySelf->d); char *doc{}; PyObject *append{}, *count{}, *at{}, *clear{}, *replace{}, *removeLast{}, *notify{}; bool designable{true}, scriptable{true}, stored{true}; bool user{false}, constant{false}, finalProp{false}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOOOOsObbbbbb:QtQml.ListProperty", const_cast(kwlist), &data->elementType, &append, &count, &at, &clear, &replace, &removeLast, /*s*/ &doc, /*O*/ ¬ify, // PySideProperty /*bbb*/ &designable, &scriptable, &stored, /*bbb*/ &user, &constant, &finalProp)) { return -1; } if (!PySidePropertyPrivate::assignCheckCallable(append, "append", &data->obAppend) || !PySidePropertyPrivate::assignCheckCallable(count, "count", &data->obCount) || !PySidePropertyPrivate::assignCheckCallable(at, "at", &data->obAt) || !PySidePropertyPrivate::assignCheckCallable(clear, "clear", &data->obClear) || !PySidePropertyPrivate::assignCheckCallable(replace, "replace", &data->obReplace) || !PySidePropertyPrivate::assignCheckCallable(removeLast, "removeLast", &data->obRemoveLast)) { return -1; } data->setMethodFlag(QmlListPropertyMixin::MethodFlag::Count, data->obCount != nullptr); data->setMethodFlag(QmlListPropertyMixin::MethodFlag::At, data->obAt != nullptr); data->setMethodFlag(QmlListPropertyMixin::MethodFlag::Append, data->obAppend != nullptr); data->setMethodFlag(QmlListPropertyMixin::MethodFlag::Clear, data->obClear != nullptr); data->setMethodFlag(QmlListPropertyMixin::MethodFlag::Replace, data->obReplace != nullptr); data->setMethodFlag(QmlListPropertyMixin::MethodFlag::RemoveLast, data->obRemoveLast != nullptr); if (notify != nullptr && notify != Py_None) data->notify = notify; if (doc) data->doc = doc; else data->doc.clear(); PyTypeObject *qobjectType = PySide::qObjectType(); if (!PySequence_Contains(data->elementType->tp_mro, reinterpret_cast(qobjectType))) { PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.", qobjectType->tp_name, data->elementType->tp_name); return -1; } data->typeName = QByteArrayLiteral("QQmlListProperty"); auto &flags = data->flags; flags.setFlag(PySide::Property::PropertyFlag::Readable, true); flags.setFlag(PySide::Property::PropertyFlag::Designable, designable); flags.setFlag(PySide::Property::PropertyFlag::Scriptable, scriptable); flags.setFlag(PySide::Property::PropertyFlag::Stored, stored); flags.setFlag(PySide::Property::PropertyFlag::User, user); flags.setFlag(PySide::Property::PropertyFlag::Constant, constant); flags.setFlag(PySide::Property::PropertyFlag::Final, finalProp); return 0; } static PyTypeObject *createPropertyListType() { PyType_Slot PropertyListType_slots[] = { {Py_tp_new, reinterpret_cast(propList_tp_new)}, {Py_tp_init, reinterpret_cast(propListTpInit)}, {0, nullptr} }; PyType_Spec PropertyListType_spec = { "2:PySide6.QtQml.ListProperty", sizeof(PySideProperty), 0, Py_TPFLAGS_DEFAULT, PropertyListType_slots, }; Shiboken::AutoDecRef bases(Py_BuildValue("(O)", PySideProperty_TypeF())); return SbkType_FromSpecWithBases(&PropertyListType_spec, bases.object()); } PyTypeObject *PropertyList_TypeF(void) { // PYSIDE-2230: This was a wrong replacement by static AutoDecref. // Never do that, deletes things way too late. static PyTypeObject *type = createPropertyListType(); return type; } } // extern "C" // Implementation of QQmlListProperty::AppendFunction callback void QmlListPropertyPrivate::append(QQmlListProperty *propList, QObject *item) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(2)); PyTypeObject *qobjectType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); PyTuple_SetItem(args, 1, Shiboken::Conversions::pointerToPython(qobjectType, item)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obAppend, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::CountFunction callback qsizetype QmlListPropertyPrivate::count(QQmlListProperty *propList) const { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); auto *qobjType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjType, propList->object)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obCount, args)); // Check return type if (PyErr_Occurred()) { PyErr_Print(); return 0; } qsizetype cppResult = 0; auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); if (auto *pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, retVal)) pythonToCpp(retVal, &cppResult); return cppResult; } // Implementation of QQmlListProperty::AtFunction callback QObject *QmlListPropertyPrivate::at(QQmlListProperty *propList, qsizetype index) const { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(2)); PyTypeObject *qobjectType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); PyTuple_SetItem(args, 1, Shiboken::Conversions::copyToPython(converter, &index)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obAt, args)); QObject *result = nullptr; if (PyErr_Occurred()) PyErr_Print(); else if (PyType_IsSubtype(Py_TYPE(retVal), elementType)) Shiboken::Conversions::pythonToCppPointer(qobjectType, retVal, &result); return result; } // Implementation of QQmlListProperty::ClearFunction callback void QmlListPropertyPrivate::clear(QQmlListProperty * propList) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); PyTypeObject *qobjectType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obClear, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::ReplaceFunction callback void QmlListPropertyPrivate::replace(QQmlListProperty *propList, qsizetype index, QObject *value) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(3)); PyTypeObject *qobjectType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); PyTuple_SetItem(args, 1, Shiboken::Conversions::copyToPython(converter, &index)); PyTuple_SetItem(args, 2, Shiboken::Conversions::pointerToPython(qobjectType, value)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obReplace, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::RemoveLastFunction callback void QmlListPropertyPrivate::removeLast(QQmlListProperty *propList) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); PyTypeObject *qobjectType = PySide::qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); Shiboken::AutoDecRef retVal(PyObject_CallObject(obRemoveLast, args)); if (PyErr_Occurred()) PyErr_Print(); } static const char *PropertyList_SignatureStrings[] = { "PySide6.QtQml.ListProperty(self,type:type," "append:typing.Optional[collections.abc.Callable[...,typing.Any]]=None," "at:typing.Optional[collections.abc.Callable[...,typing.Any]]=None," "clear:typing.Optional[collections.abc.Callable[...,typing.Any]]=None," "count:typing.Optional[collections.abc.Callable[...,typing.Any]]=None)", nullptr // Sentinel }; namespace PySide::Qml { void initQtQmlListProperty(PyObject *module) { // Export QmlListProperty type if (InitSignatureStrings(PropertyList_TypeF(), PropertyList_SignatureStrings) < 0) { PyErr_Print(); qWarning() << "Error initializing PropertyList type."; return; } // Register QQmlListProperty metatype for use in QML qRegisterMetaType>(); auto *propertyListType = PropertyList_TypeF(); auto *obPropertyListType = reinterpret_cast(propertyListType); Py_INCREF(obPropertyListType); PepModule_AddType(module, propertyListType); } } // namespace PySide::Qml