diff options
Diffstat (limited to 'sources/pyside6/libpysideqml')
16 files changed, 1672 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideqml/CMakeLists.txt b/sources/pyside6/libpysideqml/CMakeLists.txt new file mode 100644 index 000000000..8142f7040 --- /dev/null +++ b/sources/pyside6/libpysideqml/CMakeLists.txt @@ -0,0 +1,85 @@ +set(libpysideqml_libraries Qt::Core Qt::CorePrivate Qt::Qml Qt::QmlPrivate) + +set(libpysideqml_SRC + pysideqml.cpp + pysideqmlregistertype.cpp + pysideqmlmetacallerror.cpp + pysideqmllistproperty.cpp + pysideqmluncreatable.cpp +) + +add_library(pyside6qml SHARED ${libpysideqml_SRC} ${other_files}) +add_library(PySide6::pyside6qml ALIAS pyside6qml) + +target_include_directories(pyside6qml PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> + $<INSTALL_INTERFACE:include/PySide6Qml> +) + +target_link_libraries(pyside6qml + PRIVATE PySide6::pyside6 Shiboken6::libshiboken ${libpysideqml_libraries}) + +set_target_properties(pyside6qml PROPERTIES + VERSION ${BINDING_API_VERSION} + SOVERSION "${PYSIDE_SO_VERSION}" + OUTPUT_NAME "pyside6qml${pyside6_SUFFIX}${SHIBOKEN_PYTHON_SHARED_LIBRARY_SUFFIX}" + DEFINE_SYMBOL BUILD_LIBPYSIDEQML) + +set_property(TARGET pyside6 PROPERTY CXX_STANDARD 17) + +if(PYSIDE_QT_CONF_PREFIX) + set_property(SOURCE pysideqml.cpp + APPEND + PROPERTY COMPILE_DEFINITIONS + PYSIDE_QT_CONF_PREFIX=${PYSIDE_QT_CONF_PREFIX}) +endif() + +# +# install stuff +# + +set(libpysideqml_HEADERS + pysideqmlmacros.h + pysideqml.h + pysideqmlregistertype.h +) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(LIBRARY_OUTPUT_SUFFIX ${CMAKE_DEBUG_POSTFIX}) +else() + set(LIBRARY_OUTPUT_SUFFIX ${CMAKE_RELEASE_POSTFIX}) +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D QT_NO_CAST_FROM_ASCII -D QT_NO_CAST_TO_ASCII") + +# Install-tree / relocatable package config file. +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/PySide6QmlConfig-spec.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/install/PySide6QmlConfig${SHIBOKEN_PYTHON_CONFIG_SUFFIX}.cmake" + INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6Qml-${BINDING_API_VERSION}" +) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide6QmlConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PySide6QmlConfig.cmake" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide6QmlConfigVersion.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PySide6QmlConfigVersion.cmake" @ONLY) + +install(FILES ${libpysideqml_HEADERS} + DESTINATION include/${BINDING_NAME}${pyside6qml_SUFFIX}) + +install(TARGETS pyside6qml EXPORT PySide6QmlTargets + LIBRARY DESTINATION "${LIB_INSTALL_DIR}" + ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" + RUNTIME DESTINATION bin) +install(EXPORT PySide6QmlTargets NAMESPACE PySide6Qml:: + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6Qml-${BINDING_API_VERSION}") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide6QmlConfig.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6Qml-${BINDING_API_VERSION}") + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/install/PySide6QmlConfig${SHIBOKEN_PYTHON_CONFIG_SUFFIX}.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6Qml-${BINDING_API_VERSION}") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide6QmlConfigVersion.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6Qml-${BINDING_API_VERSION}") diff --git a/sources/pyside6/libpysideqml/PySide6QmlConfig-spec.cmake.in b/sources/pyside6/libpysideqml/PySide6QmlConfig-spec.cmake.in new file mode 100644 index 000000000..36eb4123a --- /dev/null +++ b/sources/pyside6/libpysideqml/PySide6QmlConfig-spec.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +# Import targets only when using an installed PySide6 config file (so not during a regular +# PySide6 build, or during a super project build). +if (NOT TARGET PySide6::pyside6qml) + include("${CMAKE_CURRENT_LIST_DIR}/PySide6QmlTargets.cmake") +endif() diff --git a/sources/pyside6/libpysideqml/PySide6QmlConfig.cmake.in b/sources/pyside6/libpysideqml/PySide6QmlConfig.cmake.in new file mode 100644 index 000000000..dab0a6b13 --- /dev/null +++ b/sources/pyside6/libpysideqml/PySide6QmlConfig.cmake.in @@ -0,0 +1,5 @@ +if (NOT PYTHON_CONFIG_SUFFIX) + message(STATUS "PySide6QmlConfig: Using default python: @SHIBOKEN_PYTHON_CONFIG_SUFFIX@") + SET(PYTHON_CONFIG_SUFFIX @SHIBOKEN_PYTHON_CONFIG_SUFFIX@) +endif() +include(${CMAKE_CURRENT_LIST_DIR}/PySide6QmlConfig${PYTHON_CONFIG_SUFFIX}.cmake) diff --git a/sources/pyside6/libpysideqml/PySide6QmlConfigVersion.cmake.in b/sources/pyside6/libpysideqml/PySide6QmlConfigVersion.cmake.in new file mode 100644 index 000000000..f5073ce08 --- /dev/null +++ b/sources/pyside6/libpysideqml/PySide6QmlConfigVersion.cmake.in @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION @BINDING_API_VERSION@) + +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") +endif("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) diff --git a/sources/pyside6/libpysideqml/pysideqml.cpp b/sources/pyside6/libpysideqml/pysideqml.cpp new file mode 100644 index 000000000..6bdc277f6 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqml.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pysideqml.h" +#include "pysideqmllistproperty_p.h" +#include "pysideqmluncreatable.h" +#include "pysideqmluncreatable.h" +#include "pysideqmlmetacallerror_p.h" + +#include <signalmanager.h> + +namespace PySide::Qml +{ + +void init(PyObject *module) +{ + initQtQmlListProperty(module); + initQmlUncreatable(module); + PySide::SignalManager::setQmlMetaCallErrorHandler(PySide::qmlMetaCallErrorHandler); +} + +} //namespace PySide::Qml diff --git a/sources/pyside6/libpysideqml/pysideqml.h b/sources/pyside6/libpysideqml/pysideqml.h new file mode 100644 index 000000000..a227a4cee --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqml.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQML_H +#define PYSIDEQML_H + +#include "pysideqmlmacros.h" + +#include <sbkpython.h> + +namespace PySide::Qml +{ + +PYSIDEQML_API void init(PyObject *module); + +} //namespace PySide::Qml + +#endif // PYSIDEQML_H diff --git a/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp b/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp new file mode 100644 index 000000000..7cca21326 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pysideqmllistproperty_p.h" +#include "pysideqmlregistertype_p.h" + +#include <shiboken.h> +#include <signature.h> + +#include <pysideproperty.h> + +#include <QtCore/QObject> +#include <QtQml/QQmlListProperty> + +// Forward declarations. +static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, + void **args); + +extern "C" +{ + +// This is the user data we store in the property. +struct QmlListProperty +{ + PyTypeObject *type; + PyObject *append; + PyObject *count; + PyObject *at; + PyObject *clear; + PyObject *replace; + PyObject *removeLast; +}; + +static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"type", "append", "count", "at", "clear", "replace", "removeLast", 0}; + PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self); + QmlListProperty *data = new QmlListProperty; + memset(data, 0, sizeof(QmlListProperty)); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O|OOOOOO:QtQml.ListProperty", (char **) kwlist, + &data->type, + &data->append, + &data->count, + &data->at, + &data->clear, + &data->replace, + &data->removeLast)) { + delete data; + return -1; + } + + PyTypeObject *qobjectType = qObjectType(); + + if (!PySequence_Contains(data->type->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) { + PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.", + qobjectType->tp_name, data->type->tp_name); + delete data; + return -1; + } + + if ((data->append && data->append != Py_None && !PyCallable_Check(data->append)) || + (data->count && data->count != Py_None && !PyCallable_Check(data->count)) || + (data->at && data->at != Py_None && !PyCallable_Check(data->at)) || + (data->clear && data->clear != Py_None && !PyCallable_Check(data->clear)) || + (data->replace && data->replace != Py_None && !PyCallable_Check(data->replace)) || + (data->removeLast && data->removeLast != Py_None && !PyCallable_Check(data->removeLast))) { + PyErr_Format(PyExc_TypeError, "Non-callable parameter given"); + delete data; + return -1; + } + + PySide::Property::setMetaCallHandler(pySelf, &propListMetaCall); + PySide::Property::setTypeName(pySelf, "QQmlListProperty<QObject>"); + PySide::Property::setUserData(pySelf, data); + + return 0; +} + +void propListTpFree(void *self) +{ + auto pySelf = reinterpret_cast<PySideProperty *>(self); + delete reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pySelf)); + // calls base type constructor + Py_TYPE(pySelf)->tp_base->tp_free(self); +} + +static PyType_Slot PropertyListType_slots[] = { + {Py_tp_init, reinterpret_cast<void *>(propListTpInit)}, + {Py_tp_free, reinterpret_cast<void *>(propListTpFree)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {0, nullptr} +}; +static PyType_Spec PropertyListType_spec = { + "2:PySide6.QtQml.ListProperty", + sizeof(PySideProperty), + 0, + Py_TPFLAGS_DEFAULT, + PropertyListType_slots, +}; + + +PyTypeObject *PropertyListTypeF(void) +{ + static Shiboken::AutoDecRef bases(Py_BuildValue("(O)", PySidePropertyTypeF())); + static auto *type = SbkType_FromSpecWithBases(&PropertyListType_spec, bases); + return type; +} + +} // extern "C" + +// Implementation of QQmlListProperty<T>::AppendFunction callback +void propListAppender(QQmlListProperty<QObject> *propList, QObject *item) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(2)); + PyTypeObject *qobjectType = qObjectType(); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); + PyTuple_SET_ITEM(args, 1, + Shiboken::Conversions::pointerToPython(qobjectType, item)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->append, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + +// Implementation of QQmlListProperty<T>::CountFunction callback +qsizetype propListCount(QQmlListProperty<QObject> *propList) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(1)); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qObjectType(), propList->object)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->count, args)); + + // Check return type + if (PyErr_Occurred()) { + PyErr_Print(); + return 0; + } + + int cppResult = 0; + auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>(); + if (auto *pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, retVal)) + pythonToCpp(retVal, &cppResult); + return cppResult; +} + +// Implementation of QQmlListProperty<T>::AtFunction callback +QObject *propListAt(QQmlListProperty<QObject> *propList, qsizetype index) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(2)); + PyTypeObject *qobjectType = qObjectType(); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); + auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>(); + PyTuple_SET_ITEM(args, 1, + Shiboken::Conversions::copyToPython(converter, &index)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args)); + + QObject *result = 0; + if (PyErr_Occurred()) + PyErr_Print(); + else if (PyType_IsSubtype(Py_TYPE(retVal), data->type)) + Shiboken::Conversions::pythonToCppPointer(qobjectType, retVal, &result); + return result; +} + +// Implementation of QQmlListProperty<T>::ClearFunction callback +void propListClear(QQmlListProperty<QObject> * propList) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(1)); + PyTypeObject *qobjectType = qObjectType(); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->clear, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + +// Implementation of QQmlListProperty<T>::ReplaceFunction callback +void propListReplace(QQmlListProperty<QObject> *propList, qsizetype index, QObject *value) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(3)); + PyTypeObject *qobjectType = qObjectType(); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); + auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>(); + PyTuple_SET_ITEM(args, 1, + Shiboken::Conversions::copyToPython(converter, &index)); + PyTuple_SET_ITEM(args, 2, + Shiboken::Conversions::pointerToPython(qobjectType, value)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->replace, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + +// Implementation of QQmlListProperty<T>::RemoveLastFunction callback +void propListRemoveLast(QQmlListProperty<QObject> *propList) +{ + Shiboken::GilState state; + + Shiboken::AutoDecRef args(PyTuple_New(1)); + PyTypeObject *qobjectType = qObjectType(); + PyTuple_SET_ITEM(args, 0, + Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); + + auto data = reinterpret_cast<QmlListProperty *>(propList->data); + Shiboken::AutoDecRef retVal(PyObject_CallObject(data->removeLast, args)); + + if (PyErr_Occurred()) + PyErr_Print(); +} + +// qt_metacall specialization for ListProperties +static void propListMetaCall(PySideProperty *pp, PyObject *self, + QMetaObject::Call call, void **args) +{ + if (call != QMetaObject::ReadProperty) + return; + + auto data = reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pp)); + QObject *qobj; + PyTypeObject *qobjectType = qObjectType(); + Shiboken::Conversions::pythonToCppPointer(qobjectType, self, &qobj); + QQmlListProperty<QObject> declProp( + qobj, data, + data->append && data->append != Py_None ? &propListAppender : nullptr, + data->count && data->count != Py_None ? &propListCount : nullptr, + data->at && data->at != Py_None ? &propListAt : nullptr, + data->clear && data->clear != Py_None ? &propListClear : nullptr, + data->replace && data->replace != Py_None ? &propListReplace : nullptr, + data->removeLast && data->removeLast != Py_None ? &propListRemoveLast : nullptr); + + // Copy the data to the memory location requested by the meta call + void *v = args[0]; + *reinterpret_cast<QQmlListProperty<QObject> *>(v) = declProp; +} + +static const char *PropertyList_SignatureStrings[] = { + "PySide6.QtQml.ListProperty(self,type:type,append:typing.Callable," + "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)", + nullptr // Sentinel +}; + +void initQtQmlListProperty(PyObject *module) +{ + // Export QmlListProperty type + if (InitSignatureStrings(PropertyListTypeF(), PropertyList_SignatureStrings) < 0) { + PyErr_Print(); + qWarning() << "Error initializing PropertyList type."; + return; + } + + // Register QQmlListProperty metatype for use in QML + qRegisterMetaType<QQmlListProperty<QObject>>(); + + Py_INCREF(reinterpret_cast<PyObject *>(PropertyListTypeF())); + PyModule_AddObject(module, PepType_GetNameStr(PropertyListTypeF()), + reinterpret_cast<PyObject *>(PropertyListTypeF())); +} diff --git a/sources/pyside6/libpysideqml/pysideqmllistproperty_p.h b/sources/pyside6/libpysideqml/pysideqmllistproperty_p.h new file mode 100644 index 000000000..bfae04bfc --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmllistproperty_p.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLLISTPROPERTY_H +#define PYSIDEQMLLISTPROPERTY_H + +#include <sbkpython.h> + +void initQtQmlListProperty(PyObject *module); + +#endif // PYSIDEQMLLISTPROPERTY_H diff --git a/sources/pyside6/libpysideqml/pysideqmlmacros.h b/sources/pyside6/libpysideqml/pysideqmlmacros.h new file mode 100644 index 000000000..208a92f04 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlmacros.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLMACROS_H +#define PYSIDEQMLMACROS_H + +#include <shibokenmacros.h> + +#define PYSIDEQML_EXPORT LIBSHIBOKEN_EXPORT +#define PYSIDEQML_IMPORT LIBSHIBOKEN_IMPORT + +#ifdef BUILD_LIBPYSIDEQML +# define PYSIDEQML_API PYSIDEQML_EXPORT +#else +# define PYSIDEQML_API PYSIDEQML_IMPORT +#endif + +#endif // PYSIDEQMLMACROS_H diff --git a/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp b/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp new file mode 100644 index 000000000..4887e3f51 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pysideqmlmetacallerror_p.h" + +#include <sbkpython.h> +#include <sbkstring.h> +#include <autodecref.h> + +#include <QtCore/QObject> +#include <QtCore/QString> + +#include <QtQml/QQmlEngine> +#include <QtQml/QQmlListProperty> + +#if __has_include (<private/qv4engine_p.h>) +# define QML_PRIVATE_API_SUPPORT +# include <private/qv4engine_p.h> +# include <private/qv4context_p.h> +# include <private/qqmldata_p.h> +#endif + +namespace PySide { + +std::optional<int> qmlMetaCallErrorHandler(QObject *object) +{ +#ifdef QML_PRIVATE_API_SUPPORT + // This JS engine grabber based off of Qt 5.5's `qjsEngine` function + QQmlData *data = QQmlData::get(object, false); + if (!data || data->jsWrapper.isNullOrUndefined()) + return {}; + + QV4::ExecutionEngine *engine = data->jsWrapper.engine(); + if (engine->currentStackFrame == nullptr) + return {}; + + PyObject *errType, *errValue, *errTraceback; + PyErr_Fetch(&errType, &errValue, &errTraceback); + // PYSIDE-464: The error is only valid before PyErr_Restore, + // PYSIDE-464: therefore we take local copies. + Shiboken::AutoDecRef objStr(PyObject_Str(errValue)); + const QString errString = QLatin1String(Shiboken::String::toCString(objStr)); + const bool isSyntaxError = errType == PyExc_SyntaxError; + const bool isTypeError = errType == PyExc_TypeError; + PyErr_Restore(errType, errValue, errTraceback); + + PyErr_Print(); // Note: PyErr_Print clears the error. + + if (isSyntaxError) + return engine->throwSyntaxError(errString); + if (isTypeError) + return engine->throwTypeError(errString); + return engine->throwError(errString); +#else + Q_UNUSED(object); + qWarning("libpyside6qml was built without QML private API support, error handling will not work."); + return {}; +#endif // QML_PRIVATE_API_SUPPORT +} + +} // namespace PySide diff --git a/sources/pyside6/libpysideqml/pysideqmlmetacallerror_p.h b/sources/pyside6/libpysideqml/pysideqmlmetacallerror_p.h new file mode 100644 index 000000000..9ecb0f6eb --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlmetacallerror_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLMETACALLERROR_P_H +#define PYSIDEQMLMETACALLERROR_P_H + +#include <optional> + +#include <QtCore/QtGlobal> + +QT_FORWARD_DECLARE_CLASS(QObject) + +namespace PySide { + +// Helper for SignalManager::qt_metacall(): +// Bubbles Python exceptions up to the Javascript engine, if called from one +std::optional<int> qmlMetaCallErrorHandler(QObject *object); + +} // namespace PySide + +#endif // PYSIDEQMLMETACALLERROR_P_H diff --git a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp new file mode 100644 index 000000000..84fec16b6 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pysideqmlregistertype.h" +#include "pysideqmlregistertype_p.h" +#include "pysideqmluncreatable.h" + +#include <limits> + +// shiboken +#include <shiboken.h> +#include <sbkstring.h> + +// pyside +#include <pyside.h> +#include <pysideqobject.h> +#include <pyside_p.h> + +#include <QtCore/QMutex> +#include <QtCore/QTypeRevision> + +#include <QtQml/qqml.h> +#include <QtQml/QJSValue> +#include <QtQml/QQmlListProperty> + +// Mutex used to avoid race condition on PySide::nextQObjectMemoryAddr. +static QMutex nextQmlElementMutex; + +static void createInto(void *memory, void *type) +{ + QMutexLocker locker(&nextQmlElementMutex); + PySide::setNextQObjectMemoryAddr(memory); + Shiboken::GilState state; + PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), 0); + if (!obj || PyErr_Occurred()) + PyErr_Print(); + PySide::setNextQObjectMemoryAddr(0); +} + +PyTypeObject *qObjectType() +{ + static PyTypeObject *const result = + Shiboken::Conversions::getPythonTypeObject("QObject*"); + assert(result); + return result; +} + +static PyTypeObject *qQmlEngineType() +{ + static PyTypeObject *const result = + Shiboken::Conversions::getPythonTypeObject("QQmlEngine*"); + assert(result); + return result; +} + +static PyTypeObject *qQJSValueType() +{ + static PyTypeObject *const result = + Shiboken::Conversions::getPythonTypeObject("QJSValue*"); + assert(result); + return result; +} + +int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, + int versionMinor, const char *qmlName, const char *noCreationReason, + bool creatable) +{ + using namespace Shiboken; + + PyTypeObject *qobjectType = 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; + } + + const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType); + Q_ASSERT(metaObject); + + QQmlPrivate::RegisterType type; + + // Allow registering Qt Quick items. + bool registered = false; + QuickRegisterItemFunction quickRegisterItemFunction = getQuickRegisterItemFunction(); + if (quickRegisterItemFunction) { + registered = + quickRegisterItemFunction(pyObj, uri, versionMajor, versionMinor, + qmlName, creatable, noCreationReason, &type); + } + + // Register as simple QObject rather than Qt Quick item. + if (!registered) { + // 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; + + // FIXME: Fix this to assign new type ids each time. + type.typeId = QMetaType(QMetaType::QObjectStar); + type.listId = QMetaType::fromType<QQmlListProperty<QObject> >(); + type.attachedPropertiesFunction = QQmlPrivate::attachedPropertiesFunc<QObject>(); + type.attachedPropertiesMetaObject = QQmlPrivate::attachedPropertiesMetaObject<QObject>(); + + type.parserStatusCast = + QQmlPrivate::StaticCastSelector<QObject, QQmlParserStatus>::cast(); + type.valueSourceCast = + QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueSource>::cast(); + type.valueInterceptorCast = + 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; + + type.extensionObjectCreate = 0; + type.extensionMetaObject = 0; + type.customParser = 0; + } + type.metaObject = metaObject; // Snapshot may have changed. + + int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); + if (qmlTypeId == -1) { + PyErr_Format(PyExc_TypeError, "QML meta type registration of \"%s\" failed.", + qmlName); + } + 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 = PyLong_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) { + PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj); + + 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; + + 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; + }; + } else { + type.scriptApi = + [callback](QQmlEngine *engine, QJSEngine *) -> QJSValue { + Shiboken::GilState gil; + AutoDecRef args(PyTuple_New(1)); + + PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython( + qQmlEngineType(), engine)); + + AutoDecRef retVal(PyObject_CallObject(callback, args)); + + PyTypeObject *qjsvalueType = qQJSValueType(); + + // 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); +} + +int PySide::qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMajor, + int versionMinor, const char *qmlName, + PyObject *instanceObject) +{ + using namespace Shiboken; + + // Check if the Python Type inherit from QObject + PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj); + + if (!isQObjectDerived(pyObjType, true)) + return -1; + + // Convert the instanceObject (PyObject) into a QObject + QObject *instanceQObject = PySide::convertToQObject(instanceObject, true); + if (instanceQObject == nullptr) + return -1; + + // Create Singleton Functor to pass the QObject to the Type registration step + // similarly to the case when we have a callback + QQmlPrivate::SingletonFunctor registrationFunctor; + registrationFunctor.m_object = instanceQObject; + + 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); +} + +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) +{ + 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; +} + +enum class RegisterMode { + Normal, + Anonymous, + Uncreatable, + Singleton +}; + +static PyObject *qmlElementMacroHelper(PyObject *pyObj, + const char *decoratorName, + RegisterMode mode = RegisterMode::Normal, + const char *noCreationReason = nullptr) +{ + if (!PyType_Check(pyObj)) { + PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes."); + return nullptr; + } + + PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj); + const char *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); + 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); + 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::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) +{ + 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); +} + +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/libpysideqml/pysideqmlregistertype.h b/sources/pyside6/libpysideqml/pysideqmlregistertype.h new file mode 100644 index 000000000..b1c4f9dcc --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlregistertype.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLREGISTERTYPE_H +#define PYSIDEQMLREGISTERTYPE_H + +#include "pysideqmlmacros.h" + +#include <sbkpython.h> + +namespace PySide +{ + +/** + * 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 + * \return the metatype id of the registered type. + */ +PYSIDEQML_API int qmlRegisterType(PyObject *pyObj, const char *uri, + int versionMajor, int versionMinor, + const char *qmlName, const char *noCreationReason = nullptr, + bool creatable = true); + +/** + * PySide implementation of qmlRegisterSingletonType<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. + */ +PYSIDEQML_API int qmlRegisterSingletonType(PyObject *pyObj,const char *uri, + int versionMajor, int versionMinor, const char *qmlName, + PyObject *callback, bool isQObject, bool hasCallback); + +/** + * PySide implementation of qmlRegisterSingletonInstance<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 instanceObject singleton object to be registered. + * \return the metatype id of the registered type. + */ +PYSIDEQML_API int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, + int versionMajor, int versionMinor, + const char *qmlName, PyObject *instanceObject); + +/** + * PySide implementation of the QML_ELEMENT macro + * + * \param pyObj Python type to be registered + */ +PYSIDEQML_API PyObject *qmlElementMacro(PyObject *pyObj); + +/// PySide implementation of the QML_ANONYMOUS macro +/// \param pyObj Python type to be registered +PYSIDEQML_API PyObject *qmlAnonymousMacro(PyObject *pyObj); + +/// PySide implementation of the QML_SINGLETON macro +/// \param pyObj Python type to be registered +PYSIDEQML_API PyObject *qmlSingletonMacro(PyObject *pyObj); + +} // namespace PySide + +#endif // PYSIDEQMLREGISTERTYPE_H diff --git a/sources/pyside6/libpysideqml/pysideqmlregistertype_p.h b/sources/pyside6/libpysideqml/pysideqmlregistertype_p.h new file mode 100644 index 000000000..d357a0392 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmlregistertype_p.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLREGISTERTYPE_P_H +#define PYSIDEQMLREGISTERTYPE_P_H + +#include <sbkpython.h> + +PyTypeObject *qObjectType(); + +#endif // PYSIDEQMLREGISTERTYPE_P_H diff --git a/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp b/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp new file mode 100644 index 000000000..e59476146 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pysideqmluncreatable.h" + +#include <shiboken.h> +#include <signature.h> +#include <sbkcppstring.h> + +#include <string> +#include <unordered_map> + +#include <QtCore/QtGlobal> + +struct PySideQmlUncreatablePrivate +{ + std::string reason; +}; + +using UncreatableReasonMap = std::unordered_map<PyObject *, std::string>; + +// Types and their nocreationReason. FIXME: Store this in PySide::TypeUserData +// once it is moved to libpyside? +UncreatableReasonMap &uncreatableReasonMap() +{ + static UncreatableReasonMap result; + return result; +} + +extern "C" +{ + +// The call operator is passed the class type and registers the reason +// in the uncreatableReasonMap() +static PyObject *classCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) { + PyErr_Format(PyExc_TypeError, + "The QmlUncreatable decorator takes exactly 1 positional argument (%zd given)", + PyTuple_Size(args)); + return nullptr; + } + + PyObject *klass = PyTuple_GetItem(args, 0); + // This will sometimes segfault if you mistakenly use it on a function declaration + if (!PyType_Check(klass)) { + PyErr_SetString(PyExc_TypeError, + "This decorator can only be used on class declarations"); + return nullptr; + } + + PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass); + if (!Shiboken::ObjectType::checkType(klassType)) { + PyErr_SetString(PyExc_TypeError, + "This decorator can only be used on classes that are subclasses of QObject"); + return nullptr; + } + + auto data = reinterpret_cast<PySideQmlUncreatable *>(self); + uncreatableReasonMap().insert({klass, data->d->reason}); + + Py_INCREF(klass); + return klass; +} + +static PyObject *qmlUncreatableTpNew(PyTypeObject *subtype, PyObject * /* args */, + PyObject * /* kwds */) +{ + auto *me = reinterpret_cast<PySideQmlUncreatable *>(subtype->tp_alloc(subtype, 0)); + me->d = new PySideQmlUncreatablePrivate; + return reinterpret_cast<PyObject *>(me); +} + +static int qmlUncreatableTpInit(PyObject *self, PyObject *args, PyObject * /* kwds */) +{ + PySideQmlUncreatable *data = reinterpret_cast<PySideQmlUncreatable *>(self); + PySideQmlUncreatablePrivate *pData = data->d; + + bool ok = false; + const auto argsCount = PyTuple_Size(args); + if (argsCount == 0) { + ok = true; // QML-generated reason + } else if (argsCount == 1) { + PyObject *arg = PyTuple_GET_ITEM(args, 0); + if (arg == Py_None) { + ok = true; // QML-generated reason + } else if (PyUnicode_Check(arg)) { + ok = true; + Shiboken::String::toCppString(arg, &(pData->reason)); + } + } + + if (!ok) { + PyErr_Format(PyExc_TypeError, + "QmlUncreatable() takes a single string argument or no argument"); + return -1; + } + + return 0; +} + +static void qmlUncreatableFree(void *self) +{ + auto pySelf = reinterpret_cast<PyObject *>(self); + auto data = reinterpret_cast<PySideQmlUncreatable *>(self); + + delete data->d; + Py_TYPE(pySelf)->tp_base->tp_free(self); +} + +static PyType_Slot PySideQmlUncreatableType_slots[] = { + {Py_tp_call, reinterpret_cast<void *>(classCall)}, + {Py_tp_init, reinterpret_cast<void *>(qmlUncreatableTpInit)}, + {Py_tp_new, reinterpret_cast<void *>(qmlUncreatableTpNew)}, + {Py_tp_free, reinterpret_cast<void *>(qmlUncreatableFree)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {0, nullptr} +}; + +static PyType_Spec PySideQmlUncreatableType_spec = { + "2:PySide6.QtCore.qmlUncreatable", + sizeof(PySideQmlUncreatable), + 0, + Py_TPFLAGS_DEFAULT, + PySideQmlUncreatableType_slots, +}; + +PyTypeObject *PySideQmlUncreatableTypeF(void) +{ + static auto *type = SbkType_FromSpec(&PySideQmlUncreatableType_spec); + return type; +} + +} // extern "C" + +static const char *qmlUncreatable_SignatureStrings[] = { + "PySide6.QtQml.QmlUncreatable(self,reason:str)", + nullptr // Sentinel +}; + +void initQmlUncreatable(PyObject *module) +{ + if (InitSignatureStrings(PySideQmlUncreatableTypeF(), qmlUncreatable_SignatureStrings) < 0) + return; + + Py_INCREF(PySideQmlUncreatableTypeF()); + PyModule_AddObject(module, "QmlUncreatable", + reinterpret_cast<PyObject *>(PySideQmlUncreatableTypeF())); +} + +namespace PySide +{ +const char *qmlNoCreationReason(PyObject *type) +{ + const auto &map = uncreatableReasonMap(); + auto it = map.find(type); + return it != map.cend() ? it->second.c_str() : nullptr; +} + +} // PySide diff --git a/sources/pyside6/libpysideqml/pysideqmluncreatable.h b/sources/pyside6/libpysideqml/pysideqmluncreatable.h new file mode 100644 index 000000000..89a5f1223 --- /dev/null +++ b/sources/pyside6/libpysideqml/pysideqmluncreatable.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PYSIDEQMLUNCREATABLE_H +#define PYSIDEQMLUNCREATABLE_H + +#include <sbkpython.h> + +// The QmlUncreatable decorator modifies QmlElement to register an uncreatable +// type. Due to the (reverse) execution order of decorators, it needs to follow +// QmlElement. +extern "C" +{ + extern PyTypeObject *PySideQmlUncreatableTypeF(void); + + struct PySideQmlUncreatablePrivate; + struct PySideQmlUncreatable + { + PyObject_HEAD + PySideQmlUncreatablePrivate* d; + }; + +} // extern "C" + +void initQmlUncreatable(PyObject *module); + +namespace PySide +{ + // Return the reason if a type is not creatable. + const char *qmlNoCreationReason(PyObject *type); +} // PySide + +#endif // PYSIDEQMLUNCREATABLE_H |