diff options
Diffstat (limited to 'sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp')
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp | 311 |
1 files changed, 228 insertions, 83 deletions
diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 2b60c5c7f..fd470cd71 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -52,74 +52,34 @@ #include "pyside2_qtcore_python.h" #include "pyside2_qtqml_python.h" -#ifndef PYSIDE_MAX_QML_TYPES -// Maximum number of different Qt QML types the user can export to QML using -// qmlRegisterType. This limit exists because the QML engine instantiates objects -// by calling a function with one argument (a void *pointer where the object should -// be created), and thus does not allow us to choose which object to create. Thus -// we create a C++ factory function for each new registered type at compile time. -#define PYSIDE_MAX_QML_TYPES 50 -#endif +#include <QtQml/QJSValue> // Forward declarations. static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, void **args); -// All registered python types and their creation functions. -static PyObject *pyTypes[PYSIDE_MAX_QML_TYPES]; -static void (*createFuncs[PYSIDE_MAX_QML_TYPES])(void *); - // Mutex used to avoid race condition on PySide::nextQObjectMemoryAddr. static QMutex nextQmlElementMutex; -template<int N> -struct ElementFactoryBase +static void createInto(void *memory, void *type) { - static void createInto(void *memory) - { - QMutexLocker locker(&nextQmlElementMutex); - PySide::setNextQObjectMemoryAddr(memory); - Shiboken::GilState state; - PyObject *obj = PyObject_CallObject(pyTypes[N], 0); - if (!obj || PyErr_Occurred()) - PyErr_Print(); - PySide::setNextQObjectMemoryAddr(0); - } -}; - -template<int N> -struct ElementFactory : ElementFactoryBase<N> -{ - static void init() - { - createFuncs[N] = &ElementFactoryBase<N>::createInto; - ElementFactory<N-1>::init(); - } -}; - -template<> -struct ElementFactory<0> : ElementFactoryBase<0> -{ - static void init() - { - createFuncs[0] = &ElementFactoryBase<0>::createInto; - } -}; + QMutexLocker locker(&nextQmlElementMutex); + PySide::setNextQObjectMemoryAddr(memory); + Shiboken::GilState state; + PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), 0); + if (!obj || PyErr_Occurred()) + PyErr_Print(); + PySide::setNextQObjectMemoryAddr(0); +} int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, - int versionMinor, const char *qmlName) + int versionMinor, const char *qmlName, const char *noCreationReason, + bool creatable) { using namespace Shiboken; static PyTypeObject *qobjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); assert(qobjectType); - static int nextType = 0; - - if (nextType >= PYSIDE_MAX_QML_TYPES) { - PyErr_Format(PyExc_TypeError, "You can only export %d custom QML types to QML.", - PYSIDE_MAX_QML_TYPES); - return -1; - } PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj); if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) { @@ -132,15 +92,15 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, Q_ASSERT(metaObject); QQmlPrivate::RegisterType type; - type.version = 0; // Allow registering Qt Quick items. bool registered = false; #ifdef PYSIDE_QML_SUPPORT QuickRegisterItemFunction quickRegisterItemFunction = getQuickRegisterItemFunction(); if (quickRegisterItemFunction) { - registered = quickRegisterItemFunction(pyObj, uri, versionMajor, versionMinor, - qmlName, &type); + registered = + quickRegisterItemFunction(pyObj, uri, versionMajor, versionMinor, + qmlName, creatable, noCreationReason, &type); } #endif @@ -150,11 +110,11 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, // there's no way to unregister a QML type. Py_INCREF(pyObj); - pyTypes[nextType] = pyObj; + type.structVersion = 0; // FIXME: Fix this to assign new type ids each time. - type.typeId = qMetaTypeId<QObject *>(); - type.listId = qMetaTypeId<QQmlListProperty<QObject> >(); + type.typeId = QMetaType(QMetaType::QObjectStar); + type.listId = QMetaType::fromType<QQmlListProperty<QObject> >(); type.attachedPropertiesFunction = QQmlPrivate::attachedPropertiesFunc<QObject>(); type.attachedPropertiesMetaObject = QQmlPrivate::attachedPropertiesMetaObject<QObject>(); @@ -168,16 +128,16 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int objectSize = static_cast<int>(PySide::getSizeOfQObject( reinterpret_cast<SbkObjectType *>(pyObj))); type.objectSize = objectSize; - type.create = createFuncs[nextType]; + type.create = creatable ? createInto : nullptr; + type.noCreationReason = noCreationReason; + type.userdata = pyObj; type.uri = uri; - type.versionMajor = versionMajor; - type.versionMinor = versionMinor; + type.version = QTypeRevision::fromVersion(versionMajor, versionMinor); type.elementName = qmlName; type.extensionObjectCreate = 0; type.extensionMetaObject = 0; type.customParser = 0; - ++nextType; } type.metaObject = metaObject; // Snapshot may have changed. @@ -189,6 +149,126 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, return qmlTypeId; } +int PySide::qmlRegisterSingletonType(PyObject *pyObj, const char *uri, int versionMajor, + int versionMinor, const char *qmlName, PyObject *callback, + bool isQObject, bool hasCallback) +{ + using namespace Shiboken; + + if (hasCallback) { + if (!PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, "Invalid callback specified."); + return -1; + } + + AutoDecRef funcCode(PyObject_GetAttrString(callback, "__code__")); + AutoDecRef argCount(PyObject_GetAttrString(funcCode, "co_argcount")); + + int count = PyInt_AsLong(argCount); + + if (count != 1) { + PyErr_Format(PyExc_TypeError, "Callback has a bad parameter count."); + return -1; + } + + // Make sure the callback never gets deallocated + Py_INCREF(callback); + } + + const QMetaObject *metaObject = nullptr; + + if (isQObject) { + static PyTypeObject *qobjectType = Conversions::getPythonTypeObject("QObject*"); + assert(qobjectType); + + PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj); + if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) { + PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.", + qobjectType->tp_name, pyObjType->tp_name); + return -1; + } + + // If we don't have a callback we'll need the pyObj to stay allocated indefinitely + if (!hasCallback) + Py_INCREF(pyObj); + + metaObject = PySide::retrieveMetaObject(pyObjType); + Q_ASSERT(metaObject); + } + + QQmlPrivate::RegisterSingletonType type; + type.structVersion = 0; + + type.uri = uri; + type.version = QTypeRevision::fromVersion(versionMajor, versionMinor); + type.typeName = qmlName; + type.instanceMetaObject = metaObject; + + if (isQObject) { + // FIXME: Fix this to assign new type ids each time. + type.typeId = QMetaType(QMetaType::QObjectStar); + + type.qObjectApi = + [callback, pyObj, hasCallback](QQmlEngine *engine, QJSEngine *) -> QObject * { + AutoDecRef args(PyTuple_New(hasCallback ? 1 : 0)); + + if (hasCallback) { + PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython( + (SbkObjectType *)SbkPySide2_QtQmlTypes[SBK_QQMLENGINE_IDX], + engine)); + } + + AutoDecRef retVal(PyObject_CallObject(hasCallback ? callback : pyObj, args)); + + SbkObjectType *qobjectType = (SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX]; + + // Make sure the callback returns something we can convert, else the entire application will crash. + if (retVal.isNull() || + Conversions::isPythonToCppPointerConvertible(qobjectType, retVal) == nullptr) { + PyErr_Format(PyExc_TypeError, "Callback returns invalid value."); + return nullptr; + } + + QObject *obj = nullptr; + Conversions::pythonToCppPointer(qobjectType, retVal, &obj); + + if (obj != nullptr) + Py_INCREF(retVal); + + return obj; + }; + } else { + type.scriptApi = + [callback](QQmlEngine *engine, QJSEngine *) -> QJSValue { + AutoDecRef args(PyTuple_New(1)); + + PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython( + (SbkObjectType *)SbkPySide2_QtQmlTypes[SBK_QQMLENGINE_IDX], + engine)); + + AutoDecRef retVal(PyObject_CallObject(callback, args)); + + SbkObjectType *qjsvalueType = (SbkObjectType *)SbkPySide2_QtQmlTypes[SBK_QJSVALUE_IDX]; + + // Make sure the callback returns something we can convert, else the entire application will crash. + if (retVal.isNull() || + Conversions::isPythonToCppPointerConvertible(qjsvalueType, retVal) == nullptr) { + PyErr_Format(PyExc_TypeError, "Callback returns invalid value."); + return QJSValue(QJSValue::UndefinedValue); + } + + QJSValue *val = nullptr; + Conversions::pythonToCppPointer(qjsvalueType, retVal, &val); + + Py_INCREF(retVal); + + return *val; + }; + } + + return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &type); +} + extern "C" { @@ -197,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); @@ -333,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) { @@ -342,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]; @@ -367,18 +509,23 @@ QtQml_VolatileBoolObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) QtQml_VolatileBoolObject *self = reinterpret_cast<QtQml_VolatileBoolObject *>(type->tp_alloc(type, 0)); - if (self != Q_NULLPTR) - self->flag = ok; + if (self != nullptr) + self->flag = new AtomicBool(ok); return reinterpret_cast<PyObject *>(self); } +static void QtQml_VolatileBoolObject_dealloc(PyObject *self) +{ + auto volatileBool = reinterpret_cast<QtQml_VolatileBoolObject *>(self); + delete volatileBool->flag; + Sbk_object_dealloc(self); +} + static PyObject * QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject *self) { - if (self->flag) - return Py_True; - return Py_False; + return *self->flag ? Py_True : Py_False; } static PyObject * @@ -397,10 +544,7 @@ QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject *self, PyObject *args) return Q_NULLPTR; } - if (ok > 0) - self->flag = true; - else - self->flag = false; + *self->flag = ok > 0; Py_RETURN_NONE; } @@ -420,7 +564,7 @@ QtQml_VolatileBoolObject_repr(QtQml_VolatileBoolObject *self) { PyObject *s; - if (self->flag) + if (*self->flag) s = PyBytes_FromFormat("%s(True)", Py_TYPE(self)->tp_name); else @@ -435,12 +579,12 @@ QtQml_VolatileBoolObject_str(QtQml_VolatileBoolObject *self) { PyObject *s; - if (self->flag) + if (*self->flag) s = PyBytes_FromFormat("%s(True) -> %p", - Py_TYPE(self)->tp_name, &(self->flag)); + Py_TYPE(self)->tp_name, self->flag); else s = PyBytes_FromFormat("%s(False) -> %p", - Py_TYPE(self)->tp_name, &(self->flag)); + Py_TYPE(self)->tp_name, self->flag); Py_XINCREF(s); return s; } @@ -450,7 +594,7 @@ static PyType_Slot QtQml_VolatileBoolType_slots[] = { {Py_tp_str, (void *)reinterpret_cast<reprfunc>(QtQml_VolatileBoolObject_str)}, {Py_tp_methods, (void *)QtQml_VolatileBoolObject_methods}, {Py_tp_new, (void *)QtQml_VolatileBoolObject_new}, - {Py_tp_dealloc, (void *)Sbk_object_dealloc}, + {Py_tp_dealloc, (void *)QtQml_VolatileBoolObject_dealloc}, {0, 0} }; static PyType_Spec QtQml_VolatileBoolType_spec = { @@ -481,8 +625,6 @@ static const char *VolatileBool_SignatureStrings[] = { void PySide::initQmlSupport(PyObject *module) { - ElementFactory<PYSIDE_MAX_QML_TYPES - 1>::init(); - // Export QmlListProperty type if (InitSignatureStrings(PropertyListTypeF(), PropertyList_SignatureStrings) < 0) { PyErr_Print(); @@ -490,6 +632,9 @@ void PySide::initQmlSupport(PyObject *module) return; } + // Register QQmlListProperty metatype for use in QML + qRegisterMetaType<QQmlListProperty<QObject>>(); + Py_INCREF(reinterpret_cast<PyObject *>(PropertyListTypeF())); PyModule_AddObject(module, PepType_GetNameStr(PropertyListTypeF()), reinterpret_cast<PyObject *>(PropertyListTypeF())); |