aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpysideqml/pysideqmlregistertype.cpp')
-rw-r--r--sources/pyside6/libpysideqml/pysideqmlregistertype.cpp707
1 files changed, 489 insertions, 218 deletions
diff --git a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
index 26398ae76..4ccd459d5 100644
--- a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
+++ b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
@@ -6,8 +6,10 @@
#include "pysideqmltypeinfo_p.h"
#include "pysideqmlattached_p.h"
#include "pysideqmlextended_p.h"
+#include "pysideqmluncreatable.h"
#include <limits>
+#include <optional>
// shiboken
#include <shiboken.h>
@@ -16,6 +18,7 @@
// pyside
#include <pyside.h>
#include <pysideqobject.h>
+#include <pysideclassinfo.h>
#include <pyside_p.h>
#include <QtCore/QMutex>
@@ -25,9 +28,16 @@
#include <QtQml/QJSValue>
#include <QtQml/QQmlListProperty>
#include <private/qqmlmetatype_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+
+#include <memory>
+
+using namespace Qt::StringLiterals;
static PySide::Qml::QuickRegisterItemFunction quickRegisterItemFunction = nullptr;
+static const auto qmlElementKey = "QML.Element"_ba;
+
static void createInto(void *memory, void *type)
{
QMutexLocker locker(&PySide::nextQObjectMemoryAddrMutex());
@@ -85,49 +95,154 @@ static inline bool isQmlParserStatus(const QMetaObject *o)
return inheritsFrom(o, "QPyQmlParserStatus");
}
-namespace PySide::Qml {
+static QByteArray getGlobalString(const char *name)
+{
+ PyObject *globalVar = PyDict_GetItemString(PyEval_GetGlobals(), name);
-int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName, const char *noCreationReason,
- bool creatable)
+ if (globalVar == nullptr || PyUnicode_Check(globalVar) == 0)
+ return {};
+
+ const char *stringValue = _PepUnicode_AsString(globalVar);
+ return stringValue != nullptr ? QByteArray(stringValue) : QByteArray{};
+}
+
+static int getGlobalInt(const char *name)
{
- using namespace Shiboken;
+ PyObject *globalVar = PyDict_GetItemString(PyEval_GetGlobals(), name);
- PyTypeObject *qobjectType = qObjectType();
+ if (globalVar == nullptr || PyLong_Check(globalVar) == 0)
+ return -1;
- 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);
+ long value = PyLong_AsLong(globalVar);
+
+ if (value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min())
return -1;
+
+ return value;
+}
+
+struct ImportData
+{
+ QByteArray importName;
+ int majorVersion = 0;
+ int minorVersion = 0;
+
+ QTypeRevision toTypeRevision() const;
+};
+
+QTypeRevision ImportData::toTypeRevision() const
+{
+ return QTypeRevision::fromVersion(majorVersion, minorVersion);
+}
+
+std::optional<ImportData> getGlobalImportData(const char *decoratorName)
+{
+ ImportData result{getGlobalString("QML_IMPORT_NAME"),
+ getGlobalInt("QML_IMPORT_MAJOR_VERSION"),
+ getGlobalInt("QML_IMPORT_MINOR_VERSION")};
+
+ if (result.importName.isEmpty()) {
+ PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_NAME in order to use %s.",
+ decoratorName);
+ return {};
}
- const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
- Q_ASSERT(metaObject);
+ if (result.majorVersion == -1) {
+ PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_MAJOR_VERSION in order to use %s.",
+ decoratorName);
+ return {};
+ }
- QQmlPrivate::RegisterType type;
+ // Specifying a minor version is optional
+ if (result.minorVersion == -1)
+ result.minorVersion = 0;
+ return result;
+}
- // Allow registering Qt Quick items.
- const bool isQuickType = quickRegisterItemFunction && quickRegisterItemFunction(pyObj, &type);
+static PyTypeObject *checkTypeObject(PyObject *pyObj, const char *what)
+{
+ if (PyType_Check(pyObj) == 0) {
+ PyErr_Format(PyExc_TypeError, "%s can only be used for classes.", what);
+ return nullptr;
+ }
+ return reinterpret_cast<PyTypeObject *>(pyObj);
+}
+
+static bool setClassInfo(PyTypeObject *type, const QByteArray &key, const QByteArray &value)
+{
+ if (!PySide::ClassInfo::setClassInfo(type, key, value)) {
+ PyErr_Format(PyExc_TypeError, "Setting class info \"%s\" to \"%s\" on \"%s\" failed.",
+ key.constData(), value.constData(), type->tp_name);
+ return false;
+ }
+ return true;
+}
+
+static inline bool setSingletonClassInfo(PyTypeObject *type)
+{
+ return setClassInfo(type, "QML.Singleton"_ba, "true"_ba);
+}
+
+static QQmlCustomParser *defaultCustomParserFactory()
+{
+ return nullptr;
+}
+
+namespace PySide::Qml {
+
+// Modern (6.7) type registration using RegisterTypeAndRevisions
+// and information set to QMetaClassInfo.
+static int qmlRegisterType(PyObject *pyObj,
+ const ImportData &importData,
+ const QMetaObject *metaObject,
+ const QMetaObject *classInfoMetaObject = nullptr)
+{
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+
+ if (classInfoMetaObject == nullptr)
+ classInfoMetaObject = metaObject;
// Register as simple QObject rather than Qt Quick item.
// Incref the type object, don't worry about decref'ing it because
// there's no way to unregister a QML type.
Py_INCREF(pyObj);
- type.structVersion = 0;
-
const QByteArray typeName(pyObjType->tp_name);
QByteArray ptrType = typeName + '*';
QByteArray listType = QByteArrayLiteral("QQmlListProperty<") + typeName + '>';
+ const auto typeId = QMetaType(new QQmlMetaTypeInterface(ptrType));
+ const auto listId = QMetaType(new QQmlListMetaTypeInterface(listType, typeId.iface()));
+ const int objectSize = static_cast<int>(PySide::getSizeOfQObject(reinterpret_cast<PyTypeObject *>(pyObj)));
- type.typeId = QMetaType(new QQmlMetaTypeInterface(ptrType));
- type.listId = QMetaType(new QQmlListMetaTypeInterface(listType,
- type.typeId.iface()));
const auto typeInfo = qmlTypeInfo(pyObj);
- auto info = qmlAttachedInfo(pyObjType, typeInfo);
- type.attachedPropertiesFunction = info.factory;
- type.attachedPropertiesMetaObject = info.metaObject;
+ const auto attachedInfo = qmlAttachedInfo(pyObjType, typeInfo);
+ const auto extendedInfo = qmlExtendedInfo(pyObj, typeInfo);
+
+ QList<int> ids;
+ QQmlPrivate::RegisterTypeAndRevisions type {
+ QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
+ typeId, listId, objectSize,
+ createInto, // create
+ pyObj, // userdata
+ nullptr, // createValueType (Remove in Qt 7)
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ metaObject,
+ classInfoMetaObject,
+ attachedInfo.factory, // attachedPropertiesFunction
+ attachedInfo.metaObject, // attachedPropertiesMetaObject
+ 0, 0, 0, // parserStatusCast, valueSourceCast, valueInterceptorCast
+ extendedInfo.factory, // extensionObjectCreate
+ extendedInfo.metaObject, // extensionMetaObject
+ defaultCustomParserFactory, // customParser
+ &ids, // qmlTypeIds
+ 0, // finalizerCast
+ false, // forceAnonymous
+ {} // listMetaSequence
+ };
+
+ // Allow registering Qt Quick items.
+ const bool isQuickType = quickRegisterItemFunction && quickRegisterItemFunction(pyObj, &type);
if (!isQuickType) { // values filled by the Quick registration
// QPyQmlParserStatus inherits QObject, QQmlParserStatus, so,
@@ -143,55 +258,245 @@ int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor,
QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueInterceptor>::cast();
}
- int objectSize = static_cast<int>(PySide::getSizeOfQObject(
- reinterpret_cast<PyTypeObject *>(pyObj)));
- type.objectSize = objectSize;
- type.create = creatable ? createInto : nullptr;
- type.noCreationReason = QString::fromUtf8(noCreationReason);
- type.userdata = pyObj;
- type.uri = uri;
- type.version = QTypeRevision::fromVersion(versionMajor, versionMinor);
- type.elementName = qmlName;
-
- info = qmlExtendedInfo(pyObj, typeInfo);
- type.extensionObjectCreate = info.factory;
- type.extensionMetaObject = info.metaObject;
- type.customParser = 0;
- type.metaObject = metaObject; // Snapshot may have changed.
-
- int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+ QQmlPrivate::qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type);
+ const int qmlTypeId = ids.value(0, -1);
if (qmlTypeId == -1) {
PyErr_Format(PyExc_TypeError, "QML meta type registration of \"%s\" failed.",
- qmlName);
+ typeName.constData());
}
return qmlTypeId;
}
-int qmlRegisterSingletonType(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName, PyObject *callback,
- bool isQObject, bool hasCallback)
+static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
+ const ImportData &importData)
{
- using namespace Shiboken;
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ if (!isQObjectDerived(pyObjType, true))
+ return -1;
- if (hasCallback) {
- if (!PyCallable_Check(callback)) {
- PyErr_Format(PyExc_TypeError, "Invalid callback specified.");
- return -1;
- }
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
+ Q_ASSERT(metaObject);
+ const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
+ ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+ return qmlRegisterType(pyObj, importData, metaObject, classInfoMetaObject);
+}
- AutoDecRef funcCode(PyObject_GetAttrString(callback, "__code__"));
- AutoDecRef argCount(PyObject_GetAttrString(funcCode, "co_argcount"));
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const char *noCreationReason,
+ bool creatable)
+{
+ auto *type = checkTypeObject(pyObj, "qmlRegisterType()");
+ if (type == nullptr || !PySide::isQObjectDerived(type, true))
+ return -1;
- int count = PyLong_AsLong(argCount);
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(type);
+ Q_ASSERT(metaObject);
- if (count != 1) {
- PyErr_Format(PyExc_TypeError, "Callback has a bad parameter count.");
- return -1;
+ // PYSIDE-2709: Use a separate QMetaObject for the class information
+ // as modifying metaObject breaks inheritance.
+ QMetaObjectBuilder classInfobuilder(&QObject::staticMetaObject);
+ classInfobuilder.addClassInfo(qmlElementKey, qmlName);
+ if (!creatable)
+ setUncreatableClassInfo(&classInfobuilder, noCreationReason);
+ auto *classInfoMetaObject = classInfobuilder.toMetaObject();
+
+ const int qmlTypeId = qmlRegisterType(pyObj, {uri, versionMajor, versionMinor},
+ metaObject, classInfoMetaObject);
+ free(classInfoMetaObject);
+ return qmlTypeId;
+}
+
+// Singleton helpers
+
+// Check the arguments of a singleton callback (C++: "QJSValue cb(QQmlEngine *, QJSEngine *)",
+// but we drop the QJSEngine since it will be the same as QQmlEngine when the latter exists.
+static bool checkSingletonCallback(PyObject *callback)
+{
+ if (callback == nullptr) {
+ PyErr_SetString(PyExc_TypeError, "No callback specified.");
+ return false;
+ }
+ if (PyCallable_Check(callback) == 0) {
+ PyErr_Format(PyExc_TypeError, "Invalid callback specified (%S).", callback);
+ return false;
+ }
+ Shiboken::AutoDecRef funcCode(PyObject_GetAttrString(callback, "__code__"));
+ if (funcCode.isNull()) {
+ PyErr_Format(PyExc_TypeError, "Cannot retrieve code of callback (%S).", callback);
+ return false;
+ }
+ Shiboken::AutoDecRef argCountAttr(PyObject_GetAttrString(funcCode, "co_argcount"));
+ const int argCount = PyLong_AsLong(argCountAttr.object());
+ if (argCount != 1) {
+ PyErr_Format(PyExc_TypeError, "Callback (%S) has %d parameter(s), expected one.",
+ callback, argCount);
+ return false;
+ }
+
+ return true;
+}
+
+// Shared data of a singleton creation callback which dereferences an object on
+// destruction.
+class SingletonQObjectCreationSharedData
+{
+public:
+ Q_DISABLE_COPY_MOVE(SingletonQObjectCreationSharedData)
+
+ SingletonQObjectCreationSharedData(PyObject *cb, PyObject *ref = nullptr) noexcept :
+ callable(cb), reference(ref)
+ {
+ Py_XINCREF(ref);
+ }
+
+ // FIXME: Currently, the QML registration data are in global static variables
+ // and thus cleaned up after Python terminates. Once they are cleaned up
+ // by the QML engine, the code can be activated for proper cleanup of the references.
+ ~SingletonQObjectCreationSharedData()
+#if 0 //
+ ~SingletonQObjectCreationSharedData()
+ {
+ if (reference != nullptr) {
+ Shiboken::GilState gil;
+ Py_DECREF(reference);
}
+ }
+#else
+ = default;
+#endif
+
+ PyObject *callable{}; // Callback, static method or type object to be invoked.
+ PyObject *reference{}; // Object to dereference when going out scope
+};
+
+// Base class for QML singleton creation callbacks with helper for error checking.
+class SingletonQObjectCreationBase
+{
+protected:
+ explicit SingletonQObjectCreationBase(PyObject *cb, PyObject *ref = nullptr) :
+ m_data(std::make_shared<SingletonQObjectCreationSharedData>(cb, ref))
+ {
+ }
+
+ static QObject *handleReturnValue(PyObject *retVal);
- // Make sure the callback never gets deallocated
- Py_INCREF(callback);
+ std::shared_ptr<SingletonQObjectCreationSharedData> data() const { return m_data; }
+
+private:
+ std::shared_ptr<SingletonQObjectCreationSharedData> m_data;
+};
+
+QObject *SingletonQObjectCreationBase::handleReturnValue(PyObject *retVal)
+{
+ using Shiboken::Conversions::isPythonToCppPointerConvertible;
+ // Make sure the callback returns something we can convert, else the entire application will crash.
+ if (retVal == nullptr) {
+ PyErr_Format(PyExc_TypeError, "Callback returns 0 value.");
+ return nullptr;
}
+ if (isPythonToCppPointerConvertible(qObjectType(), retVal) == nullptr) {
+ PyErr_Format(PyExc_TypeError, "Callback returns invalid value (%S).", retVal);
+ return nullptr;
+ }
+
+ QObject *obj = nullptr;
+ Shiboken::Conversions::pythonToCppPointer(qObjectType(), retVal, &obj);
+ return obj;
+}
+
+// QML singleton creation callback by invoking a type object
+class SingletonQObjectFromTypeCreation : public SingletonQObjectCreationBase
+{
+public:
+ explicit SingletonQObjectFromTypeCreation(PyObject *typeObj) :
+ SingletonQObjectCreationBase(typeObj, typeObj) {}
+
+ QObject *operator ()(QQmlEngine *, QJSEngine *) const
+ {
+ Shiboken::GilState gil;
+ Shiboken::AutoDecRef args(PyTuple_New(0));
+ PyObject *retVal = PyObject_CallObject(data()->callable, args);
+ QObject *result = handleReturnValue(retVal);
+ if (result == nullptr)
+ Py_XDECREF(retVal);
+ return result;
+ }
+};
+
+// QML singleton creation by invoking a callback, passing QQmlEngine. Keeps a
+// references to the the callback.
+class SingletonQObjectCallbackCreation : public SingletonQObjectCreationBase
+{
+public:
+ explicit SingletonQObjectCallbackCreation(PyObject *callback) :
+ SingletonQObjectCreationBase(callback, callback) {}
+ explicit SingletonQObjectCallbackCreation(PyObject *callback, PyObject *ref) :
+ SingletonQObjectCreationBase(callback, ref) {}
+
+ QObject *operator ()(QQmlEngine *engine, QJSEngine *) const
+ {
+ Shiboken::GilState gil;
+ Shiboken::AutoDecRef args(PyTuple_New(1));
+ PyTuple_SET_ITEM(args, 0,
+ Shiboken::Conversions::pointerToPython(qQmlEngineType(), engine));
+ PyObject *retVal = PyObject_CallObject(data()->callable, args);
+ QObject *result = handleReturnValue(retVal);
+ if (result == nullptr)
+ Py_XDECREF(retVal);
+ return result;
+ }
+};
+
+using SingletonQObjectCreation = std::function<QObject*(QQmlEngine *, QJSEngine *)>;
+
+// Modern (6.7) singleton type registration using RegisterSingletonTypeAndRevisions
+// and information set to QMetaClassInfo (QObject only pending QTBUG-110467).
+static int qmlRegisterSingletonTypeV2(PyObject *pyObj, PyObject *pyClassInfoObj,
+ const ImportData &importData,
+ const SingletonQObjectCreation &callback)
+{
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ if (!isQObjectDerived(pyObjType, true))
+ return -1;
+
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
+ Q_ASSERT(metaObject);
+ const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
+ ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+
+ QList<int> ids;
+ QQmlPrivate::RegisterSingletonTypeAndRevisions type {
+ QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ callback, // qObjectApi,
+ metaObject,
+ classInfoMetaObject,
+ QMetaType(QMetaType::QObjectStar), // typeId
+ nullptr, // extensionMetaObject
+ nullptr, // extensionObjectCreate
+ &ids
+ };
+
+ QQmlPrivate::qmlregister(QQmlPrivate::SingletonAndRevisionsRegistration, &type);
+ const int qmlTypeId = ids.value(0, -1);
+ if (qmlTypeId == -1) {
+ PyErr_Format(PyExc_TypeError, "Singleton QML meta type registration of \"%s\" failed.",
+ pyObjType->tp_name);
+ }
+ return qmlTypeId;
+}
+
+// Legacy (pre 6.7) singleton type registration using RegisterSingletonType
+// for QObject and value types. Still used by qmlRegisterSingletonType()
+// for the hypothetical case of a value type.
+static int qmlRegisterSingletonType(PyObject *pyObj, const ImportData &importData,
+ const char *qmlName, PyObject *callback,
+ bool isQObject, bool hasCallback)
+{
+ if (hasCallback && !checkSingletonCallback(callback))
+ return -1;
const QMetaObject *metaObject = nullptr;
@@ -201,56 +506,37 @@ int qmlRegisterSingletonType(PyObject *pyObj, const char *uri, int versionMajor,
if (!isQObjectDerived(pyObjType, true))
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;
+ QQmlPrivate::RegisterSingletonType type {
+ QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ qmlName, // typeName
+ {}, // scriptApi
+ {}, // qObjectApi
+ metaObject, // instanceMetaObject
+ {}, // typeId
+ nullptr, // extensionMetaObject
+ nullptr, // extensionObjectCreate
+ {} // revision
+ };
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 * {
- Shiboken::GilState gil;
- AutoDecRef args(PyTuple_New(hasCallback ? 1 : 0));
-
- if (hasCallback) {
- PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython(
- qQmlEngineType(), engine));
- }
-
- AutoDecRef retVal(PyObject_CallObject(hasCallback ? callback : pyObj, args));
-
- // 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;
- };
+ if (hasCallback)
+ type.qObjectApi = SingletonQObjectCallbackCreation(callback);
+ else
+ type.qObjectApi = SingletonQObjectFromTypeCreation(pyObj);
} else {
type.scriptApi =
[callback](QQmlEngine *engine, QJSEngine *) -> QJSValue {
+ using namespace Shiboken;
+
Shiboken::GilState gil;
AutoDecRef args(PyTuple_New(1));
@@ -280,9 +566,19 @@ int qmlRegisterSingletonType(PyObject *pyObj, const char *uri, int versionMajor,
return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &type);
}
-int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName,
- PyObject *instanceObject)
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterSingletonType(PyObject *pyObj,const char *uri,
+ int versionMajor, int versionMinor, const char *qmlName,
+ PyObject *callback, bool isQObject, bool hasCallback)
+{
+ return qmlRegisterSingletonType(pyObj, {uri, versionMajor, versionMinor}, qmlName,
+ callback, isQObject, hasCallback);
+}
+
+// Modern (6.7) singleton instance registration using RegisterSingletonTypeAndRevisions
+// and information set to QMetaClassInfo (QObject only).
+static int qmlRegisterSingletonInstance(PyObject *pyObj, const ImportData &importData,
+ PyObject *instanceObject)
{
using namespace Shiboken;
@@ -305,165 +601,140 @@ int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMa
const QMetaObject *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;
-
- // FIXME: Fix this to assign new type ids each time.
- type.typeId = QMetaType(QMetaType::QObjectStar);
- type.qObjectApi = registrationFunctor;
-
-
- return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &type);
+ QList<int> ids;
+ QQmlPrivate::RegisterSingletonTypeAndRevisions type {
+ QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ registrationFunctor, // qObjectApi,
+ metaObject,
+ metaObject, // classInfoMetaObject
+ QMetaType(QMetaType::QObjectStar), // typeId
+ nullptr, // extensionMetaObject
+ nullptr, // extensionObjectCreate
+ &ids
+ };
+
+ QQmlPrivate::qmlregister(QQmlPrivate::SingletonAndRevisionsRegistration, &type);
+ return ids.value(0, -1);
}
-} // namespace PySide::Qml
-
-static std::string getGlobalString(const char *name)
-{
- using Shiboken::AutoDecRef;
-
- PyObject *globals = PyEval_GetGlobals();
-
- AutoDecRef pyName(Py_BuildValue("s", name));
-
- PyObject *globalVar = PyDict_GetItem(globals, pyName);
-
- if (globalVar == nullptr || !PyUnicode_Check(globalVar))
- return "";
-
- const char *stringValue = _PepUnicode_AsString(globalVar);
- return stringValue != nullptr ? stringValue : "";
-}
-
-static int getGlobalInt(const char *name)
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMajor,
+ int versionMinor, const char *qmlName,
+ PyObject *instanceObject)
{
- using Shiboken::AutoDecRef;
-
- PyObject *globals = PyEval_GetGlobals();
-
- AutoDecRef pyName(Py_BuildValue("s", name));
-
- PyObject *globalVar = PyDict_GetItem(globals, pyName);
-
- if (globalVar == nullptr || !PyLong_Check(globalVar))
- return -1;
-
- long value = PyLong_AsLong(globalVar);
-
- if (value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min())
+ auto *type = checkTypeObject(pyObj, "qmlRegisterSingletonInstance()");
+ if (type == nullptr || !setClassInfo(type, qmlElementKey, qmlName)
+ || !setSingletonClassInfo(type)) {
return -1;
-
- return value;
+ }
+ return qmlRegisterSingletonInstance(pyObj, {uri, versionMajor, versionMinor},
+ instanceObject);
}
+} // namespace PySide::Qml
+
enum class RegisterMode {
Normal,
- Anonymous,
- Uncreatable,
Singleton
};
-static PyObject *qmlElementMacroHelper(PyObject *pyObj,
- const char *decoratorName,
- const char *typeName = nullptr,
- RegisterMode mode = RegisterMode::Normal,
- const char *noCreationReason = nullptr)
+namespace PySide::Qml {
+
+// Check for a static create() method on a decorated singleton.
+// Might set a Python error if the check fails.
+static std::optional<SingletonQObjectCreation>
+ singletonCreateMethod(PyTypeObject *pyObjType)
{
- if (!PyType_Check(pyObj)) {
- PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes.");
- return nullptr;
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(pyObjType));
+ auto *create = PyDict_GetItemString(tpDict.object(), "create");
+ // Method decorated by "@staticmethod"
+ if (create == nullptr || std::strcmp(Py_TYPE(create)->tp_name, "staticmethod") != 0)
+ return std::nullopt;
+ // 3.10: "__wrapped__"
+ Shiboken::AutoDecRef function(PyObject_GetAttrString(create, "__func__"));
+ if (function.isNull()) {
+ PyErr_Format(PyExc_TypeError, "Cannot retrieve function of callback (%S).",
+ create);
+ return std::nullopt;
}
+ if (!checkSingletonCallback(function.object()))
+ return std::nullopt;
+ // Reference to the type needs to be kept.
+ return SingletonQObjectCallbackCreation(function.object(),
+ reinterpret_cast<PyObject *>(pyObjType));
+}
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
- if (typeName == nullptr)
- typeName = pyObjType->tp_name;
- if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qObjectType()))) {
- PyErr_Format(PyExc_TypeError, "This decorator can only be used with classes inherited from QObject, got %s.",
- typeName);
+PyObject *qmlElementMacro(PyObject *pyObj, const char *decoratorName,
+ const QByteArray &typeName)
+{
+ auto *pyObjType = checkTypeObject(pyObj, decoratorName);
+ if (pyObjType == nullptr)
return nullptr;
- }
- std::string importName = getGlobalString("QML_IMPORT_NAME");
- int majorVersion = getGlobalInt("QML_IMPORT_MAJOR_VERSION");
- int minorVersion = getGlobalInt("QML_IMPORT_MINOR_VERSION");
-
- if (importName.empty()) {
- PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_NAME in order to use %s.",
- decoratorName);
+ if (!PySide::isQObjectDerived(pyObjType, false)) {
+ PyErr_Format(PyExc_TypeError,
+ "%s can only be used with classes inherited from QObject, got %s.",
+ decoratorName, pyObjType->tp_name);
return nullptr;
}
- if (majorVersion == -1) {
- PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_MAJOR_VERSION in order to use %s.",
- decoratorName);
- return nullptr;
- }
-
- // Specifying a minor version is optional
- if (minorVersion == -1)
- minorVersion = 0;
-
- const char *uri = importName.c_str();
- const int result = mode == RegisterMode::Singleton
- ? PySide::Qml::qmlRegisterSingletonType(pyObj, uri, majorVersion, minorVersion,
- typeName, nullptr,
- PySide::isQObjectDerived(pyObjType, false),
- false)
- : PySide::Qml::qmlRegisterType(pyObj, uri, majorVersion, minorVersion,
- mode != RegisterMode::Anonymous ? typeName : nullptr,
- noCreationReason,
- mode == RegisterMode::Normal);
-
- if (result == -1) {
- PyErr_Format(PyExc_TypeError, "%s: Failed to register type %s.",
- decoratorName, typeName);
- }
-
- return pyObj;
-}
-
-namespace PySide::Qml {
+ if (!setClassInfo(pyObjType, qmlElementKey, typeName))
+ return nullptr;
-PyObject *qmlElementMacro(PyObject *pyObj, const char *decoratorName,
- const char *typeName = nullptr)
-{
RegisterMode mode = RegisterMode::Normal;
- const char *noCreationReason = nullptr;
const auto info = PySide::Qml::qmlTypeInfo(pyObj);
auto *registerObject = pyObj;
- if (!info.isNull()) {
- if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Singleton))
+ if (info) {
+ if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Singleton)) {
mode = RegisterMode::Singleton;
- else if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Uncreatable))
- mode = RegisterMode::Uncreatable;
- noCreationReason = info->noCreationReason.c_str();
+ setSingletonClassInfo(pyObjType);
+ }
if (info->foreignType)
registerObject = reinterpret_cast<PyObject *>(info->foreignType);
}
- if (!qmlElementMacroHelper(registerObject, decoratorName, typeName, mode, noCreationReason))
+
+ const auto importDataO = getGlobalImportData(decoratorName);
+ if (!importDataO.has_value())
+ return nullptr;
+ const auto importData = importDataO.value();
+
+ int result{};
+ if (mode == RegisterMode::Singleton) {
+ auto singletonCreateMethodO = singletonCreateMethod(pyObjType);
+ if (!singletonCreateMethodO.has_value()) {
+ if (PyErr_Occurred() != nullptr)
+ return nullptr;
+ singletonCreateMethodO = SingletonQObjectFromTypeCreation(pyObj);
+ }
+ result = PySide::Qml::qmlRegisterSingletonTypeV2(registerObject, pyObj, importData,
+ singletonCreateMethodO.value());
+ } else {
+ result = PySide::Qml::qmlRegisterType(registerObject, pyObj, importData);
+ }
+ if (result == -1) {
+ PyErr_Format(PyExc_TypeError, "%s: Failed to register type %s.",
+ decoratorName, pyObjType->tp_name);
return nullptr;
+ }
+
return pyObj;
}
PyObject *qmlElementMacro(PyObject *pyObj)
{
- return qmlElementMacro(pyObj, "QmlElement");
+ return qmlElementMacro(pyObj, "QmlElement", "auto"_ba);
}
-PyObject *qmlNamedElementMacro(PyObject *pyObj, const char *typeName)
+PyObject *qmlNamedElementMacro(PyObject *pyObj, const QByteArray &typeName)
{
- return qmlElementMacro(pyObj, "QmlNamedElement", qstrdup(typeName));
+ return qmlElementMacro(pyObj, "QmlNamedElement", typeName);
}
PyObject *qmlAnonymousMacro(PyObject *pyObj)
{
- return qmlElementMacroHelper(pyObj, "QmlAnonymous", nullptr,
- RegisterMode::Anonymous);
+ return qmlElementMacro(pyObj, "QmlAnonymous", "anonymous"_ba);
}
PyObject *qmlSingletonMacro(PyObject *pyObj)