diff options
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp | 81 | ||||
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h | 7 | ||||
-rw-r--r-- | sources/pyside2/PySide2/QtQml/typesystem_qml.xml | 4 | ||||
-rw-r--r-- | sources/pyside2/PySide2/glue/qtqml.cpp | 4 | ||||
-rw-r--r-- | sources/pyside2/doc/extras/QtQml.QmlElement.rst | 28 | ||||
-rw-r--r-- | sources/pyside2/tests/QtQml/registertype.py | 10 |
6 files changed, 130 insertions, 4 deletions
diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index fd470cd71..3a3c0d27e 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -39,6 +39,8 @@ #include "pysideqmlregistertype.h" +#include <limits> + // shiboken #include <shiboken.h> #include <signature.h> @@ -649,3 +651,82 @@ void PySide::initQmlSupport(PyObject *module) PyModule_AddObject(module, PepType_GetNameStr(QtQml_VolatileBoolTypeF()), reinterpret_cast<PyObject *>(QtQml_VolatileBoolTypeF())); } + +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 = PyUnicode_AsUTF8(globalVar); + return stringValue != nullptr ? stringValue : ""; +} + +static int getGlobalInt(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 || !PyLong_Check(globalVar)) + return -1; + + long value = PyLong_AsLong(globalVar); + + if (value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min()) + return -1; + + return value; +} + +PyObject *PySide::qmlElementMacro(PyObject *pyObj) +{ + if (!PyType_Check(pyObj)) { + PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes."); + return nullptr; + } + + static PyTypeObject *qobjectType = Shiboken::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, "This decorator can only be used with classes inherited from QObject, got %s.", pyObjType->tp_name); + 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 QmlElement."); + return nullptr; + } + + if (majorVersion == -1) { + PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_MAJOR_VERSION in order to use QmlElement."); + return nullptr; + } + + // Specifying a minor version is optional + if (minorVersion == -1) + minorVersion = 0; + + if (qmlRegisterType(pyObj, importName.c_str(), majorVersion, minorVersion, pyObjType->tp_name) == -1) { + PyErr_Format(PyExc_TypeError, "Failed to register type %s.", pyObjType->tp_name); + } + + return pyObj; +} diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h index b147d9888..30c174e46 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.h @@ -84,6 +84,13 @@ int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int vers int qmlRegisterSingletonType(PyObject *pyObj,const char *uri, int versionMajor, int versionMinor, const char *qmlName, PyObject *callback, bool isQObject, bool hasCallback); + +/** + * PySide implementation of the QML_ELEMENT macro + * + * \param pyObj Python type to be registered + */ +PyObject *qmlElementMacro(PyObject *pyObj); } PyAPI_FUNC(PyTypeObject *) QtQml_VolatileBoolTypeF(void); diff --git a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml index b2def633f..0d7adf9e8 100644 --- a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml +++ b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml @@ -100,6 +100,10 @@ <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregisteruncreatabletype"/> </add-function> + <add-function signature="QmlElement(PyObject*)" return-type="PyObject*"> + <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlelement"/> + </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 5ff072e09..eab6eab95 100644 --- a/sources/pyside2/PySide2/glue/qtqml.cpp +++ b/sources/pyside2/PySide2/glue/qtqml.cpp @@ -70,3 +70,7 @@ PySide::initQmlSupport(module); %RETURN_TYPE retval = %CPPSELF.%FUNCTION_NAME(%1); return %CONVERTTOPYTHON[%RETURN_TYPE](retval); // @snippet qjsengine-toscriptvalue + +// @snippet qmlelement +%PYARG_0 = PySide::qmlElementMacro(%ARGUMENT_NAMES); +// @snippet qmlelement diff --git a/sources/pyside2/doc/extras/QtQml.QmlElement.rst b/sources/pyside2/doc/extras/QtQml.QmlElement.rst new file mode 100644 index 000000000..90a5134c1 --- /dev/null +++ b/sources/pyside2/doc/extras/QtQml.QmlElement.rst @@ -0,0 +1,28 @@ +.. currentmodule:: PySide2.QtQml +.. _QmlElement: + +QmlElement +********** + +.. py:decorator:: QmlElement + + This decorator registers a class it is attached to for use in QML, using + global variables to specify the import name and version. + + :: + QML_IMPORT_NAME = "com.library.name" + QML_IMPORT_MAJOR_VERSION = 1 + QML_IMPORT_MINOR_VERSION = 0 # Optional + + @QmlElement + class ClassForQml(QObject): + # ... + + Afterwards the class may be used in QML: + + :: + import com.library.name 1.0 + + ClassForQml { + // ... + } diff --git a/sources/pyside2/tests/QtQml/registertype.py b/sources/pyside2/tests/QtQml/registertype.py index e4dcd36f9..2c0577fe3 100644 --- a/sources/pyside2/tests/QtQml/registertype.py +++ b/sources/pyside2/tests/QtQml/registertype.py @@ -38,9 +38,13 @@ from helper.helper import adjust_filename from PySide2.QtCore import Property, QObject, QTimer, QUrl from PySide2.QtGui import QGuiApplication, QPen, QColor, QPainter -from PySide2.QtQml import qmlRegisterType, ListProperty +from PySide2.QtQml import qmlRegisterType, ListProperty, QmlElement from PySide2.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem +QML_IMPORT_NAME = "Charts" +QML_IMPORT_MAJOR_VERSION = 1 + +@QmlElement class PieSlice (QQuickPaintedItem): def __init__(self, parent = None): QQuickPaintedItem.__init__(self, parent) @@ -78,6 +82,7 @@ class PieSlice (QQuickPaintedItem): painter.drawPie(self.boundingRect(), self._fromAngle * 16, self._angleSpan * 16); paintCalled = True +@QmlElement class PieChart (QQuickItem): def __init__(self, parent = None): QQuickItem.__init__(self, parent) @@ -108,9 +113,6 @@ class TestQmlSupport(unittest.TestCase): def testIt(self): app = QGuiApplication([]) - self.assertTrue(qmlRegisterType(PieChart, 'Charts', 1, 0, 'PieChart') != -1) - self.assertTrue(qmlRegisterType(PieSlice, "Charts", 1, 0, "PieSlice") != -1) - view = QQuickView() view.setSource(QUrl.fromLocalFile(adjust_filename('registertype.qml', __file__))) view.show() |