From 13d5bffca41178f969f06a0ce892edc8695ad41a Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 15 Nov 2021 09:37:51 +0100 Subject: Add a QmlSingleton decorator Add a simple decorator function that stores the type in a list for the QmlElement decorator to use singleton registration. Task-number: PYSIDE-1709 Change-Id: I075d583404bd60dc52b84c23a3d09e50d32a5a3a Reviewed-by: Cristian Maureira-Fredes (cherry picked from commit 671f9ed73ee174fe2ba96d7a1c2b267455ef367e) Reviewed-by: Qt Cherry-pick Bot --- .../PySide6/QtQml/pysideqmlregistertype.cpp | 41 +++++++++++++++++----- .../pyside6/PySide6/QtQml/pysideqmlregistertype.h | 4 +++ sources/pyside6/PySide6/QtQml/typesystem_qml.xml | 4 +++ sources/pyside6/PySide6/glue/qtqml.cpp | 4 +++ sources/pyside6/doc/extras/QtQml.QmlSingleton.rst | 25 +++++++++++++ .../doc/extras/QtQml.qmlRegisterSingletonType.rst | 2 ++ .../pyside6/tests/QtQml/registersingletontype.py | 24 +++++++++++-- .../pyside6/tests/QtQml/registersingletontype.qml | 3 +- 8 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 sources/pyside6/doc/extras/QtQml.QmlSingleton.rst diff --git a/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.cpp b/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.cpp index 1dd0507f7..b3d2b31fe 100644 --- a/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.cpp @@ -370,7 +370,8 @@ static int getGlobalInt(const char *name) enum class RegisterMode { Normal, Anonymous, - Uncreatable + Uncreatable, + Singleton }; static PyObject *qmlElementMacroHelper(PyObject *pyObj, @@ -411,21 +412,36 @@ static PyObject *qmlElementMacroHelper(PyObject *pyObj, if (minorVersion == -1) minorVersion = 0; - if (PySide::qmlRegisterType(pyObj, importName.c_str(), majorVersion, minorVersion, - mode != RegisterMode::Anonymous ? typeName : nullptr, - noCreationReason, - mode == RegisterMode::Normal) == -1) { - PyErr_Format(PyExc_TypeError, "Failed to register type %s.", typeName); + const char *uri = importName.c_str(); + const int result = mode == RegisterMode::Singleton + ? PySide::qmlRegisterSingletonType(pyObj, uri, majorVersion, minorVersion, + typeName, nullptr, + PySide::isQObjectDerived(pyObjType, false), + false) + : PySide::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; } +// FIXME: Store this in PySide::TypeUserData once it is moved to libpyside? +static QList decoratedSingletons; + PyObject *PySide::qmlElementMacro(PyObject *pyObj) { - auto *noCreationReason = PySide::qmlNoCreationReason(pyObj); - const auto mode = noCreationReason != nullptr - ? RegisterMode::Uncreatable : RegisterMode::Normal; + const char *noCreationReason = nullptr; + RegisterMode mode = RegisterMode::Normal; + if (decoratedSingletons.contains(pyObj)) + mode = RegisterMode::Singleton; + else if ((noCreationReason = PySide::qmlNoCreationReason(pyObj))) + mode = RegisterMode::Uncreatable; return qmlElementMacroHelper(pyObj, "QmlElement", mode, noCreationReason); } @@ -434,3 +450,10 @@ PyObject *PySide::qmlAnonymousMacro(PyObject *pyObj) return qmlElementMacroHelper(pyObj, "QmlAnonymous", RegisterMode::Anonymous); } + +PyObject *PySide::qmlSingletonMacro(PyObject *pyObj) +{ + decoratedSingletons.append(pyObj); + Py_INCREF(pyObj); + return pyObj; +} diff --git a/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.h b/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.h index e8105eb94..643033237 100644 --- a/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.h +++ b/sources/pyside6/PySide6/QtQml/pysideqmlregistertype.h @@ -100,6 +100,10 @@ PyObject *qmlElementMacro(PyObject *pyObj); /// \param pyObj Python type to be registered PyObject *qmlAnonymousMacro(PyObject *pyObj); +/// PySide implementation of the QML_SINGLETON macro +/// \param pyObj Python type to be registered +PyObject *qmlSingletonMacro(PyObject *pyObj); + } // namespace PySide #endif // PYSIDEQMLREGISTERTYPE_H diff --git a/sources/pyside6/PySide6/QtQml/typesystem_qml.xml b/sources/pyside6/PySide6/QtQml/typesystem_qml.xml index 02fcb222f..d1664f1ed 100644 --- a/sources/pyside6/PySide6/QtQml/typesystem_qml.xml +++ b/sources/pyside6/PySide6/QtQml/typesystem_qml.xml @@ -111,6 +111,10 @@ + + + + diff --git a/sources/pyside6/PySide6/glue/qtqml.cpp b/sources/pyside6/PySide6/glue/qtqml.cpp index 2d4a69a0e..27834e2a7 100644 --- a/sources/pyside6/PySide6/glue/qtqml.cpp +++ b/sources/pyside6/PySide6/glue/qtqml.cpp @@ -90,3 +90,7 @@ return %CONVERTTOPYTHON[%RETURN_TYPE](retval); // @snippet qmlanonymous %PYARG_0 = PySide::qmlAnonymousMacro(%ARGUMENT_NAMES); // @snippet qmlanonymous + +// @snippet qmlsingleton +%PYARG_0 = PySide::qmlSingletonMacro(%ARGUMENT_NAMES); +// @snippet qmlsingleton diff --git a/sources/pyside6/doc/extras/QtQml.QmlSingleton.rst b/sources/pyside6/doc/extras/QtQml.QmlSingleton.rst new file mode 100644 index 000000000..d3d3bf4a7 --- /dev/null +++ b/sources/pyside6/doc/extras/QtQml.QmlSingleton.rst @@ -0,0 +1,25 @@ +.. currentmodule:: PySide6.QtQml +.. _QmlSingleton: + +QmlSingleton +************ + +.. py:decorator:: QmlSingleton + +Declares the decorated type to be a singleton in QML. This only takes effect if +the type is a Q_OBJECT and is available in QML (by having a QmlElement decorator). +The QQmlEngine will try to create a singleton instance using the type's default +constructor. + +.. code-block:: python + + QML_IMPORT_NAME = "com.library.name" + QML_IMPORT_MAJOR_VERSION = 1 + QML_IMPORT_MINOR_VERSION = 0 # Optional + + @QmlElement + @QmlSingleton + class ClassForQml(QObject): + # ... + +.. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``. diff --git a/sources/pyside6/doc/extras/QtQml.qmlRegisterSingletonType.rst b/sources/pyside6/doc/extras/QtQml.qmlRegisterSingletonType.rst index 83102bf86..32231a391 100644 --- a/sources/pyside6/doc/extras/QtQml.qmlRegisterSingletonType.rst +++ b/sources/pyside6/doc/extras/QtQml.qmlRegisterSingletonType.rst @@ -15,6 +15,8 @@ qmlRegisterSingletonType This function registers a Python type as a singleton in the QML system. + Alternatively, the :ref:`QmlSingleton` decorator can be used. + .. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int :param type pytype: Python class diff --git a/sources/pyside6/tests/QtQml/registersingletontype.py b/sources/pyside6/tests/QtQml/registersingletontype.py index a939628cf..6ebc0004c 100644 --- a/sources/pyside6/tests/QtQml/registersingletontype.py +++ b/sources/pyside6/tests/QtQml/registersingletontype.py @@ -39,7 +39,8 @@ from helper.helper import quickview_errorstring from PySide6.QtCore import Property, Signal, QTimer, QUrl, QObject from PySide6.QtGui import QGuiApplication -from PySide6.QtQml import qmlRegisterSingletonType, qmlRegisterSingletonInstance +from PySide6.QtQml import (qmlRegisterSingletonType, qmlRegisterSingletonInstance, + QmlElement, QmlSingleton) from PySide6.QtQuick import QQuickView finalResult = 0 @@ -70,6 +71,25 @@ def singletonQJSValueCallback(engine): return engine.evaluate("new Object({data: 50})") +QML_IMPORT_NAME = "Singletons" +QML_IMPORT_MAJOR_VERSION = 1 + +@QmlElement +@QmlSingleton +class DecoratedSingletonQObject(QObject): + def __init__(self, parent=None): + super().__init__(parent) + self._data = 200 + + def getData(self): + return self._data + + def setData(self, data): + self._data = data + + data = Property(int, getData, setData) + + class TestQmlSupport(unittest.TestCase): def testIt(self): app = QGuiApplication([]) @@ -99,7 +119,7 @@ class TestQmlSupport(unittest.TestCase): view.show() QTimer.singleShot(250, view.close) app.exec() - self.assertEqual(finalResult, 299) + self.assertEqual(finalResult, 499) if __name__ == '__main__': diff --git a/sources/pyside6/tests/QtQml/registersingletontype.qml b/sources/pyside6/tests/QtQml/registersingletontype.qml index 2365cf201..16defa867 100644 --- a/sources/pyside6/tests/QtQml/registersingletontype.qml +++ b/sources/pyside6/tests/QtQml/registersingletontype.qml @@ -33,6 +33,7 @@ Item { Component.onCompleted: { SingletonQObjectCallback.data += SingletonQObjectNoCallback.data + SingletonQJSValue.data - + SingletonInstance.data; + + SingletonInstance.data + + DecoratedSingletonQObject.data; } } -- cgit v1.2.3