diff options
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp | 122 | ||||
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h | 15 | ||||
-rw-r--r-- | sources/pyside2/PySide2/QtQml/typesystem_qml.xml | 21 | ||||
-rw-r--r-- | sources/pyside2/PySide2/glue/qtqml.cpp | 15 | ||||
-rw-r--r-- | sources/pyside2/tests/QtQml/registersingletontype.py | 86 | ||||
-rw-r--r-- | sources/pyside2/tests/QtQml/registersingletontype.qml | 36 |
6 files changed, 295 insertions, 0 deletions
diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 046d62800..2381b3e73 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -52,6 +52,8 @@ #include "pyside2_qtcore_python.h" #include "pyside2_qtqml_python.h" +#include <QtQml/QJSValue> + // Forward declarations. static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, void **args); @@ -144,6 +146,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" { diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h index 74690d937..52ef92608 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h @@ -71,6 +71,21 @@ void initQmlSupport(PyObject *module); */ int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int versionMinor, const char *qmlName); + +/** + * PySide implementation of qmlRegisterType<T> function. + * + * \param pyObj Python type to be registered. + * \param uri QML element uri. + * \param versionMajor QML component major version. + * \param versionMinor QML component minor version. + * \param qmlName QML element name + * \param callback Registration callback + * \return the metatype id of the registered type. + */ +int qmlRegisterSingletonType(PyObject *pyObj,const char *uri, int versionMajor, int versionMinor, const char *qmlName, + PyObject *callback, bool isQObject, bool hasCallback); + } // Volatile Bool Ptr type definition for QQmlIncubationController::incubateWhile(std::atomic<bool> *, int) diff --git a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml index af6f75131..1b3dade86 100644 --- a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml +++ b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml @@ -90,6 +90,27 @@ <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistertype"/> </add-function> + <add-function signature="qmlRegisterSingletonType(PyTypeObject,const char*,int,int,const char*,PyObject*)" return-type="int"> + <inject-documentation format="target" mode="append"> + This function registers a Python type as a singleton in the QML system using the provided callback (which gets a QQmlEngine as a parameter) to generate the singleton. + </inject-documentation> + <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_callback"/> + </add-function> + + <add-function signature="qmlRegisterSingletonType(PyTypeObject,const char*,int,int,const char*)" return-type="int"> + <inject-documentation format="target" mode="append"> + This function registers a Python type as a singleton in the QML system. + </inject-documentation> + <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_nocallback"/> + </add-function> + + <add-function signature="qmlRegisterSingletonType(const char*,int,int,const char*,PyObject*)" return-type="int"> + <inject-documentation format="target" mode="append"> + This function registers a QJSValue as a singleton in the QML system using the provided callback (which gets a QQmlEngine as a parameter) to generate the singleton. + </inject-documentation> + <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qjsvalue"/> + </add-function> + <enum-type identified-by-value="QML_HAS_ATTACHED_PROPERTIES"> <extra-includes> <include file-name="QtQml" location="global"/> diff --git a/sources/pyside2/PySide2/glue/qtqml.cpp b/sources/pyside2/PySide2/glue/qtqml.cpp index 1913204c3..b1d043cf8 100644 --- a/sources/pyside2/PySide2/glue/qtqml.cpp +++ b/sources/pyside2/PySide2/glue/qtqml.cpp @@ -42,6 +42,21 @@ int %0 = PySide::qmlRegisterType(%ARGUMENT_NAMES); %PYARG_0 = %CONVERTTOPYTHON[int](%0); // @snippet qmlregistertype +// @snippet qmlregistersingletontype_qobject_callback +int %0 = PySide::qmlRegisterSingletonType(%ARGUMENT_NAMES, true, true); +%PYARG_0 = %CONVERTTOPYTHON[int](%0); +// @snippet qmlregistersingletontype_qobject_callback + +// @snippet qmlregistersingletontype_qobject_nocallback +int %0 = PySide::qmlRegisterSingletonType(%ARGUMENT_NAMES, nullptr, true, false); +%PYARG_0 = %CONVERTTOPYTHON[int](%0); +// @snippet qmlregistersingletontype_qobject_nocallback + +// @snippet qmlregistersingletontype_qjsvalue +int %0 = PySide::qmlRegisterSingletonType(nullptr, %ARGUMENT_NAMES, false, true); +%PYARG_0 = %CONVERTTOPYTHON[int](%0); +// @snippet qmlregistersingletontype_qjsvalue + // @snippet init PySide::initQmlSupport(module); // @snippet init diff --git a/sources/pyside2/tests/QtQml/registersingletontype.py b/sources/pyside2/tests/QtQml/registersingletontype.py new file mode 100644 index 000000000..fe3f749f5 --- /dev/null +++ b/sources/pyside2/tests/QtQml/registersingletontype.py @@ -0,0 +1,86 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +import sys +import unittest + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from init_paths import init_test_paths +init_test_paths(False) + +from helper.helper import adjust_filename + +from PySide2.QtCore import Property, Signal, QTimer, QUrl, QObject +from PySide2.QtGui import QGuiApplication +from PySide2.QtQml import qmlRegisterSingletonType +from PySide2.QtQuick import QQuickView + +finalResult = 0 + +class SingletonQObject(QObject): + def __init__(self, parent = None): + QObject.__init__(self, parent) + self._data = 100 + + def getData(self): + return self._data + + def setData(self, data): + global finalResult + finalResult = self._data = data + + data = Property(int, getData, setData) + +def singletonQObjectCallback(engine): + obj = SingletonQObject() + obj.setData(50) + return obj + +def singletonQJSValueCallback(engine): + return engine.evaluate("new Object({data: 50})") + +class TestQmlSupport(unittest.TestCase): + def testIt(self): + app = QGuiApplication([]) + + qmlRegisterSingletonType(SingletonQObject, 'Singletons', 1, 0, 'SingletonQObjectNoCallback') + qmlRegisterSingletonType(SingletonQObject, 'Singletons', 1, 0, 'SingletonQObjectCallback', + singletonQObjectCallback) + + qmlRegisterSingletonType('Singletons', 1, 0, 'SingletonQJSValue', singletonQJSValueCallback) + + view = QQuickView() + view.setSource(QUrl.fromLocalFile(adjust_filename('registersingletontype.qml', __file__))) + view.show() + QTimer.singleShot(250, view.close) + app.exec_() + self.assertEqual(finalResult, 200) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside2/tests/QtQml/registersingletontype.qml b/sources/pyside2/tests/QtQml/registersingletontype.qml new file mode 100644 index 000000000..c8b34e69a --- /dev/null +++ b/sources/pyside2/tests/QtQml/registersingletontype.qml @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import Singletons 1.0 + +Item { + Component.onCompleted: { + SingletonQObjectCallback.data += SingletonQObjectNoCallback.data + SingletonQJSValue.data + } +} |