aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2021-11-15 09:37:51 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-11-16 09:58:24 +0000
commit13d5bffca41178f969f06a0ce892edc8695ad41a (patch)
tree502ed337e85b751b79fe6fdf2994e162734c87b2
parentb97d977a5def85f262ac6f82c9f246e81816f4e1 (diff)
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 <cristian.maureira-fredes@qt.io> (cherry picked from commit 671f9ed73ee174fe2ba96d7a1c2b267455ef367e) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--sources/pyside6/PySide6/QtQml/pysideqmlregistertype.cpp41
-rw-r--r--sources/pyside6/PySide6/QtQml/pysideqmlregistertype.h4
-rw-r--r--sources/pyside6/PySide6/QtQml/typesystem_qml.xml4
-rw-r--r--sources/pyside6/PySide6/glue/qtqml.cpp4
-rw-r--r--sources/pyside6/doc/extras/QtQml.QmlSingleton.rst25
-rw-r--r--sources/pyside6/doc/extras/QtQml.qmlRegisterSingletonType.rst2
-rw-r--r--sources/pyside6/tests/QtQml/registersingletontype.py24
-rw-r--r--sources/pyside6/tests/QtQml/registersingletontype.qml3
8 files changed, 95 insertions, 12 deletions
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<PyObject *> 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 @@
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlanonymous"/>
</add-function>
+ <add-function signature="QmlSingleton(PyObject*)" return-type="PyObject*">
+ <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlsingleton"/>
+ </add-function>
+
<function signature="qjsEngine(const QObject*)">
<modify-function>
<modify-argument index="return" pyi-type="Optional[PySide6.QtQml.QJSEngine]"/>
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;
}
}