From 39236c01ae6c9bc99ee3a02d8294679e12d9b734 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 22 May 2017 16:44:51 +0200 Subject: move everying into sources/pyside2 in preparation for a subtree merge. this should not be necessary to do in a separate commit, but git is a tad stupid about following history correctly without it. --- sources/pyside2/libpyside/CMakeLists.txt | 166 +++ .../pyside2/libpyside/PySide2Config-spec.cmake.in | 18 + sources/pyside2/libpyside/PySide2Config.cmake.in | 5 + .../libpyside/PySide2ConfigVersion.cmake.in | 10 + sources/pyside2/libpyside/destroylistener.cpp | 106 ++ sources/pyside2/libpyside/destroylistener.h | 72 ++ sources/pyside2/libpyside/dynamicqmetaobject.cpp | 901 ++++++++++++++++ sources/pyside2/libpyside/dynamicqmetaobject.h | 82 ++ sources/pyside2/libpyside/dynamicqmetaobject_p.h | 108 ++ sources/pyside2/libpyside/globalreceiver.cpp | 330 ++++++ sources/pyside2/libpyside/globalreceiver.h | 80 ++ sources/pyside2/libpyside/globalreceiverv2.cpp | 358 +++++++ sources/pyside2/libpyside/globalreceiverv2.h | 149 +++ sources/pyside2/libpyside/pyside.cpp | 387 +++++++ sources/pyside2/libpyside/pyside.h | 157 +++ sources/pyside2/libpyside/pyside2.pc.in | 14 + sources/pyside2/libpyside/pysideclassinfo.cpp | 229 +++++ sources/pyside2/libpyside/pysideclassinfo.h | 68 ++ sources/pyside2/libpyside/pysideclassinfo_p.h | 72 ++ sources/pyside2/libpyside/pysideconversions.h | 275 +++++ sources/pyside2/libpyside/pysidemacros.h | 64 ++ sources/pyside2/libpyside/pysidemetafunction.cpp | 253 +++++ sources/pyside2/libpyside/pysidemetafunction.h | 76 ++ sources/pyside2/libpyside/pysidemetafunction_p.h | 62 ++ sources/pyside2/libpyside/pysideproperty.cpp | 519 ++++++++++ sources/pyside2/libpyside/pysideproperty.h | 117 +++ sources/pyside2/libpyside/pysideproperty_p.h | 184 ++++ sources/pyside2/libpyside/pysideqflags.cpp | 167 +++ sources/pyside2/libpyside/pysideqflags.h | 79 ++ sources/pyside2/libpyside/pysidesignal.cpp | 1070 ++++++++++++++++++++ sources/pyside2/libpyside/pysidesignal.h | 173 ++++ sources/pyside2/libpyside/pysidesignal_p.h | 82 ++ sources/pyside2/libpyside/pysideslot.cpp | 220 ++++ sources/pyside2/libpyside/pysideslot_p.h | 49 + sources/pyside2/libpyside/pysideweakref.cpp | 141 +++ sources/pyside2/libpyside/pysideweakref.h | 56 + sources/pyside2/libpyside/signalmanager.cpp.in | 726 +++++++++++++ sources/pyside2/libpyside/signalmanager.h | 123 +++ 38 files changed, 7748 insertions(+) create mode 100644 sources/pyside2/libpyside/CMakeLists.txt create mode 100644 sources/pyside2/libpyside/PySide2Config-spec.cmake.in create mode 100644 sources/pyside2/libpyside/PySide2Config.cmake.in create mode 100644 sources/pyside2/libpyside/PySide2ConfigVersion.cmake.in create mode 100644 sources/pyside2/libpyside/destroylistener.cpp create mode 100644 sources/pyside2/libpyside/destroylistener.h create mode 100644 sources/pyside2/libpyside/dynamicqmetaobject.cpp create mode 100644 sources/pyside2/libpyside/dynamicqmetaobject.h create mode 100644 sources/pyside2/libpyside/dynamicqmetaobject_p.h create mode 100644 sources/pyside2/libpyside/globalreceiver.cpp create mode 100644 sources/pyside2/libpyside/globalreceiver.h create mode 100644 sources/pyside2/libpyside/globalreceiverv2.cpp create mode 100644 sources/pyside2/libpyside/globalreceiverv2.h create mode 100644 sources/pyside2/libpyside/pyside.cpp create mode 100644 sources/pyside2/libpyside/pyside.h create mode 100644 sources/pyside2/libpyside/pyside2.pc.in create mode 100644 sources/pyside2/libpyside/pysideclassinfo.cpp create mode 100644 sources/pyside2/libpyside/pysideclassinfo.h create mode 100644 sources/pyside2/libpyside/pysideclassinfo_p.h create mode 100644 sources/pyside2/libpyside/pysideconversions.h create mode 100644 sources/pyside2/libpyside/pysidemacros.h create mode 100644 sources/pyside2/libpyside/pysidemetafunction.cpp create mode 100644 sources/pyside2/libpyside/pysidemetafunction.h create mode 100644 sources/pyside2/libpyside/pysidemetafunction_p.h create mode 100644 sources/pyside2/libpyside/pysideproperty.cpp create mode 100644 sources/pyside2/libpyside/pysideproperty.h create mode 100644 sources/pyside2/libpyside/pysideproperty_p.h create mode 100644 sources/pyside2/libpyside/pysideqflags.cpp create mode 100644 sources/pyside2/libpyside/pysideqflags.h create mode 100644 sources/pyside2/libpyside/pysidesignal.cpp create mode 100644 sources/pyside2/libpyside/pysidesignal.h create mode 100644 sources/pyside2/libpyside/pysidesignal_p.h create mode 100644 sources/pyside2/libpyside/pysideslot.cpp create mode 100644 sources/pyside2/libpyside/pysideslot_p.h create mode 100644 sources/pyside2/libpyside/pysideweakref.cpp create mode 100644 sources/pyside2/libpyside/pysideweakref.h create mode 100644 sources/pyside2/libpyside/signalmanager.cpp.in create mode 100644 sources/pyside2/libpyside/signalmanager.h (limited to 'sources/pyside2/libpyside') diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt new file mode 100644 index 000000000..481062caa --- /dev/null +++ b/sources/pyside2/libpyside/CMakeLists.txt @@ -0,0 +1,166 @@ +project(libpyside) + +if(${Qt5Qml_FOUND}) + if(NOT "${Qt5Qml_PRIVATE_INCLUDE_DIRS}" MATCHES "/QtQml/") + string(REPLACE "/QtCore" "/QtQml" replaceme "${Qt5Core_PRIVATE_INCLUDE_DIRS}") + list(APPEND Qt5Qml_PRIVATE_INCLUDE_DIRS ${replaceme}) + list(REMOVE_DUPLICATES Qt5Qml_PRIVATE_INCLUDE_DIRS) + endif() +endif() + +if(${Qt5Quick_FOUND}) + if(NOT "${Qt5Quick_PRIVATE_INCLUDE_DIRS}" MATCHES "/QtQuick/") + string(REPLACE "/QtCore" "/QtQuick" replaceme "${Qt5Core_PRIVATE_INCLUDE_DIRS}") + list(APPEND Qt5Quick_PRIVATE_INCLUDE_DIRS ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) + list(APPEND Qt5Quick_PRIVATE_INCLUDE_DIRS ${replaceme}) + list(REMOVE_DUPLICATES Qt5Quick_PRIVATE_INCLUDE_DIRS) + endif() +endif() + +if(Qt5Qml_FOUND) + # Used for registering custom QQuickItem classes defined in Python code. + set(QML_SUPPORT 1) + set(QML_INCLUDES ${Qt5Qml_INCLUDE_DIRS}) + set(QML_LIBRARIES ${Qt5Qml_LIBRARIES}) + + if(Qt5Qml_PRIVATE_INCLUDE_DIRS) + # Used for transforming QML exceptions into Python exceptions. + set(QML_PRIVATE_API_SUPPORT 1) + set(QML_INCLUDES ${QML_INCLUDES} ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) + else() + set(QML_PRIVATE_API_SUPPORT 0) + message(WARNING "QML private API include files could not be found, support for catching QML exceptions inside Python code will not work.") + endif() +else() + set(QML_SUPPORT 0) + set(QML_PRIVATE_API_SUPPORT 0) + set(QML_INCLUDES "") + set(QML_LIBRARIES "") +endif() + +qt5_wrap_cpp(DESTROYLISTENER_MOC "destroylistener.h") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/signalmanager.cpp.in" + "${CMAKE_CURRENT_BINARY_DIR}/signalmanager.cpp" @ONLY) + +set(libpyside_SRC + dynamicqmetaobject.cpp + destroylistener.cpp + ${CMAKE_CURRENT_BINARY_DIR}/signalmanager.cpp + globalreceiver.cpp + globalreceiverv2.cpp + pysideclassinfo.cpp + pysidemetafunction.cpp + pysidesignal.cpp + pysideslot.cpp + pysideproperty.cpp + pysideqflags.cpp + pysideweakref.cpp + pyside.cpp + ${DESTROYLISTENER_MOC} +) + +# Add python files to project explorer in Qt Creator, when opening the CMakeLists.txt as a project, +# so you can look up python files with the Locator. +macro(add_other_files) + foreach(_it ${ARGN}) + if(NOT IS_DIRECTORY ${_it}) + get_filename_component(name ${_it} NAME) + if(NOT ${_it} MATCHES "^/\\\\..*$;~$") + set_source_files_properties(${_it} PROPERTIES HEADER_FILE_ONLY TRUE) + endif() + endif() + endforeach() +endmacro() + +# Test files. +file(GLOB_RECURSE pyside_folder_py_files "../*.py") + +# Example files. +file(GLOB_RECURSE example_folder_py_files "../../pyside2-examples/*.py") + +# Mostly for setup.py. +file(GLOB setup_folder_py_files "../../../*.py") + +set(other_files ${pyside_folder_py_files} ${example_folder_py_files} ${setup_folder_py_files}) +add_other_files(${other_files}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${SHIBOKEN_INCLUDE_DIR} + ${SHIBOKEN_PYTHON_INCLUDE_DIR} + ${QML_INCLUDES} + ${Qt5Core_INCLUDE_DIRS}) +add_library(pyside2 SHARED ${libpyside_SRC} ${other_files}) +target_link_libraries(pyside2 + ${SHIBOKEN_PYTHON_LIBRARIES} + ${SHIBOKEN_LIBRARY} + ${QML_LIBRARIES} + ${Qt5Core_LIBRARIES}) + +set_target_properties(pyside2 PROPERTIES + VERSION ${BINDING_API_VERSION} + SOVERSION "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}" + OUTPUT_NAME "pyside2${pyside2_SUFFIX}${SHIBOKEN_PYTHON_EXTENSION_SUFFIX}" + DEFINE_SYMBOL PYSIDE_EXPORTS) + +if(Qt5Core_VERSION VERSION_GREATER "5.7.1") + set_property(TARGET pyside2 PROPERTY CXX_STANDARD 11) +endif() + +if(QML_SUPPORT) + target_compile_definitions(pyside2 PUBLIC PYSIDE_QML_SUPPORT=1) +endif() + +# +# install stuff +# + +set(libpyside_HEADERS + destroylistener.h + dynamicqmetaobject.h + globalreceiver.h + pysideclassinfo.h + pysideconversions.h + pysidemacros.h + signalmanager.h + pyside.h + pysidemetafunction.h + pysidesignal.h + pysideproperty.h + pysideqflags.h + pysideweakref.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") + +# create pkg-config file +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pyside2.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/pyside2${pyside2_SUFFIX}.pc" @ONLY) +# create cmake-config files +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide2Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PySide2Config.cmake" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide2Config-spec.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PySide2Config${SHIBOKEN_PYTHON_EXTENSION_SUFFIX}.cmake" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide2ConfigVersion.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PySide2ConfigVersion.cmake" @ONLY) + +install(FILES ${libpyside_HEADERS} + DESTINATION include/${BINDING_NAME}${pyside2_SUFFIX}) +install(TARGETS pyside2 EXPORT pyside2 + LIBRARY DESTINATION "${LIB_INSTALL_DIR}" + ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" + RUNTIME DESTINATION bin) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pyside2${pyside2_SUFFIX}.pc" + DESTINATION "${LIB_INSTALL_DIR}/pkgconfig") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide2Config.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide2-${BINDING_API_VERSION}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide2Config${SHIBOKEN_PYTHON_EXTENSION_SUFFIX}.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide2-${BINDING_API_VERSION}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide2ConfigVersion.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide2-${BINDING_API_VERSION}") diff --git a/sources/pyside2/libpyside/PySide2Config-spec.cmake.in b/sources/pyside2/libpyside/PySide2Config-spec.cmake.in new file mode 100644 index 000000000..205b6f10e --- /dev/null +++ b/sources/pyside2/libpyside/PySide2Config-spec.cmake.in @@ -0,0 +1,18 @@ +# PYSIDE_INCLUDE_DIR - Directories to include to use PySide2 +# PYSIDE_LIBRARY - Files to link against to use PySide2 +# PYSIDE_PYTHONPATH - Path to where the PySide2 Python module files could be found +# PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide2 + +SET(PYSIDE_INCLUDE_DIR "@CMAKE_INSTALL_PREFIX@/include/PySide2@pyside2_SUFFIX@") +# Platform specific library names +if(MSVC) + SET(PYSIDE_LIBRARY "@LIB_INSTALL_DIR@/@CMAKE_SHARED_LIBRARY_PREFIX@pyside2@pyside2_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@@SHIBOKEN_PYTHON_EXTENSION_SUFFIX@.lib") +elseif(CYGWIN) + SET(PYSIDE_LIBRARY "@LIB_INSTALL_DIR@/@CMAKE_SHARED_LIBRARY_PREFIX@pyside2@pyside2_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@@SHIBOKEN_PYTHON_EXTENSION_SUFFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@") +elseif(WIN32) + SET(PYSIDE_LIBRARY "@CMAKE_INSTALL_PREFIX@/bin/@CMAKE_SHARED_LIBRARY_PREFIX@pyside2@pyside2_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@@SHIBOKEN_PYTHON_EXTENSION_SUFFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@") +else() + SET(PYSIDE_LIBRARY "@LIB_INSTALL_DIR@/@CMAKE_SHARED_LIBRARY_PREFIX@pyside2@pyside2_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@@SHIBOKEN_PYTHON_EXTENSION_SUFFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@") +endif() +SET(PYSIDE_PYTHONPATH "@SITE_PACKAGE@") +SET(PYSIDE_TYPESYSTEMS "@CMAKE_INSTALL_PREFIX@/share/PySide2@pyside2_SUFFIX@/typesystems") diff --git a/sources/pyside2/libpyside/PySide2Config.cmake.in b/sources/pyside2/libpyside/PySide2Config.cmake.in new file mode 100644 index 000000000..cf3e6d1a6 --- /dev/null +++ b/sources/pyside2/libpyside/PySide2Config.cmake.in @@ -0,0 +1,5 @@ +if (NOT PYTHON_BASENAME) + message(STATUS "PySide2Config: Using default python: @SHIBOKEN_PYTHON_EXTENSION_SUFFIX@") + SET(PYTHON_BASENAME @SHIBOKEN_PYTHON_EXTENSION_SUFFIX@) +endif() +include(@LIB_INSTALL_DIR@/cmake/PySide2-@BINDING_API_VERSION@/PySide2Config${PYTHON_BASENAME}.cmake) diff --git a/sources/pyside2/libpyside/PySide2ConfigVersion.cmake.in b/sources/pyside2/libpyside/PySide2ConfigVersion.cmake.in new file mode 100644 index 000000000..f5073ce08 --- /dev/null +++ b/sources/pyside2/libpyside/PySide2ConfigVersion.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/pyside2/libpyside/destroylistener.cpp b/sources/pyside2/libpyside/destroylistener.cpp new file mode 100644 index 000000000..c1d3afb89 --- /dev/null +++ b/sources/pyside2/libpyside/destroylistener.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 +#include "destroylistener.h" + +#include +#include +#include +#include + +PySide::DestroyListener* PySide::DestroyListener::m_instance = 0; + +namespace PySide +{ + +struct DestroyListenerPrivate +{ + static bool m_destroyed; +}; + + +DestroyListener* DestroyListener::instance() +{ + if (!m_instance) + m_instance = new DestroyListener(0); + return m_instance; +} + +void DestroyListener::destroy() +{ + if (m_instance) { + m_instance->disconnect(); + delete m_instance; + m_instance = 0; + } +} + +void DestroyListener::listen(QObject *obj) +{ + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(obj); + if (!wrapper) // avoid problem with multiple inheritance + return; + + if (Py_IsInitialized() == 0) + onObjectDestroyed(obj); + else + QObject::connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onObjectDestroyed(QObject*)), Qt::DirectConnection); +} + +void DestroyListener::onObjectDestroyed(QObject* obj) +{ + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(obj); + if (wrapper) //make sure the object exists before destroy + Shiboken::Object::destroy(wrapper, obj); +} + +DestroyListener::DestroyListener(QObject *parent) + : QObject(parent) +{ + m_d = new DestroyListenerPrivate(); +} + +DestroyListener::~DestroyListener() +{ + delete m_d; +} + +}//namespace + diff --git a/sources/pyside2/libpyside/destroylistener.h b/sources/pyside2/libpyside/destroylistener.h new file mode 100644 index 000000000..c4aa55494 --- /dev/null +++ b/sources/pyside2/libpyside/destroylistener.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_DESTROY_LISTENER +#define PYSIDE_DESTROY_LISTENER + + +#include +#include "pysidemacros.h" + +namespace PySide +{ +struct DestroyListenerPrivate; +/// \deprecated This class is deprecated and isn't used by libpyside anymore. +class PYSIDE_API DestroyListener : public QObject +{ + Q_OBJECT + public: + PYSIDE_DEPRECATED(static DestroyListener* instance()); + static void destroy(); + void listen(QObject* obj); + + public slots: + void onObjectDestroyed(QObject* obj); + + private: + static DestroyListener* m_instance; + DestroyListenerPrivate* m_d; + DestroyListener(QObject *parent); + ~DestroyListener(); +}; + +}//namespace + +#endif + diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp new file mode 100644 index 000000000..651ed70c1 --- /dev/null +++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp @@ -0,0 +1,901 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "dynamicqmetaobject.h" +#include "dynamicqmetaobject_p.h" +#include "pysidesignal.h" +#include "pysidesignal_p.h" +#include "pysideproperty.h" +#include "pysideproperty_p.h" +#include "pysideslot_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define EMPTY_META_METHOD "0()" + +using namespace PySide; + +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000 +}; + +// these values are from moc source code, generator.cpp:66 +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 +}; + +enum MetaDataFlags { + IsUnresolvedType = 0x80000000, + TypeNameIndexMask = 0x7FFFFFFF +}; + +class DynamicQMetaObject::DynamicQMetaObjectPrivate +{ +public: + QList m_methods; + QList m_properties; + + QMap m_info; + QByteArray m_className; + bool m_updated; // when the meta data is not update + int m_methodOffset; + int m_propertyOffset; + int m_dataSize; + int m_emptyMethod; + int m_nullIndex; + + DynamicQMetaObjectPrivate() + : m_updated(false), m_methodOffset(0), m_propertyOffset(0), + m_dataSize(0), m_emptyMethod(-1), m_nullIndex(0) {} + + int createMetaData(QMetaObject* metaObj, QLinkedList &strings); + void updateMetaObject(QMetaObject* metaObj); + void writeMethodsData(const QList& methods, unsigned int** data, QLinkedList& strings, int* prtIndex, int nullIndex, int flags); + void writeStringData(char *, QLinkedList &strings); + int getPropertyNotifyId(PySideProperty *property) const; +}; + +bool sortMethodSignalSlot(const MethodData &m1, const MethodData &m2) +{ + if (m1.methodType() == QMetaMethod::Signal) + return m2.methodType() == QMetaMethod::Slot; + return false; +} + +static int registerString(const QByteArray& s, QLinkedList& strings) +{ + int idx = 0; + QLinkedList::const_iterator it = strings.begin(); + QLinkedList::const_iterator itEnd = strings.end(); + while (it != itEnd) { + if (strcmp(*it, s) == 0) + return idx; + ++idx; + ++it; + } + strings.append(s); + return idx; +} + +static int blobSize(QLinkedList &strings) +{ + int size = strings.size() * sizeof(QByteArrayData); + + QByteArray str; + QByteArray debug_str; + foreach (const QByteArray &field, strings) { + str.append(field); + str.append(char(0)); + + debug_str.append(field); + debug_str.append('|'); + } + //qDebug()< &methods) +{ + int sum = 0; + for (int i = 0; i < methods.size(); ++i) + sum += methods.at(i).parameterCount() * 2 + 1; // nb_param*2 (type and names) +1 for return type + return sum; +} + +static void writeString(char *out, int i, const QByteArray &str, + const int offsetOfStringdataMember, int &stringdataOffset) +{ + int size = str.size(); + qptrdiff offset = offsetOfStringdataMember + stringdataOffset + - i * sizeof(QByteArrayData); + const QByteArrayData data = + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset); + + memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); + + memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); + out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; + + stringdataOffset += size + 1; +} + +static int qvariant_nameToType(const char* name) +{ + if (!name) + return 0; + + if (strcmp(name, "QVariant") == 0) + return 0xffffffff; + if (strcmp(name, "QCString") == 0) + return QMetaType::QByteArray; + if (strcmp(name, "Q_LLONG") == 0) + return QMetaType::LongLong; + if (strcmp(name, "Q_ULLONG") == 0) + return QMetaType::ULongLong; + if (strcmp(name, "QIconSet") == 0) + return QMetaType::QIcon; + + uint tp = QMetaType::type(name); + return tp < QMetaType::User ? tp : 0; +} + +/* + Returns true if the type is a QVariant types. +*/ +static bool isVariantType(const char* type) +{ + return qvariant_nameToType(type) != 0; +} + +/*! + Returns true if the type is qreal. +*/ +static bool isQRealType(const char *type) +{ + return strcmp(type, "qreal") == 0; +} + +uint PropertyData::flags() const +{ + const QByteArray btype(type()); + const char* typeName = btype.data(); + uint flags = Invalid; + if (!isVariantType(typeName)) + flags |= EnumOrFlag; + else if (!isQRealType(typeName)) + flags |= qvariant_nameToType(typeName) << 24; + + if (PySide::Property::isReadable(m_data)) + flags |= Readable; + + if (PySide::Property::isWritable(m_data)) + flags |= Writable; + + if (PySide::Property::hasReset(m_data)) + flags |= Resettable; + + if (PySide::Property::isDesignable(m_data)) + flags |= Designable; + else + flags |= ResolveDesignable; + + if (PySide::Property::isScriptable(m_data)) + flags |= Scriptable; + else + flags |= ResolveScriptable; + + if (PySide::Property::isStored(m_data)) + flags |= Stored; + else + flags |= ResolveStored; + + //EDITABLE + flags |= ResolveEditable; + + if (PySide::Property::isUser(m_data)) + flags |= User; + else + flags |= ResolveUser; + + if (m_cachedNotifyId != -1) + flags |= Notify; + + if (PySide::Property::isConstant(m_data)) + flags |= Constant; + + if (PySide::Property::isFinal(m_data)) + flags |= Final; + + return flags; +} + +// const QByteArray with EMPTY_META_METHOD, used to save some memory +const QByteArray MethodData::m_emptySig(EMPTY_META_METHOD); + +MethodData::MethodData() + : m_signature(m_emptySig) +{ +} + +MethodData::MethodData(QMetaMethod::MethodType mtype, const QByteArray& signature, const QByteArray& rtype) + : m_signature(QMetaObject::normalizedSignature(signature.constData())) + , m_rtype(QMetaObject::normalizedSignature(rtype.constData())) + , m_mtype(mtype) +{ +} + +void MethodData::clear() +{ + m_signature = m_emptySig; + m_rtype.clear(); +} + +bool MethodData::isValid() const +{ + return m_signature != m_emptySig; +} + +QList MethodData::parameterTypes() const +{ + const char *signature = m_signature.constData(); + QList list; + while (*signature && *signature != '(') + ++signature; + while (*signature && *signature != ')' && *++signature != ')') { + const char *begin = signature; + int level = 0; + while (*signature && (level > 0 || *signature != ',') && *signature != ')') { + if (*signature == '<') + ++level; + else if (*signature == '>') + --level; + ++signature; + } + list += QByteArray(begin, signature - begin); + } + return list; +} + +int MethodData::parameterCount() const +{ + return parameterTypes().size(); +} + +QByteArray MethodData::name() const +{ + return m_signature.left(qMax(m_signature.indexOf('('), 0)); +} + +PropertyData::PropertyData() + : m_cachedNotifyId(0), m_data(0) +{ +} + +PropertyData::PropertyData(const char* name, int notifyId, PySideProperty* data) + : m_name(name), m_cachedNotifyId(notifyId), m_data(data) +{ +} + +QByteArray PropertyData::type() const +{ + return QByteArray(PySide::Property::getTypeName(m_data)); +} + + +bool PropertyData::isValid() const +{ + return !m_name.isEmpty(); +} + +int PropertyData::cachedNotifyId() const +{ + return m_cachedNotifyId; +} + +bool PropertyData::operator==(const PropertyData& other) const +{ + return m_data == other.m_data; +} + +bool PropertyData::operator==(const char* name) const +{ + return m_name == name; +} + + +DynamicQMetaObject::DynamicQMetaObject(PyTypeObject* type, const QMetaObject* base) + : m_d(new DynamicQMetaObjectPrivate) +{ + d.superdata = base; + d.stringdata = NULL; + d.data = NULL; + d.extradata = NULL; + d.relatedMetaObjects = NULL; + d.static_metacall = NULL; + + m_d->m_className = QByteArray(type->tp_name).split('.').last(); + m_d->m_methodOffset = base->methodCount() - 1; + m_d->m_propertyOffset = base->propertyCount() - 1; + parsePythonType(type); +} + +DynamicQMetaObject::DynamicQMetaObject(const char* className, const QMetaObject* metaObject) + : m_d(new DynamicQMetaObjectPrivate) +{ + d.superdata = metaObject; + d.stringdata = 0; + d.data = 0; + d.extradata = 0; + d.relatedMetaObjects = NULL; + d.static_metacall = NULL; + + m_d->m_className = className; + m_d->m_methodOffset = metaObject->methodCount() - 1; + m_d->m_propertyOffset = metaObject->propertyCount() - 1; +} + +DynamicQMetaObject::~DynamicQMetaObject() +{ + free(reinterpret_cast(const_cast(d.stringdata))); + free(const_cast(d.data)); + delete m_d; +} + +int DynamicQMetaObject::addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type) +{ + int index = -1; + int counter = 0; + + QList::iterator it = m_d->m_methods.begin(); + for (; it != m_d->m_methods.end(); ++it) { + if ((it->signature() == signature) && (it->methodType() == mtype)) + return m_d->m_methodOffset + counter; + else if (!it->isValid()) { + index = counter; + } + counter++; + } + + // Common mistake not to add parentheses to the signature. + if ((strchr(signature, ')') == 0) || ((strchr(signature, '(') == 0))) { + const QString message = + QLatin1String("DynamicQMetaObject::addMethod: Invalid method signature " + "provided for ") + QLatin1String(signature); + const QByteArray messageLatin = message.toLatin1(); + PyErr_WarnEx(PyExc_RuntimeWarning, messageLatin.constData(), 0); + return -1; + } + + //has blank method + if (index != -1) { + m_d->m_methods[index] = MethodData(mtype, signature, type); + index++; + } else { + m_d->m_methods << MethodData(mtype, signature, type); + index = m_d->m_methods.size(); + } + + m_d->m_updated = false; + return m_d->m_methodOffset + index; +} + +void DynamicQMetaObject::removeMethod(QMetaMethod::MethodType mtype, uint index) +{ + const char* methodSig = method(index).methodSignature(); + QList::iterator it = m_d->m_methods.begin(); + for (; it != m_d->m_methods.end(); ++it) { + if ((it->signature() == methodSig) && (it->methodType() == mtype)){ + it->clear(); + m_d->m_updated = false; + break; + } + } +} + +int DynamicQMetaObject::addSignal(const char* signal, const char* type) +{ + return addMethod(QMetaMethod::Signal, signal, type); +} + +int DynamicQMetaObject::addSlot(const char* slot, const char* type) +{ + return addMethod(QMetaMethod::Slot, slot, type); +} + +void DynamicQMetaObject::removeSlot(uint index) +{ + removeMethod(QMetaMethod::Slot, index); +} + +void DynamicQMetaObject::removeSignal(uint index) +{ + removeMethod(QMetaMethod::Signal, index); +} + +int DynamicQMetaObject::addProperty(const char* propertyName, PyObject* data) +{ + int index = m_d->m_properties.indexOf(propertyName); + if (index != -1) + return m_d->m_propertyOffset + index; + + // retrieve notifyId + PySideProperty *property = reinterpret_cast(data); + const int notifyId = m_d->getPropertyNotifyId(property); + + //search for a empty space + PropertyData blank; + index = m_d->m_properties.indexOf(blank); + if (index != -1) { + m_d->m_properties[index] = PropertyData(propertyName, notifyId, property); + } else { + m_d->m_properties << PropertyData(propertyName, notifyId, property); + index = m_d->m_properties.size(); + } + m_d->m_updated = false; + return m_d->m_propertyOffset + index; +} + +int DynamicQMetaObject::DynamicQMetaObjectPrivate::getPropertyNotifyId(PySideProperty *property) const +{ + int notifyId = -1; + if (property->d->notify) { + const char *signalNotify = PySide::Property::getNotifyName(property); + if (signalNotify) { + const MethodData signalObject(QMetaMethod::Signal, signalNotify, ""); + notifyId = m_methods.indexOf(signalObject); + } + } + return notifyId; +} + +void DynamicQMetaObject::addInfo(const char* key, const char* value) +{ + m_d->m_info[key] = value; +} + +void DynamicQMetaObject::addInfo(QMap info) +{ + QMap::const_iterator i = info.constBegin(); + while (i != info.constEnd()) { + m_d->m_info[i.key()] = i.value(); + ++i; + } + m_d->m_updated = false; +} + +const QMetaObject* DynamicQMetaObject::update() const +{ + if (!m_d->m_updated) { + m_d->updateMetaObject(const_cast(this)); + m_d->m_updated = true; + } + return this; +} + +void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeMethodsData(const QList& methods, + unsigned int** data, + QLinkedList& strings, + int* prtIndex, + int nullIndex, + int flags) +{ + int index = *prtIndex; + int paramsIndex = index + methods.count() * 5; + + QList::const_iterator it = methods.begin(); + + if (m_emptyMethod == -1) + m_emptyMethod = registerString(EMPTY_META_METHOD, strings); + + for (; it != methods.end(); ++it) { + int name_idx = 0; + int argc = it->parameterCount(); + if (it->signature() != EMPTY_META_METHOD) + name_idx = registerString(it->name(), strings); + else + name_idx = m_emptyMethod; // func name + + (*data)[index++] = name_idx; + (*data)[index++] = argc; // argc (previously: arg name) + (*data)[index++] = paramsIndex; //parameter index + (*data)[index++] = nullIndex; // tags + (*data)[index++] = flags | (it->methodType() == QMetaMethod::Signal ? MethodSignal : MethodSlot); + + if (it->methodType() == QMetaMethod::Signal) + (*data)[13] += 1; //signal count + + paramsIndex += 1 + argc * 2; + } + *prtIndex = index; +} + +void DynamicQMetaObject::parsePythonType(PyTypeObject *type) +{ + // Get all non-QObject-derived base types in method resolution order, filtering out the types + // that can't have signals, slots or properties. + // This enforces registering of all signals and slots at type parsing time, and not later at + // signal connection time, thus making sure no method indices change which would break + // existing connections. + const PyObject *mro = type->tp_mro; + const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro); + PyTypeObject *qObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + QVector basesToCheck; + for (Py_ssize_t i = 0; i < basesCount; ++i) { + PyTypeObject *baseType = reinterpret_cast(PyTuple_GET_ITEM(mro, i)); + if (PyType_IsSubtype(baseType, qObjectType) + || baseType == reinterpret_cast(&SbkObject_Type) + || baseType == reinterpret_cast(&PyBaseObject_Type)) { + continue; + } else { + basesToCheck.append(baseType); + } + } + + // Prepend the actual type that we are parsing. + basesToCheck.prepend(type); + // PYSIDE-315: Handle all signals first, in all involved types. + for (int baseIndex = 0, baseEnd = basesToCheck.size(); baseIndex < baseEnd; ++baseIndex) { + PyTypeObject *baseType = basesToCheck[baseIndex]; + PyObject *attrs = baseType->tp_dict; + PyObject *key = 0; + PyObject *value = 0; + Py_ssize_t pos = 0; + + while (PyDict_Next(attrs, &pos, &key, &value)) { + if (Signal::checkType(value)) { + // Register signals. + PySideSignal *data = reinterpret_cast(value); + const char *signalName = Shiboken::String::toCString(key); + data->signalName = strdup(signalName); + QByteArray sig; + sig.reserve(128); + for (int i = 0; i < data->signaturesSize; ++i) { + sig = signalName; + sig += '('; + if (data->signatures[i]) + sig += data->signatures[i]; + sig += ')'; + if (d.superdata->indexOfSignal(sig) == -1) + addSignal(sig, "void"); + } + } + } + } + + Shiboken::AutoDecRef slotAttrName(Shiboken::String::fromCString(PYSIDE_SLOT_LIST_ATTR)); + // PYSIDE-315: Now take care of the rest. + // Signals and slots should be separated, unless the types are modified, later. + // We check for this using "is_sorted()". Sorting no longer happens at all. + for (int baseIndex = 0, baseEnd = basesToCheck.size(); baseIndex < baseEnd; ++baseIndex) { + PyTypeObject *baseType = basesToCheck[baseIndex]; + PyObject *attrs = baseType->tp_dict; + PyObject *key = 0; + PyObject *value = 0; + Py_ssize_t pos = 0; + + typedef std::pair PropPair; + QVector properties; + + while (PyDict_Next(attrs, &pos, &key, &value)) { + if (Property::checkType(value)) { + // Leave the properties to be registered after signals because they may depend on + // notify signals. + int index = d.superdata->indexOfProperty(Shiboken::String::toCString(key)); + if (index == -1) + properties << PropPair(Shiboken::String::toCString(key), value); + } else if (PyFunction_Check(value)) { + // Register slots. + if (PyObject_HasAttr(value, slotAttrName)) { + PyObject *signatureList = PyObject_GetAttr(value, slotAttrName); + for (Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) { + PyObject *signature = PyList_GET_ITEM(signatureList, i); + QByteArray sig(Shiboken::String::toCString(signature)); + // Split the slot type and its signature. + QList slotInfo = sig.split(' '); + int index = d.superdata->indexOfSlot(slotInfo[1]); + if (index == -1) + addSlot(slotInfo[1], slotInfo[0]); + } + } + } + } + + // Register properties + foreach (const PropPair &propPair, properties) + addProperty(propPair.first, propPair.second); + } +} + +/*! + Allocate the meta data table. + Returns the index in the table corresponding to the header fields count. +*/ +int DynamicQMetaObject::DynamicQMetaObjectPrivate::createMetaData(QMetaObject* metaObj, QLinkedList &strings) +{ + const int n_methods = m_methods.size(); + const int n_properties = m_properties.size(); + const int n_info = m_info.size(); + + int header[] = {7, // revision (Used by moc, qmetaobjectbuilder and qdbus) + 0, // class name index in m_metadata + n_info, 0, // classinfo and classinfo index + n_methods, 0, // method count and method list index + n_properties, 0, // prop count and prop indexes + 0, 0, // enum count and enum index + 0, 0, // constructors (since revision 2) + 0, // flags (since revision 3) + 0}; // signal count (since revision 4) + + const int HEADER_LENGHT = sizeof(header)/sizeof(int); + + m_dataSize = HEADER_LENGHT; + m_dataSize += n_info*2; //class info: name, value + m_dataSize += n_methods*5; //method: name, argc, parameters, tag, flags + m_dataSize += n_properties*4; //property: name, type, flags + m_dataSize += 1; //eod + + m_dataSize += aggregateParameterCount(m_methods); // types and parameter names + + uint* data = reinterpret_cast(realloc(const_cast(metaObj->d.data), m_dataSize * sizeof(uint))); + + Q_ASSERT(data); + std::memcpy(data, header, sizeof(header)); + + metaObj->d.data = data; + + return HEADER_LENGHT; +} + +// Writes strings to string data struct. +// The struct consists of an array of QByteArrayData, followed by a char array +// containing the actual strings. This format must match the one produced by +// moc (see generator.cpp). +void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeStringData(char *out, QLinkedList &strings) +{ + Q_ASSERT(!(reinterpret_cast(out) & (Q_ALIGNOF(QByteArrayData)-1))); + + int offsetOfStringdataMember = strings.size() * sizeof(QByteArrayData); + int stringdataOffset = 0; + int i = 0; + foreach(const QByteArray& str, strings) { + writeString(out, i, str, offsetOfStringdataMember, stringdataOffset); + i++; + } +} + +QList::iterator is_sorted_until(QList::iterator first, + QList::iterator last, + bool comp(const MethodData &m1, const MethodData &m2)) +{ + if (first != last) { + QList::iterator next = first; + while (++next != last) { + if (comp(*next, *first)) + return next; + ++first; + } + } + return last; +} + +bool is_sorted(QList::iterator first, QList::iterator last, + bool comp(const MethodData &m1, const MethodData &m2)) +{ + return is_sorted_until(first, last, comp) == last; +} + +void DynamicQMetaObject::DynamicQMetaObjectPrivate::updateMetaObject(QMetaObject* metaObj) +{ + Q_ASSERT(!m_updated); + uint *data = const_cast(metaObj->d.data); + int index = 0; + QLinkedList strings; + m_dataSize = 0; + + // Recompute the size and reallocate memory + // index is set after the last header field. + index = createMetaData(metaObj, strings); + data = const_cast(metaObj->d.data); + + registerString(m_className, strings); // register class string + m_nullIndex = registerString("", strings); // register a null string + + // Write class info. + if (m_info.size()) { + if (data[3] == 0) + data[3] = index; + + QMap::const_iterator i = m_info.constBegin(); //TODO: info is a hash this can fail + while (i != m_info.constEnd()) { + int valueIndex = registerString(i.value(), strings); + int keyIndex = registerString(i.key(), strings); + data[index++] = keyIndex; + data[index++] = valueIndex; + i++; + } + } + + // Write methods first, then properties, to be consistent with moc. + // Write signals/slots (signals must be written first, see indexOfMethodRelative in + // qmetaobject.cpp). + + QList::iterator it; + // PYSIDE-315: Instead of sorting the items and maybe breaking indices, + // we ensure that the signals and slots are sorted by the improved parsePythonType(). + // The order can only become distorted if the class is modified after creation. + // In that case, we give a warning. + if (!is_sorted(m_methods.begin(), m_methods.end(), sortMethodSignalSlot)) { + const char *metaObjectName = this->m_className.data(); + PyObject *txt = PyBytes_FromFormat("\n\n*** Sort Warning ***\n" + "Signals and slots in QMetaObject '%s' are not ordered correctly, " + "this may lead to issues.\n", metaObjectName); + it = m_methods.begin(); + QList::iterator end = m_methods.end(); + QList::iterator until = is_sorted_until(m_methods.begin(), m_methods.end(), + sortMethodSignalSlot); + for (; it != end; ++it) { + PyObject *atxt = PyBytes_FromFormat("%d%s %s %s\n", it - m_methods.begin() + 1, + until >= it + 1 ? " " : "!", + it->methodType() == QMetaMethod::Signal ? "Signal" : "Slot ", + it->signature().data() ); + PyBytes_ConcatAndDel(&txt, atxt); + } + PyErr_WarnEx(PyExc_RuntimeWarning, PyBytes_AsString(txt), 0); + Py_DECREF(txt); + // Prevent a warning from being turned into an error. We cannot easily unwind. + PyErr_Clear(); + } + + if (m_methods.size()) { + if (data[5] == 0) + data[5] = index; + + writeMethodsData(m_methods, &data, strings, &index, m_nullIndex, AccessPublic); + } + + // Write signal/slots parameters. + if (m_methods.size()) { + for (it = m_methods.begin(); it != m_methods.end(); ++it) { + QList paramTypeNames = it->parameterTypes(); + int paramCount = paramTypeNames.size(); + for (int i = -1; i < paramCount; ++i) { + const QByteArray &typeName = (i < 0) ? it->returnType() : paramTypeNames.at(i); + int typeInfo; + if (QtPrivate::isBuiltinType(typeName)) + typeInfo = QMetaType::type(typeName); + else + typeInfo = IsUnresolvedType | registerString(typeName, strings); + data[index++] = typeInfo; + } + + // Parameter names (use a null string) + for (int i = 0; i < paramCount; ++i) { + data[index++] = m_nullIndex; + } + } + } + + // Write properties. + if (m_properties.size()) { + if (data[7] == 0) + data[7] = index; + + QList::const_iterator i = m_properties.constBegin(); + while (i != m_properties.constEnd()) { + if (i->isValid()) { + data[index++] = registerString(i->name(), strings); // name + } else + data[index++] = m_nullIndex; + + // Find out the property type index. + int typeInfo = m_nullIndex; + if (i->isValid()) { + const QByteArray &typeName = i->type(); + if (QtPrivate::isBuiltinType(typeName)) + typeInfo = QMetaType::type(typeName); + else + typeInfo = IsUnresolvedType | registerString(typeName, strings); + } + data[index++] = typeInfo; // normalized type + + data[index++] = i->flags(); + i++; + } + + // Write properties notify. + i = m_properties.constBegin(); + while (i != m_properties.constEnd()) { + // Recompute notifyId, because sorting the methods might have changed the relative + // index. + const int notifyId = getPropertyNotifyId(i->data()); + data[index++] = notifyId >= 0 ? static_cast(notifyId) : 0; //signal notify index + i++; + } + } + + data[index++] = 0; // the end + + // Create the m_metadata string. + int size = blobSize(strings); + char *blob = + reinterpret_cast(realloc(reinterpret_cast(const_cast(metaObj->d.stringdata)), size)); + writeStringData(blob, strings); + + metaObj->d.stringdata = reinterpret_cast(blob); + metaObj->d.data = data; +} diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.h b/sources/pyside2/libpyside/dynamicqmetaobject.h new file mode 100644 index 000000000..ac8f4644d --- /dev/null +++ b/sources/pyside2/libpyside/dynamicqmetaobject.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 DYNAMICQMETAOBJECT_H +#define DYNAMICQMETAOBJECT_H + +#include "pysidemacros.h" +#include +#include +#include + +namespace PySide +{ + +class DynamicQMetaObject : public QMetaObject +{ +public: + DynamicQMetaObject(const char* className, const QMetaObject* metaObject); + DynamicQMetaObject(PyTypeObject* type, const QMetaObject* metaobject); + ~DynamicQMetaObject(); + + + int addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type); + void removeMethod(QMetaMethod::MethodType mtype, uint index); + int addSignal(const char* signal, const char* type = 0); + int addSlot(const char* slot, const char* type = 0); + int addProperty(const char* property, PyObject* data); + void addInfo(const char* key, const char* value); + void addInfo(QMap info); + + void removeSignal(uint idex); + void removeSlot(uint index); + void removeProperty(uint index); + + const QMetaObject* update() const; + +private: + class DynamicQMetaObjectPrivate; + DynamicQMetaObjectPrivate* m_d; + + void parsePythonType(PyTypeObject *type); +}; + + +} +#endif diff --git a/sources/pyside2/libpyside/dynamicqmetaobject_p.h b/sources/pyside2/libpyside/dynamicqmetaobject_p.h new file mode 100644 index 000000000..202e87b08 --- /dev/null +++ b/sources/pyside2/libpyside/dynamicqmetaobject_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 DYNAMICMETAPROPERTY_P_H +#define DYNAMICMETAPROPERTY_P_H + +#include +#include +#include + +#define GLOBAL_RECEIVER_CLASS_NAME "__GlobalReceiver__" + +struct PySideProperty; +namespace PySide +{ + class MethodData + { + public: + MethodData(); + /** + * \param signature method signature + * \param type method return type + */ + MethodData(QMetaMethod::MethodType mtype, + const QByteArray& signature, + const QByteArray& rtype = QByteArray("void")); + void clear(); + bool isValid() const; + const QByteArray& signature() const { return m_signature; } + const QByteArray& returnType() const { return m_rtype; } + QMetaMethod::MethodType methodType() const { return m_mtype; } + //Qt5 moc: now we have to store method parameter names, count, type + QList parameterTypes() const; + int parameterCount() const; + QByteArray name() const; + bool operator==(const MethodData& other) const; + + private: + QByteArray m_signature; + QByteArray m_rtype; + QMetaMethod::MethodType m_mtype; + static const QByteArray m_emptySig; + }; + + class PropertyData + { + public: + PropertyData(); + PropertyData(const char *name, int cachedNotifyId = 0, PySideProperty *data = 0); + const QByteArray& name() const { return m_name; } + PySideProperty *data() const { return m_data; } + QByteArray type() const; + uint flags() const; + bool isValid() const; + int cachedNotifyId() const; + bool operator==(const PropertyData& other) const; + bool operator==(const char* name) const; + + private: + QByteArray m_name; + int m_cachedNotifyId; + PySideProperty* m_data; + }; + +inline bool MethodData::operator==(const MethodData& other) const +{ + return m_mtype == other.methodType() && m_signature == other.signature(); +} + +} + +#endif diff --git a/sources/pyside2/libpyside/globalreceiver.cpp b/sources/pyside2/libpyside/globalreceiver.cpp new file mode 100644 index 000000000..e183e09ba --- /dev/null +++ b/sources/pyside2/libpyside/globalreceiver.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "globalreceiver.h" +#include "dynamicqmetaobject_p.h" +#include "pysideweakref.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "typeresolver.h" +#include "signalmanager.h" + +#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" + +namespace PySide +{ +class DynamicSlotData +{ + public: + DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent); + void addRef(const QObject* o); + void decRef(const QObject* o); + void clear(); + int hasRefTo(const QObject* o) const; + int refCount() const; + int id() const; + PyObject* call(PyObject* args); + ~DynamicSlotData(); + static void onCallbackDestroyed(void* data); + + private: + int m_id; + bool m_isMethod; + PyObject* m_callback; + PyObject* m_pythonSelf; + PyObject* m_pyClass; + PyObject* m_weakRef; + GlobalReceiver* m_parent; + QLinkedList m_refs; +}; + +} + +using namespace PySide; + +DynamicSlotData::DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent) + : m_id(id), m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent) +{ + Shiboken::GilState gil; + + m_isMethod = PyMethod_Check(callback); + if (m_isMethod) { + //Can not store calback pointe because this will be destroyed at the end of the scope + //To avoid increment intance reference keep the callback information + m_callback = PyMethod_GET_FUNCTION(callback); +#ifdef IS_PY3K + m_pyClass = 0; +#else + m_pyClass = PyMethod_GET_CLASS(callback); +#endif + + m_pythonSelf = PyMethod_GET_SELF(callback); + + //monitor class from method lifetime + m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotData::onCallbackDestroyed, this); + } else { + m_callback = callback; + Py_INCREF(m_callback); + } +} + +PyObject* DynamicSlotData::call(PyObject* args) +{ + PyObject* callback = m_callback; + + //create a callback based on method data + Shiboken::GilState gil; + if (m_isMethod) +#ifdef IS_PY3K + callback = PyMethod_New(callback, m_pythonSelf); +#else + callback = PyMethod_New(callback, m_pythonSelf, m_pyClass); +#endif + + PyObject* result = PyObject_CallObject(callback, args); + + if (m_isMethod) + Py_DECREF(callback); + + return result; +} + +void DynamicSlotData::addRef(const QObject *o) +{ + m_refs.append(o); +} + +void DynamicSlotData::decRef(const QObject *o) +{ + m_refs.removeOne(o); +} + +int DynamicSlotData::refCount() const +{ + return m_refs.size(); +} + +int DynamicSlotData::id() const +{ + return m_id; +} + +int DynamicSlotData::hasRefTo(const QObject *o) const +{ + return m_refs.count(o); +} + +void DynamicSlotData::clear() +{ + Shiboken::GilState gil; + Py_XDECREF(m_weakRef); + m_weakRef = 0; + m_refs.clear(); +} + +DynamicSlotData::~DynamicSlotData() +{ + Shiboken::GilState gil; + clear(); + if (!m_isMethod) + Py_DECREF(m_callback); +} + +void DynamicSlotData::onCallbackDestroyed(void *data) +{ + Shiboken::GilState gil; + DynamicSlotData* self = reinterpret_cast(data); + + //Disconnect all sources + QMetaMethod m = self->m_parent->metaObject()->method(self->m_id); + QByteArray methodName = QByteArray::number(m.methodType()).append(m.methodSignature()); + QLinkedList sources = self->m_refs; + foreach(const QObject* src, sources) + const_cast(src)->disconnect(self->m_parent, methodName); + self->m_weakRef = 0; +} + +GlobalReceiver::GlobalReceiver() + : m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject) +{ + //slot used to be notifyed of object destrouction + m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); + m_metaObject.update(); + setObjectName(QLatin1String("GLOBAL RECEIVER")); +} + +GlobalReceiver::~GlobalReceiver() +{ + while(!m_slotReceivers.empty()) { + DynamicSlotData* data = m_slotReceivers.take(m_slotReceivers.begin().key()); + data->clear(); + delete data; + } +} + +void GlobalReceiver::connectNotify(QObject* source, int slotId) +{ + if (m_slotReceivers.contains(slotId)) { + DynamicSlotData* data = m_slotReceivers[slotId]; + if (!data->hasRefTo(source)) + QObject::connect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME); + data->addRef(source); + } +} + +void GlobalReceiver::disconnectNotify(QObject* source, int slotId) +{ + if (m_slotReceivers.contains(slotId)) { + DynamicSlotData *data = m_slotReceivers[slotId]; + data->decRef(source); + if (data->refCount() == 0) + removeSlot(slotId); + + if (!hasConnectionWith(source)) + QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME); + } +} + +const QMetaObject* GlobalReceiver::metaObject() const +{ + return m_metaObject.update(); +} + +int GlobalReceiver::addSlot(const char* slot, PyObject* callback) +{ + int slotId = m_metaObject.addSlot(slot); + if (!m_slotReceivers.contains(slotId)) + m_slotReceivers[slotId] = new DynamicSlotData(slotId, callback, this); + + bool isShortCircuit = true; + for (int i = 0; slot[i]; ++i) { + if (slot[i] == '(') { + isShortCircuit = false; + break; + } + } + + if (isShortCircuit) + m_shortCircuitSlots << slotId; + + Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount()); + return slotId; +} + +void GlobalReceiver::removeSlot(int slotId) +{ + if (m_slotReceivers.contains(slotId)) { + delete m_slotReceivers.take(slotId); + m_metaObject.removeSlot(slotId); + m_shortCircuitSlots.remove(slotId); + } +} + +bool GlobalReceiver::hasConnectionWith(const QObject *object) +{ + QHash::iterator i = m_slotReceivers.begin(); + while(i != m_slotReceivers.end()) { + if (i.value()->hasRefTo(object)) { + return true; + } + i++; + } + return false; +} + +int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); + QMetaMethod slot = metaObject()->method(id); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + + if (strcmp(slot.methodSignature(), RECEIVER_DESTROYED_SLOT_NAME) == 0) { + QObject *arg = *(QObject**)args[1]; + + //avoid hash changes during the destruction + QHash copy = m_slotReceivers; + QHash::iterator i = copy.begin(); + while(i != copy.end()) { + //Remove all refs + int refs = i.value()->hasRefTo(arg); + while(refs) { + disconnectNotify(arg, i.key()); + refs--; + } + i++; + } + return -1; + } + + DynamicSlotData* data = m_slotReceivers.value(id); + if (!data) { + qWarning() << "Unknown global slot, id:" << id; + return -1; + } + + Shiboken::GilState gil; + PyObject* retval = 0; + if (m_shortCircuitSlots.contains(id)) { + retval = data->call(reinterpret_cast(args[1])); + } else { + QList paramTypes = slot.parameterTypes(); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); + for (int i = 0, max = paramTypes.count(); i < max; ++i) { + const QByteArray& paramType = paramTypes[i]; + Shiboken::Conversions::SpecificConverter converter(paramType.constData()); + PyTuple_SET_ITEM(preparedArgs.object(), i, converter.toPython(args[i+1])); + } + retval = data->call(preparedArgs); + } + + if (!retval) + PyErr_Print(); + else + Py_DECREF(retval); + + return -1; +} diff --git a/sources/pyside2/libpyside/globalreceiver.h b/sources/pyside2/libpyside/globalreceiver.h new file mode 100644 index 000000000..d1114465a --- /dev/null +++ b/sources/pyside2/libpyside/globalreceiver.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 GLOBALRECEIVER_H +#define GLOBALRECEIVER_H + +#include +#include +#include +#include +#include "dynamicqmetaobject.h" + +namespace PySide +{ + +class DynamicSlotData; + +class GlobalReceiver : public QObject +{ +public: + GlobalReceiver(); + ~GlobalReceiver(); + int qt_metacall(QMetaObject::Call call, int id, void** args); + const QMetaObject* metaObject() const; + int addSlot(const char* slot, PyObject* callback); + void removeSlot(int slotId); + void connectNotify(QObject* sender, int slotId); + void disconnectNotify(QObject* sender, int slotId); + bool hasConnectionWith(const QObject* object); + +protected: + using QObject::connectNotify; + using QObject::disconnectNotify; + +private: + DynamicQMetaObject m_metaObject; + QSet m_shortCircuitSlots; + QHash m_slotReceivers; +}; + +} + +#endif + diff --git a/sources/pyside2/libpyside/globalreceiverv2.cpp b/sources/pyside2/libpyside/globalreceiverv2.cpp new file mode 100644 index 000000000..a79d43c20 --- /dev/null +++ b/sources/pyside2/libpyside/globalreceiverv2.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "globalreceiverv2.h" +#include "dynamicqmetaobject_p.h" +#include "pysideweakref.h" + +#include +#include +#include +#include +#include +#include + +#include "typeresolver.h" +#include "signalmanager.h" + +#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" + +namespace +{ + static int DESTROY_SIGNAL_ID = 0; + static int DESTROY_SLOT_ID = 0; +} + +namespace PySide +{ +class DynamicSlotDataV2 +{ + public: + DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent); + ~DynamicSlotDataV2(); + + int addSlot(const char* signature); + int id(const char* signature) const; + PyObject* callback(); + QByteArray hash() const; + void notify(); + + static void onCallbackDestroyed(void* data); + static QByteArray hash(PyObject *callback); + + + private: + bool m_isMethod; + PyObject* m_callback; + PyObject* m_pythonSelf; + PyObject* m_pyClass; + PyObject* m_weakRef; + QMap m_signatures; + GlobalReceiverV2* m_parent; + QByteArray m_hash; +}; + +} + +using namespace PySide; + +DynamicSlotDataV2::DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent) + : m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent) +{ + Shiboken::GilState gil; + + m_isMethod = PyMethod_Check(callback); + if (m_isMethod) { + //Can not store calback pointe because this will be destroyed at the end of the scope + //To avoid increment intance reference keep the callback information + m_callback = PyMethod_GET_FUNCTION(callback); +#ifndef IS_PY3K + m_pyClass = PyMethod_GET_CLASS(callback); +#endif + m_pythonSelf = PyMethod_GET_SELF(callback); + + //monitor class from method lifetime + m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this); + + m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback)) + + QByteArray::number((qlonglong)PyObject_Hash(m_pythonSelf)); + + } else { + m_callback = callback; + Py_INCREF(m_callback); + + m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback)); + } +} + +QByteArray DynamicSlotDataV2::hash() const +{ + return m_hash; +} + +QByteArray DynamicSlotDataV2::hash(PyObject* callback) +{ + Shiboken::GilState gil; + if (PyMethod_Check(callback)) + return QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_FUNCTION(callback))) + + QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_SELF(callback))); + else + return QByteArray::number((qlonglong)PyObject_Hash(callback)); +} + +PyObject* DynamicSlotDataV2::callback() +{ + PyObject* callback = m_callback; + + //create a callback based on method data + if (m_isMethod) +#ifdef IS_PY3K + callback = PyMethod_New(m_callback, m_pythonSelf); +#else + callback = PyMethod_New(m_callback, m_pythonSelf, m_pyClass); +#endif + else + Py_INCREF(callback); + + return callback; +} + +int DynamicSlotDataV2::id(const char* signature) const +{ + if (m_signatures.contains(signature)) + return m_signatures[signature]; + return -1; +} + +int DynamicSlotDataV2::addSlot(const char* signature) +{ + int index = id(signature); + if (index == -1) { + DynamicQMetaObject *dmo = const_cast(reinterpret_cast(m_parent->metaObject())); + index = m_signatures[signature] = dmo->addSlot(signature); + } + return index; +} + +void DynamicSlotDataV2::onCallbackDestroyed(void *data) +{ + DynamicSlotDataV2* self = reinterpret_cast(data); + self->m_weakRef = 0; + Py_BEGIN_ALLOW_THREADS + delete self->m_parent; + Py_END_ALLOW_THREADS +} + +DynamicSlotDataV2::~DynamicSlotDataV2() +{ + Shiboken::GilState gil; + + Py_XDECREF(m_weakRef); + m_weakRef = 0; + + if (!m_isMethod) + Py_DECREF(m_callback); +} + +GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) + : QObject(0), m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject), m_sharedMap(map) +{ + m_data = new DynamicSlotDataV2(callback, this); + m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); + m_metaObject.update(); + m_refs.append(NULL); + + + if (DESTROY_SIGNAL_ID == 0) + DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); + + if (DESTROY_SLOT_ID == 0) + DESTROY_SLOT_ID = m_metaObject.indexOfSlot(RECEIVER_DESTROYED_SLOT_NAME); + + +} + +GlobalReceiverV2::~GlobalReceiverV2() +{ + m_refs.clear(); + // Remove itself from map. + m_sharedMap->remove(m_data->hash()); + // Suppress handling of destroyed() for objects whose last reference is contained inside + // the callback object that will now be deleted. The reference could be a default argument, + // a callback local variable, etc. + // The signal has to be suppressed because it would lead to the following situation: + // Callback is deleted, hence the last reference is decremented, + // leading to the object being deleted, which emits destroyed(), which would try to invoke + // the already deleted callback, and also try to delete the object again. + DynamicSlotDataV2 *data = m_data; + m_data = Q_NULLPTR; + delete data; +} + +int GlobalReceiverV2::addSlot(const char* signature) +{ + return m_data->addSlot(signature); +} + +void GlobalReceiverV2::incRef(const QObject* link) +{ + if (link) { + if (!m_refs.contains(link)) { + bool connected; + Py_BEGIN_ALLOW_THREADS + connected = QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + Py_END_ALLOW_THREADS + if (connected) + m_refs.append(link); + else + Q_ASSERT(false); + } else { + m_refs.append(link); + } + } else { + m_refs.append(NULL); + } +} + +void GlobalReceiverV2::decRef(const QObject* link) +{ + if (m_refs.size() <= 0) + return; + + + m_refs.removeOne(link); + if (link) { + if (!m_refs.contains(link)) { + bool result; + Py_BEGIN_ALLOW_THREADS + result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + Py_END_ALLOW_THREADS + Q_ASSERT(result); + if (!result) + return; + } + } + + if (m_refs.size() == 0) + Py_BEGIN_ALLOW_THREADS + delete this; + Py_END_ALLOW_THREADS + +} + +int GlobalReceiverV2::refCount(const QObject* link) const +{ + if (link) + return m_refs.count(link); + + return m_refs.size(); +} + +void GlobalReceiverV2::notify() +{ + QSet objs = QSet::fromList(m_refs); + Py_BEGIN_ALLOW_THREADS + foreach(const QObject* o, objs) { + QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + } + Py_END_ALLOW_THREADS +} + +QByteArray GlobalReceiverV2::hash() const +{ + return m_data->hash(); +} + +QByteArray GlobalReceiverV2::hash(PyObject* callback) +{ + return DynamicSlotDataV2::hash(callback); +} + +const QMetaObject* GlobalReceiverV2::metaObject() const +{ + return m_metaObject.update(); +} + +int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args) +{ + Shiboken::GilState gil; + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); + + QMetaMethod slot = metaObject()->method(id); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + + if (!m_data) { + if (id != DESTROY_SLOT_ID) { + const QByteArray message = "PySide2 Warning: Skipping callback call " + + slot.methodSignature() + " because the callback object is being destructed."; + PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); + } + return -1; + } + + if (id == DESTROY_SLOT_ID) { + if (m_refs.size() == 0) + return -1; + QObject *obj = *(QObject**)args[1]; + incRef(); //keep the object live (safe ref) + m_refs.removeAll(obj); // remove all refs to this object + decRef(); //remove the safe ref + } else { + bool isShortCuit = (strstr(slot.methodSignature(), "(") == 0); + Shiboken::AutoDecRef callback(m_data->callback()); + SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit); + } + + // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the + // error so it considered "handled". + if (PyErr_Occurred()) { + int reclimit = Py_GetRecursionLimit(); + // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function. + // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion + // error again. Don't do it when the limit is already insanely high, to avoid overflow. + if (reclimit < (1 << 30)) + Py_SetRecursionLimit(reclimit + 5); + PyErr_Print(); + Py_SetRecursionLimit(reclimit); + } + + return -1; +} diff --git a/sources/pyside2/libpyside/globalreceiverv2.h b/sources/pyside2/libpyside/globalreceiverv2.h new file mode 100644 index 000000000..3ad9ba6b7 --- /dev/null +++ b/sources/pyside2/libpyside/globalreceiverv2.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 GLOBALRECEIVER_V2_H +#define GLOBALRECEIVER_V2_H + +#include +#include +#include +#include +#include +#include +#include + +#include "dynamicqmetaobject.h" + +namespace PySide +{ + +class DynamicSlotDataV2; +class GlobalReceiverV2; + +typedef QMap GlobalReceiverV2Map; +typedef QSharedPointer SharedMap; + +/** + * A class used to make the link between the C++ Signal/Slot and Python callback + * This class is used internally by SignalManager + **/ + +class GlobalReceiverV2 : public QObject +{ +public: + /** + * Create a GlobalReceiver object that will call 'callback' argumentent + * + * @param callback A Python callable object (can be a method or not) + * @param ma A SharedPointer used on Signal manager that contains all instaces of GlobalReceiver + **/ + GlobalReceiverV2(PyObject *callback, SharedMap map); + + /** + * Destructor + **/ + ~GlobalReceiverV2(); + + /** + * Reimplemented function from QObject + **/ + int qt_metacall(QMetaObject::Call call, int id, void** args); + const QMetaObject* metaObject() const; + + /** + * Add a extra slot to this object + * + * @param signature The signature of the slot to be added + * @return The index of this slot on metaobject + **/ + int addSlot(const char* signature); + + /** + * Notify to GlobalReceiver about when a new connection was made + **/ + void notify(); + + /** + * Used to increment the reference of the GlobalReceiver object + * + * @param link This is a optional paramenter used to link the ref to some QObject life + **/ + void incRef(const QObject* link = 0); + + /** + * Used to decrement the reference of the GlobalReceiver object + * + * @param link This is a optional paramenter used to dismiss the link ref to some QObject + **/ + void decRef(const QObject* link = 0); + + /* + * Return the count of refs which the GlobalReceiver has + * + * @param link If any QObject was passed, the function return the number of references relative to this 'link' object + * @return The number of references + **/ + int refCount(const QObject* link) const; + + /** + * Use to retrive the unique hash of this GlobalReceiver object + * + * @return a string with a unique id based on GlobalReceiver contents + **/ + QByteArray hash() const; + + /** + * Use to retrive the unique hash of the PyObject based on GlobalReceiver rules + * + * @param callback The Python callable object used to calculate the id + * @return a string with a unique id based on GlobalReceiver contents + **/ + static QByteArray hash(PyObject* callback); + +private: + DynamicQMetaObject m_metaObject; + DynamicSlotDataV2 *m_data; + QList m_refs; + int m_ref; + SharedMap m_sharedMap; +}; + +} + +#endif diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp new file mode 100644 index 000000000..7d05f45a5 --- /dev/null +++ b/sources/pyside2/libpyside/pyside.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "pyside.h" +#include "signalmanager.h" +#include "pysideclassinfo_p.h" +#include "pysideproperty_p.h" +#include "pysideproperty.h" +#include "pysidesignal.h" +#include "pysidesignal_p.h" +#include "pysideslot_p.h" +#include "pysidemetafunction_p.h" +#include "pysidemetafunction.h" +#include "dynamicqmetaobject.h" +#include "destroylistener.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static QStack cleanupFunctionList; +static void* qobjectNextAddr; + +namespace PySide +{ + +void init(PyObject *module) +{ + qobjectNextAddr = 0; + ClassInfo::init(module); + Signal::init(module); + Slot::init(module); + Property::init(module); + MetaFunction::init(module); + // Init signal manager, so it will register some meta types used by QVariant. + SignalManager::instance(); +} + +bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds, const char** blackList, unsigned int blackListSize) +{ + + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwds, &pos, &key, &value)) { + if (!blackListSize || !std::binary_search(blackList, blackList + blackListSize, std::string(Shiboken::String::toCString(key)))) { + QByteArray propName(Shiboken::String::toCString(key)); + if (metaObj->indexOfProperty(propName) != -1) { + propName[0] = std::toupper(propName[0]); + propName.prepend("set"); + + Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData())); + if (!propSetter.isNull()) { + Shiboken::AutoDecRef args(PyTuple_Pack(1, value)); + Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args)); + } else { + PyObject* attr = PyObject_GenericGetAttr(qObj, key); + if (PySide::Property::checkType(attr)) + PySide::Property::setValue(reinterpret_cast(attr), qObj, value); + } + } else { + propName.append("()"); + if (metaObj->indexOfSignal(propName) != -1) { + propName.prepend('2'); + PySide::Signal::connect(qObj, propName, value); + } else { + PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal", propName.constData()); + return false; + }; + } + } + } + return true; +} + +void registerCleanupFunction(CleanupFunction func) +{ + cleanupFunctionList.push(func); +} + +void runCleanupFunctions() +{ + //PySide::DestroyListener::instance()->destroy(); + while (!cleanupFunctionList.isEmpty()) { + CleanupFunction f = cleanupFunctionList.pop(); + f(); + } + PySide::DestroyListener::destroy(); +} + +static void destructionVisitor(SbkObject* pyObj, void* data) +{ + void** realData = reinterpret_cast(data); + SbkObject* pyQApp = reinterpret_cast(realData[0]); + PyTypeObject* pyQObjectType = reinterpret_cast(realData[1]); + + if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) { + if (Shiboken::Object::hasOwnership(pyObj) && Shiboken::Object::isValid(pyObj, false)) { + Shiboken::Object::setValidCpp(pyObj, false); + + Py_BEGIN_ALLOW_THREADS + Shiboken::callCppDestructor(Shiboken::Object::cppPointer(pyObj, pyQObjectType)); + Py_END_ALLOW_THREADS + } + } + +}; + +void destroyQCoreApplication() +{ + SignalManager::instance().clear(); + QCoreApplication* app = QCoreApplication::instance(); + if (!app) + return; + + Shiboken::BindingManager& bm = Shiboken::BindingManager::instance(); + SbkObject* pyQApp = bm.retrieveWrapper(app); + PyTypeObject* pyQObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + assert(pyQObjectType); + + void* data[2] = {pyQApp, pyQObjectType}; + bm.visitAllPyObjects(&destructionVisitor, &data); + + // in the end destroy app + // Allow threads because the destructor calls + // QThreadPool::globalInstance().waitForDone() which may deadlock on the GIL + // if there is a worker working with python objects. + Py_BEGIN_ALLOW_THREADS + delete app; + Py_END_ALLOW_THREADS +} + +struct TypeUserData { + TypeUserData(PyTypeObject* type, const QMetaObject* metaobject) : mo(type, metaobject) {} + DynamicQMetaObject mo; + std::size_t cppObjSize; +}; + +std::size_t getSizeOfQObject(SbkObjectType* type) +{ + using namespace Shiboken::ObjectType; + TypeUserData* userData = reinterpret_cast(getTypeUserData(reinterpret_cast(type))); + return userData->cppObjSize; +} + +void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, const std::size_t& cppObjSize) +{ + //create DynamicMetaObject based on python type + TypeUserData* userData = new TypeUserData(reinterpret_cast(type), base); + userData->cppObjSize = cppObjSize; + userData->mo.update(); + Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor); + + //initialize staticQMetaObject property + void* metaObjectPtr = &userData->mo; + static SbkConverter* converter = Shiboken::Conversions::getConverter("QMetaObject"); + if (!converter) + return; + Shiboken::AutoDecRef pyMetaObject(Shiboken::Conversions::pointerToPython(converter, metaObjectPtr)); + PyObject_SetAttrString(reinterpret_cast(type), "staticMetaObject", pyMetaObject); +} + +void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base) +{ + initDynamicMetaObject(type, base, 0); +} + +void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds */) +{ + PyTypeObject* qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + QByteArray className(Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0))); + + PyObject* bases = PyTuple_GET_ITEM(args, 1); + int numBases = PyTuple_GET_SIZE(bases); + QMetaObject* baseMo = 0; + SbkObjectType* qobjBase = 0; + + for (int i = 0; i < numBases; ++i) { + PyTypeObject* base = reinterpret_cast(PyTuple_GET_ITEM(bases, i)); + if (PyType_IsSubtype(base, qObjType)) { + baseMo = reinterpret_cast(Shiboken::ObjectType::getTypeUserData(reinterpret_cast(base))); + qobjBase = reinterpret_cast(base); + reinterpret_cast(baseMo)->update(); + break; + } + } + if (!baseMo) { + qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData()); + return; + } + + TypeUserData* userData = reinterpret_cast(Shiboken::ObjectType::getTypeUserData(qobjBase)); + initDynamicMetaObject(type, baseMo, userData->cppObjSize); +} + +PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name) +{ + PyObject* attr = PyObject_GenericGetAttr(self, name); + if (!Shiboken::Object::isValid(reinterpret_cast(self), false)) + return attr; + + if (attr && Property::checkType(attr)) { + PyObject *value = Property::getValue(reinterpret_cast(attr), self); + Py_DECREF(attr); + if (!value) + return 0; + Py_INCREF(value); + attr = value; + } + + //mutate native signals to signal instance type + if (attr && PyObject_TypeCheck(attr, &PySideSignalType)) { + PyObject* signal = reinterpret_cast(Signal::initialize(reinterpret_cast(attr), name, self)); + PyObject_SetAttr(self, name, reinterpret_cast(signal)); + return signal; + } + + //search on metaobject (avoid internal attributes started with '__') + if (!attr) { + const char* cname = Shiboken::String::toCString(name); + uint cnameLen = qstrlen(cname); + if (std::strncmp("__", cname, 2)) { + const QMetaObject* metaObject = cppSelf->metaObject(); + //signal + QList signalList; + for(int i=0, i_max = metaObject->methodCount(); i < i_max; i++) { + QMetaMethod method = metaObject->method(i); + const QByteArray methSig_ = method.methodSignature(); + const char *methSig = methSig_.constData(); + bool methMacth = !std::strncmp(cname, methSig, cnameLen) && methSig[cnameLen] == '('; + if (methMacth) { + if (method.methodType() == QMetaMethod::Signal) { + signalList.append(method); + } else { + PySideMetaFunction* func = MetaFunction::newObject(cppSelf, i); + if (func) { + PyObject *result = reinterpret_cast(func); + PyObject_SetAttr(self, name, result); + return result; + } + } + } + } + if (signalList.size() > 0) { + PyObject* pySignal = reinterpret_cast(Signal::newObjectFromMethod(self, signalList)); + PyObject_SetAttr(self, name, pySignal); + return pySignal; + } + } + } + return attr; +} + +bool inherits(PyTypeObject* objType, const char* class_name) +{ + if (strcmp(objType->tp_name, class_name) == 0) + return true; + + PyTypeObject* base = (objType)->tp_base; + if (base == 0) + return false; + + return inherits(base, class_name); +} + +void* nextQObjectMemoryAddr() +{ + return qobjectNextAddr; +} + +void setNextQObjectMemoryAddr(void* addr) +{ + qobjectNextAddr = addr; +} + +} // namespace PySide + +// A QSharedPointer is used with a deletion function to invalidate a pointer +// when the property value is cleared. This should be a QSharedPointer with +// a void* pointer, but that isn't allowed +typedef char any_t; +Q_DECLARE_METATYPE(QSharedPointer); + +namespace PySide +{ + +static void invalidatePtr(any_t* object) +{ + Shiboken::GilState state; + + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(object); + if (wrapper != NULL) + Shiboken::BindingManager::instance().releaseWrapper(wrapper); +} + +static const char invalidatePropertyName[] = "_PySideInvalidatePtr"; + +PyObject* getWrapperForQObject(QObject* cppSelf, SbkObjectType* sbk_type) +{ + PyObject* pyOut = reinterpret_cast(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf)); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + + // Setting the property will trigger an QEvent notification, which may call into + // code that creates the wrapper so only set the property if it isn't already + // set and check if it's created after the set call + QVariant existing = cppSelf->property(invalidatePropertyName); + if (!existing.isValid()) { + QSharedPointer shared_with_del((any_t*)cppSelf, invalidatePtr); + cppSelf->setProperty(invalidatePropertyName, QVariant::fromValue(shared_with_del)); + pyOut = reinterpret_cast(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf)); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + } + + const char* typeName = typeid(*cppSelf).name(); + pyOut = Shiboken::Object::newObject(sbk_type, cppSelf, false, false, typeName); + + return pyOut; +} + +#ifdef PYSIDE_QML_SUPPORT +static QuickRegisterItemFunction quickRegisterItem; + +QuickRegisterItemFunction getQuickRegisterItemFunction() +{ + return quickRegisterItem; +} + +void setQuickRegisterItemFunction(QuickRegisterItemFunction function) +{ + quickRegisterItem = function; +} +#endif // PYSIDE_QML_SUPPORT + +} //namespace PySide + diff --git a/sources/pyside2/libpyside/pyside.h b/sources/pyside2/libpyside/pyside.h new file mode 100644 index 000000000..3619e2875 --- /dev/null +++ b/sources/pyside2/libpyside/pyside.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_H +#define PYSIDE_H + +#include +#include + +#ifdef PYSIDE_QML_SUPPORT +# include +#endif + +#include +#include +#include + +struct SbkObjectType; + +namespace PySide +{ + +PYSIDE_API void init(PyObject *module); + +/** + * Hash function used to enable hash on objects not supported on native Qt library which has toString function. + */ +template +inline uint hash(const T& value) +{ + return qHash(value.toString()); +} + +/** + * Fill QObject properties and do signal connections using the values found in \p kwds dictonary. + * \param qObj PyObject fot the QObject. + * \param metaObj QMetaObject of \p qObj. + * \param blackList keys to be ignored in kwds dictionary, this string list MUST be sorted. + * \param blackListSize numbe rof elements in blackList. + * \param kwds key->value dictonary. + * \return True if everything goes well, false with a Python error setted otherwise. + */ +PYSIDE_API bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds, const char** blackList, unsigned int blackListSize); + +/** +* If the type \p T was registered on Qt meta type system with Q_DECLARE_METATYPE macro, this class will initialize +* the meta type. +* +* Initialize a meta type means register it on Qt meta type system, Qt itself only do this on the first call of +* qMetaTypeId, and this is exactly what we do to init it. If we don't do that, calls to QMetaType::type("QMatrix2x2") +* could return zero, causing QVariant to not recognize some C++ types, like QMatrix2x2. +*/ +template::Defined > +struct initQtMetaType { + initQtMetaType() + { + qMetaTypeId(); + } +}; + +// Template specialization to do nothing when the type wasn't registered on Qt meta type system. +template +struct initQtMetaType { +}; + +PYSIDE_DEPRECATED(PYSIDE_API void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base)); +PYSIDE_API void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, const std::size_t& cppObjSize); +PYSIDE_API void initQObjectSubType(SbkObjectType* type, PyObject* args, PyObject* kwds); + +/// Return the size in bytes of a type that inherits QObject. +PYSIDE_API std::size_t getSizeOfQObject(SbkObjectType* type); + +typedef void (*CleanupFunction)(void); + +/** + * Register a function to be called before python die + */ +PYSIDE_API void registerCleanupFunction(CleanupFunction func); +PYSIDE_API void runCleanupFunctions(); + +/** + * Destroy a QCoreApplication taking care of destroy all instances of QObject first. + */ +PYSIDE_API void destroyQCoreApplication(); + +/** + * Check for properties and signals registered on MetaObject and return these + * \param cppSelf Is the QObject which contains the metaobject + * \param self Python object of cppSelf + * \param name Name of the argument which the function will try retrieve from MetaData + * \return The Python object which contains the Data obtained in metaObject or the Python attribute related with name + */ +PYSIDE_API PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name); + +/** + * Check if self inherits from class_name + * \param self Python object + * \param class_name strict with the class name + * \return Returns true if self object inherits from class_name, otherwise returns false + */ +PYSIDE_API bool inherits(PyTypeObject* self, const char* class_name); + +PYSIDE_API void* nextQObjectMemoryAddr(); +PYSIDE_API void setNextQObjectMemoryAddr(void* addr); + +PYSIDE_API PyObject* getWrapperForQObject(QObject* cppSelf, SbkObjectType* sbk_type); + +#ifdef PYSIDE_QML_SUPPORT +// Used by QtQuick module to notify QtQml that custom QtQuick items can be registered. +typedef bool (*QuickRegisterItemFunction)(PyObject *pyObj, const char *uri, int versionMajor, + int versionMinor, const char *qmlName, + QQmlPrivate::RegisterType *); +PYSIDE_API QuickRegisterItemFunction getQuickRegisterItemFunction(); +PYSIDE_API void setQuickRegisterItemFunction(QuickRegisterItemFunction function); +#endif // PYSIDE_QML_SUPPORT + +} //namespace PySide + + +#endif // PYSIDE_H + diff --git a/sources/pyside2/libpyside/pyside2.pc.in b/sources/pyside2/libpyside/pyside2.pc.in new file mode 100644 index 000000000..4f396ac72 --- /dev/null +++ b/sources/pyside2/libpyside/pyside2.pc.in @@ -0,0 +1,14 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@LIB_INSTALL_DIR@ +includedir=@CMAKE_INSTALL_PREFIX@/include/PySide2@pyside2_SUFFIX@ +typesystemdir=@CMAKE_INSTALL_PREFIX@/share/PySide2@pyside2_SUFFIX@/typesystems +pythonpath=@SITE_PACKAGE@ + +Name: PySide2@pyside2_SUFFIX@ +Description: Support library for Python bindings of Qt5-based libraries. +Version: @BINDING_API_VERSION_FULL@ +Libs: -L${libdir} -lpyside2@pyside2_SUFFIX@@SHIBOKEN_PYTHON_EXTENSION_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@ +Cflags: -I${includedir} +Requires: shiboken2 + diff --git a/sources/pyside2/libpyside/pysideclassinfo.cpp b/sources/pyside2/libpyside/pysideclassinfo.cpp new file mode 100644 index 000000000..a80ed9c54 --- /dev/null +++ b/sources/pyside2/libpyside/pysideclassinfo.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 +#include "pysideclassinfo.h" +#include "pysideclassinfo_p.h" +#include "dynamicqmetaobject.h" + +#include +#include + +#define CLASSINFO_CLASS_NAME "ClassInfo" + +extern "C" +{ + +static PyObject* classInfoTpNew(PyTypeObject* subtype, PyObject* args, PyObject* kwds); +static int classInfoTpInit(PyObject*, PyObject*, PyObject*); +static void classInfoFree(void*); +static PyObject* classCall(PyObject*, PyObject*, PyObject*); + +PyTypeObject PySideClassInfoType = { + PyVarObject_HEAD_INIT(0, 0) + "PySide2.QtCore." CLASSINFO_CLASS_NAME, /*tp_name*/ + sizeof(PySideClassInfo), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + classCall, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc */ + 0, /*tp_traverse */ + 0, /*tp_clear */ + 0, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + 0, /*tp_iter */ + 0, /*tp_iternext */ + 0, /*tp_methods */ + 0, /*tp_members */ + 0, /*tp_getset */ + 0, /*tp_base */ + 0, /*tp_dict */ + 0, /*tp_descr_get */ + 0, /*tp_descr_set */ + 0, /*tp_dictoffset */ + classInfoTpInit, /*tp_init */ + 0, /*tp_alloc */ + classInfoTpNew, /*tp_new */ + classInfoFree, /*tp_free */ + 0, /*tp_is_gc */ + 0, /*tp_bases */ + 0, /*tp_mro */ + 0, /*tp_cache */ + 0, /*tp_subclasses */ + 0, /*tp_weaklist */ + 0, /*tp_del */ + 0, /*tp_version_tag */ +}; + +PyObject *classCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) { + PyErr_Format(PyExc_TypeError, + "The ClassInfo decorator takes exactly 1 positional argument (%zd given)", + PyTuple_Size(args)); + return 0; + } + + PySideClassInfo* data = reinterpret_cast(self); + PySideClassInfoPrivate* pData = data->d; + + if (pData->m_alreadyWrapped) { + PyErr_SetString(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object"); + return 0; + } + + PyObject* klass; + klass = PyTuple_GetItem(args, 0); + bool validClass = false; + + // 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 0; + } + + if (Shiboken::ObjectType::checkType(reinterpret_cast(klass))) { + PySide::DynamicQMetaObject* mo = reinterpret_cast(Shiboken::ObjectType::getTypeUserData(reinterpret_cast(klass))); + if (mo) { + mo->addInfo(PySide::ClassInfo::getMap(data)); + pData->m_alreadyWrapped = true; + validClass = true; + } + } + + if (!validClass) { + PyErr_SetString(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject"); + return 0; + } + + Py_INCREF(klass); + return klass; +} + +static PyObject *classInfoTpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) +{ + PySideClassInfo* me = reinterpret_cast(subtype->tp_alloc(subtype, 0)); + me->d = new PySideClassInfoPrivate; + + me->d->m_alreadyWrapped = false; + + return reinterpret_cast(me); +} + +int classInfoTpInit(PyObject* self, PyObject* args, PyObject* kwds) +{ + if (PyTuple_Check(args) && PyTuple_Size(args) > 0) { + PyErr_Format(PyExc_TypeError, "ClassInfo() takes exactly 0 positional arguments (%zd given)", PyTuple_Size(args)); + return -1; + } + + PySideClassInfo* data = reinterpret_cast(self); + PySideClassInfoPrivate* pData = data->d; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + // PyDict_Next causes a segfault if kwds is empty + if (kwds && PyDict_Check(kwds) && PyDict_Size(kwds) > 0) { + while (PyDict_Next(kwds, &pos, &key, &value)) { + if (Shiboken::String::check(key) && Shiboken::String::check(value)) { + pData->m_data[Shiboken::String::toCString(key)] = Shiboken::String::toCString(value); + } else { + PyErr_SetString(PyExc_TypeError, "All keys and values provided to ClassInfo() must be strings"); + return -1; + } + } + } + + return PyErr_Occurred() ? -1 : 1; +} + +void classInfoFree(void *self) +{ + PyObject* pySelf = reinterpret_cast(self); + PySideClassInfo* data = reinterpret_cast(self); + + delete data->d; + pySelf->ob_type->tp_base->tp_free(self); +} + + +} // extern "C" + + +namespace PySide { namespace ClassInfo { + +void init(PyObject* module) +{ + if (PyType_Ready(&PySideClassInfoType) < 0) + return; + + Py_INCREF(&PySideClassInfoType); + PyModule_AddObject(module, CLASSINFO_CLASS_NAME, reinterpret_cast(&PySideClassInfoType)); +} + +bool checkType(PyObject* pyObj) +{ + if (pyObj) + return PyType_IsSubtype(pyObj->ob_type, &PySideClassInfoType); + return false; +} + +QMap getMap(PySideClassInfo* obj) +{ + return obj->d->m_data; +} + +} //namespace Property +} //namespace PySide diff --git a/sources/pyside2/libpyside/pysideclassinfo.h b/sources/pyside2/libpyside/pysideclassinfo.h new file mode 100644 index 000000000..9c92b3fcf --- /dev/null +++ b/sources/pyside2/libpyside/pysideclassinfo.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_CLASSINFO_H +#define PYSIDE_CLASSINFO_H + +#include +#include +#include +#include + +extern "C" +{ + extern PYSIDE_API PyTypeObject PySideClassInfoType; + + struct PySideClassInfoPrivate; + struct PYSIDE_API PySideClassInfo + { + PyObject_HEAD + PySideClassInfoPrivate* d; + }; +}; + +namespace PySide { namespace ClassInfo { + +PYSIDE_API bool checkType(PyObject* pyObj); +PYSIDE_API QMap getMap(PySideClassInfo* obj); + +} //namespace ClassInfo +} //namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideclassinfo_p.h b/sources/pyside2/libpyside/pysideclassinfo_p.h new file mode 100644 index 000000000..3dbc0cd9a --- /dev/null +++ b/sources/pyside2/libpyside/pysideclassinfo_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_CLASSINFO_P_H +#define PYSIDE_CLASSINFO_P_H + +#include +#include +#include "pysideclassinfo.h" + +#define __INFO_ATTR_NAME__ "__classInfo__" + +struct PySideClassInfo; + +extern "C" +{ + +struct PySideClassInfoPrivate { + QMap m_data; + bool m_alreadyWrapped; +}; + +} // extern "C" + +namespace PySide { namespace ClassInfo { + +/** + * Init PySide QProperty support system + */ +void init(PyObject* module); + + +} // namespace ClassInfo +} // namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideconversions.h b/sources/pyside2/libpyside/pysideconversions.h new file mode 100644 index 000000000..ed8002fae --- /dev/null +++ b/sources/pyside2/libpyside/pysideconversions.h @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDECONVERSIONS_H +#define PYSIDECONVERSIONS_H + +#include +#include +#include +#include + +template +struct QtDictConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, Shiboken::SbkType())) + return true; + + if ((Shiboken::SbkType() && Shiboken::Object::checkType(pyObj)) || !PyDict_Check(pyObj)) + return false; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyObj, &pos, &key, &value)) { + if (!Shiboken::Converter::isConvertible(key) + && !Shiboken::Converter::isConvertible(value)) { + return false; + } + } + return true; + } + + static inline PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static inline PyObject* toPython(const QtDict& cppobj) + { + PyObject* result = PyDict_New(); + typename QtDict::const_iterator it = cppobj.begin(); + + for (; it != cppobj.end(); ++it) { + Shiboken::AutoDecRef keyObj(Shiboken::Converter::toPython(it.key())); + Shiboken::AutoDecRef valueObj(Shiboken::Converter::toPython(it.value())); + PyDict_SetItem(result, keyObj, valueObj); + } + + return result; + } + static inline QtDict toCpp(PyObject* pyobj) + { + if (PyObject_TypeCheck(pyobj, Shiboken::SbkType())) + return *reinterpret_cast(Shiboken::Object::cppPointer(reinterpret_cast(pyobj), Shiboken::SbkType())); + + QtDict result; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyobj, &pos, &key, &value)) + result[Shiboken::Converter::toCpp(key)] = Shiboken::Converter::toCpp(value); + return result; + } +}; + +template +struct QtMultiMapConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, Shiboken::SbkType())) + return true; + + if ((Shiboken::SbkType() && Shiboken::Object::checkType(pyObj)) || !PyDict_Check(pyObj)) + return false; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyObj, &pos, &key, &value)) { + if (!Shiboken::Converter::isConvertible(key)) { + if (PySequence_Check(value)) { + for (int i = 0, max = PySequence_Length(value); i < max; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(value, i)); + if (!Shiboken::Converter::isConvertible(value)) + return false; + } + } else if (!Shiboken::Converter::isConvertible(value)) { + return false; + } + } + } + return true; + } + + static inline PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static inline PyObject* toPython(const MultiMap& cppObj) + { + PyObject* result = PyDict_New(); + typename MultiMap::const_iterator it = cppObj.begin(); + + for (; it != cppObj.end(); ++it) { + Shiboken::AutoDecRef key(Shiboken::Converter::toPython(it.key())); + Shiboken::AutoDecRef value(Shiboken::Converter::toPython(it.value())); + + PyObject* values = PyDict_GetItem(result, key); + bool decRefValues = !values; + if (!values) + values = PyList_New(0); + PyList_Append(values, value); + PyDict_SetItem(result, key, values); + if (decRefValues) { + Py_DECREF(values); + } + } + + return result; + } + + static inline MultiMap toCpp(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, Shiboken::SbkType())) + return *reinterpret_cast(Shiboken::Object::cppPointer(reinterpret_cast(pyObj), Shiboken::SbkType())); + + MultiMap result; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyObj, &pos, &key, &value)) + result[Shiboken::Converter::toCpp(key)] = Shiboken::Converter::toCpp(value); + return result; + } +}; + +template +struct QSequenceConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, Shiboken::SbkType())) + return true; + if ((Shiboken::SbkType() && Shiboken::Object::checkType(pyObj)) || !PySequence_Check(pyObj)) + return false; + for (int i = 0, max = PySequence_Length(pyObj); i < max; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(pyObj, i)); + if (!Shiboken::Converter::isConvertible(item)) + return false; + } + return true; + } + static inline PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static PyObject* toPython(const T& cppobj) + { + PyObject* result = PyList_New((int) cppobj.size()); + typename T::const_iterator it = cppobj.begin(); + for (int idx = 0; it != cppobj.end(); ++it, ++idx) { + typename T::value_type vh(*it); + PyList_SET_ITEM(result, idx, Shiboken::Converter::toPython(vh)); + } + return result; + } + static T toCpp(PyObject* pyobj) + { + if (PyObject_TypeCheck(pyobj, Shiboken::SbkType())) + return *reinterpret_cast(Shiboken::Object::cppPointer(reinterpret_cast(pyobj), Shiboken::SbkType())); + + Shiboken::AutoDecRef fastSequence(PySequence_Fast(pyobj, "Invalid sequence object")); + T result; + for (int i = 0; i < PySequence_Size(pyobj); i++) { + PyObject* pyItem = PySequence_Fast_GET_ITEM(fastSequence.object(), i); + result << Shiboken::Converter::toCpp(pyItem); + } + return result; + } +}; + + +template +struct QFlagsConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return PyObject_TypeCheck(pyObj, Shiboken::SbkType()); + } + + static inline bool isConvertible(PyObject* pyObj) + { + return PyObject_TypeCheck(pyObj, Shiboken::SbkType()) + || PyObject_TypeCheck(pyObj, Shiboken::SbkType()); + } + + static inline PyObject* toPython(void* cppObj) + { + return toPython(*reinterpret_cast(cppObj)); + } + + static inline PyObject* toPython(const T& cppObj) + { + return reinterpret_cast(PySide::QFlags::newObject(cppObj, Shiboken::SbkType())); + } + + static inline T toCpp(PyObject* pyObj) + { + /* this was long. Needed int in Qt5 */ + int val = 0; + if (Shiboken::Enum::check(pyObj)) { + val = Shiboken::Enum::getValue(pyObj); + } else if (PyObject_TypeCheck(pyObj, Shiboken::SbkType())) { + val = PySide::QFlags::getValue(reinterpret_cast(pyObj)); + } else if (PyNumber_Check(pyObj)) { + Shiboken::AutoDecRef pyLong(PyNumber_Long(pyObj)); + val = PyLong_AsLong(pyLong.object()); + } else { + PyErr_BadArgument(); + } + return T(QFlag(val)); + } +}; + +#endif diff --git a/sources/pyside2/libpyside/pysidemacros.h b/sources/pyside2/libpyside/pysidemacros.h new file mode 100644 index 000000000..df1ed6e8c --- /dev/null +++ b/sources/pyside2/libpyside/pysidemacros.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDEMACROS_H +#define PYSIDEMACROS_H + +#if defined _WIN32 + #if PYSIDE_EXPORTS + #define PYSIDE_API __declspec(dllexport) + #else + #if defined __MINGW32__ + #define PYSIDE_API + #else + #define PYSIDE_API __declspec(dllimport) + #endif + #endif + #define PYSIDE_DEPRECATED(func) __declspec(deprecated) func +#else + #if __GNUC__ >= 4 + #define PYSIDE_API __attribute__ ((visibility("default"))) + #define PYSIDE_DEPRECATED(func) func __attribute__ ((deprecated)) + #else + #define PYSIDE_API + #define PYSIDE_DEPRECATED(func) func + #endif +#endif + +#endif diff --git a/sources/pyside2/libpyside/pysidemetafunction.cpp b/sources/pyside2/libpyside/pysidemetafunction.cpp new file mode 100644 index 000000000..039db513b --- /dev/null +++ b/sources/pyside2/libpyside/pysidemetafunction.cpp @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 +#include "pysidemetafunction.h" +#include "pysidemetafunction_p.h" + +#include +#include +#include +#include + +extern "C" +{ + +struct PySideMetaFunctionPrivate +{ + QObject* qobject; + int methodIndex; +}; + +//methods +static void functionFree(void*); +static PyObject* functionCall(PyObject*, PyObject*, PyObject*); + +PyTypeObject PySideMetaFunctionType = { + PyVarObject_HEAD_INIT(0, 0) + /*tp_name*/ "PySide.MetaFunction", + /*tp_basicsize*/ sizeof(PySideMetaFunction), + /*tp_itemsize*/ 0, + /*tp_dealloc*/ 0, + /*tp_print*/ 0, + /*tp_getattr*/ 0, + /*tp_setattr*/ 0, + /*tp_compare*/ 0, + /*tp_repr*/ 0, + /*tp_as_number*/ 0, + /*tp_as_sequence*/ 0, + /*tp_as_mapping*/ 0, + /*tp_hash*/ 0, + /*tp_call*/ functionCall, + /*tp_str*/ 0, + /*tp_getattro*/ 0, + /*tp_setattro*/ 0, + /*tp_as_buffer*/ 0, + /*tp_flags*/ Py_TPFLAGS_DEFAULT, + /*tp_doc*/ "MetaFunction", + /*tp_traverse*/ 0, + /*tp_clear*/ 0, + /*tp_richcompare*/ 0, + /*tp_weaklistoffset*/ 0, + /*tp_iter*/ 0, + /*tp_iternext*/ 0, + /*tp_methods*/ 0, + /*tp_members*/ 0, + /*tp_getset*/ 0, + /*tp_base*/ 0, + /*tp_dict*/ 0, + /*tp_descr_get*/ 0, + /*tp_descr_set*/ 0, + /*tp_dictoffset*/ 0, + /*tp_init*/ 0, + /*tp_alloc*/ 0, + /*tp_new*/ PyType_GenericNew, + /*tp_free*/ functionFree, + /*tp_is_gc*/ 0, + /*tp_bases*/ 0, + /*tp_mro*/ 0, + /*tp_cache*/ 0, + /*tp_subclasses*/ 0, + /*tp_weaklist*/ 0, + /*tp_del*/ 0, + /*tp_version_tag*/ 0 +}; + +void functionFree(void *self) +{ + PySideMetaFunction* function = reinterpret_cast(self); + delete function->d; +} + +PyObject *functionCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + PySideMetaFunction* function = reinterpret_cast(self); + + PyObject* retVal; + if (!PySide::MetaFunction::call(function->d->qobject, function->d->methodIndex, args, &retVal)) + return 0; + return retVal; +} + +} // extern "C" + +namespace PySide { namespace MetaFunction { + +void init(PyObject* module) +{ + if (PyType_Ready(&PySideMetaFunctionType) < 0) + return; + + PyModule_AddObject(module, "MetaFunction", reinterpret_cast(&PySideMetaFunctionType)); +} + +PySideMetaFunction* newObject(QObject* source, int methodIndex) +{ + if (methodIndex >= source->metaObject()->methodCount()) + return 0; + + QMetaMethod method = source->metaObject()->method(methodIndex); + if ((method.methodType() == QMetaMethod::Slot) || + (method.methodType() == QMetaMethod::Method)) { + PySideMetaFunction* function = PyObject_New(PySideMetaFunction, &PySideMetaFunctionType); + function->d = new PySideMetaFunctionPrivate(); + function->d->qobject = source; + function->d->methodIndex = methodIndex; + return function; + } + return 0; +} + +bool call(QObject* self, int methodIndex, PyObject* args, PyObject** retVal) +{ + + QMetaMethod method = self->metaObject()->method(methodIndex); + QList argTypes = method.parameterTypes(); + + // args given plus return type + Shiboken::AutoDecRef sequence(PySequence_Fast(args, 0)); + int numArgs = PySequence_Fast_GET_SIZE(sequence.object()) + 1; + + if (numArgs - 1 > argTypes.count()) { + PyErr_Format(PyExc_TypeError, "%s only accepts %d argument(s), %d given!", + method.methodSignature().constData(), + argTypes.count(), numArgs - 1); + return false; + } + + if (numArgs - 1 < argTypes.count()) { + PyErr_Format(PyExc_TypeError, "%s needs %d argument(s), %d given!", + method.methodSignature().constData(), + argTypes.count(), numArgs - 1); + return false; + } + + QVariant* methValues = new QVariant[numArgs]; + void** methArgs = new void*[numArgs]; + + // Prepare room for return type + const char* returnType = method.typeName(); + if (returnType && std::strcmp("void", returnType)) + argTypes.prepend(returnType); + else + argTypes.prepend(QByteArray()); + + int i; + for (i = 0; i < numArgs; ++i) { + const QByteArray& typeName = argTypes[i]; + // This must happen only when the method hasn't return type. + if (typeName.isEmpty()) { + methArgs[i] = 0; + continue; + } + + Shiboken::Conversions::SpecificConverter converter(typeName); + if (converter) { + int typeId = QMetaType::type(typeName); + if (!Shiboken::Conversions::pythonTypeIsObjectType(converter)) { + if (!typeId) { + PyErr_Format(PyExc_TypeError, "Value types used on meta functions (including signals) need to be " + "registered on meta type: %s", typeName.data()); + break; + } + methValues[i] = QVariant(typeId, static_cast(0)); + } + methArgs[i] = methValues[i].data(); + if (i == 0) // Don't do this for return type + continue; + if (typeId == QVariant::String) { + QString tmp; + converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), &tmp); + methValues[i] = tmp; + } else { + converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), methArgs[i]); + } + } else { + PyErr_Format(PyExc_TypeError, "Unknown type used to call meta function (that may be a signal): %s", argTypes[i].constData()); + break; + } + } + + bool ok = i == numArgs; + if (ok) { + Py_BEGIN_ALLOW_THREADS + QMetaObject::metacall(self, QMetaObject::InvokeMetaMethod, method.methodIndex(), methArgs); + Py_END_ALLOW_THREADS + + if (retVal) { + if (methArgs[0]) { + static SbkConverter* qVariantTypeConverter = Shiboken::Conversions::getConverter("QVariant"); + Q_ASSERT(qVariantTypeConverter); + *retVal = Shiboken::Conversions::copyToPython(qVariantTypeConverter, &methValues[0]); + } else { + *retVal = Py_None; + Py_INCREF(*retVal); + } + } + } + + delete[] methArgs; + delete[] methValues; + + return ok; +} + + +} //namespace MetaFunction +} //namespace PySide + diff --git a/sources/pyside2/libpyside/pysidemetafunction.h b/sources/pyside2/libpyside/pysidemetafunction.h new file mode 100644 index 000000000..2be369407 --- /dev/null +++ b/sources/pyside2/libpyside/pysidemetafunction.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_METAFUNCTION_H +#define PYSIDE_METAFUNCTION_H + +#include +#include +#include + +#include +#include + +extern "C" +{ + extern PYSIDE_API PyTypeObject PySideMetaFunctionType; + + struct PySideMetaFunctionPrivate; + struct PYSIDE_API PySideMetaFunction + { + PyObject_HEAD + PySideMetaFunctionPrivate* d; + }; +}; //extern "C" + +namespace PySide { namespace MetaFunction { + +/** + * This function creates a MetaFunction object + * + * @param obj the QObject witch this fuction is part of + * @param methodIndex The index of this function on MetaObject + * @return Return a new reference of PySideMetaFunction + **/ +PYSIDE_API PySideMetaFunction* newObject(QObject* obj, int methodIndex); + +} //namespace MetaFunction +} //namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysidemetafunction_p.h b/sources/pyside2/libpyside/pysidemetafunction_p.h new file mode 100644 index 000000000..979bbb120 --- /dev/null +++ b/sources/pyside2/libpyside/pysidemetafunction_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_METAFUNCTION_P_H +#define PYSIDE_METAFUNCTION_P_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QObject; +QT_END_NAMESPACE + +namespace PySide { namespace MetaFunction { + + void init(PyObject* module); + /** + * Does a Qt metacall on a QObject + */ + bool call(QObject* self, int methodIndex, PyObject* args, PyObject** retVal = 0); + +} //namespace MetaFunction +} //namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideproperty.cpp b/sources/pyside2/libpyside/pysideproperty.cpp new file mode 100644 index 000000000..07b9dfae5 --- /dev/null +++ b/sources/pyside2/libpyside/pysideproperty.cpp @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 +#include "pysideproperty.h" +#include "pysideproperty_p.h" +#include "dynamicqmetaobject_p.h" +#include "pysidesignal.h" +#include "pysidesignal_p.h" + +#include +#include + + +#define QPROPERTY_CLASS_NAME "Property" + +extern "C" +{ + +static PyObject* qpropertyTpNew(PyTypeObject* subtype, PyObject* args, PyObject* kwds); +static int qpropertyTpInit(PyObject*, PyObject*, PyObject*); +static void qpropertyDeAlloc(PyObject* self); + +//methods +static PyObject* qPropertyCall(PyObject*, PyObject*, PyObject*); +static PyObject* qPropertySetter(PyObject*, PyObject*); +static PyObject* qPropertyGetter(PyObject*, PyObject*); +static int qpropertyTraverse(PyObject* self, visitproc visit, void* arg); +static int qpropertyClear(PyObject* self); + +static PyMethodDef PySidePropertyMethods[] = { + {"setter", (PyCFunction)qPropertySetter, METH_O, 0}, + {"write", (PyCFunction)qPropertySetter, METH_O, 0}, + {"getter", (PyCFunction)qPropertyGetter, METH_O, 0}, + {"read", (PyCFunction)qPropertyGetter, METH_O, 0}, + {0, 0, 0, 0} +}; + +PyTypeObject PySidePropertyType = { + PyVarObject_HEAD_INIT(0, 0) + QPROPERTY_CLASS_NAME, /*tp_name*/ + sizeof(PySideProperty), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + qpropertyDeAlloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + qPropertyCall, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + 0, /*tp_doc */ + qpropertyTraverse, /*tp_traverse */ + qpropertyClear, /*tp_clear */ + 0, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + 0, /*tp_iter */ + 0, /*tp_iternext */ + PySidePropertyMethods, /*tp_methods */ + 0, /*tp_members */ + 0, /*tp_getset */ + 0, /*tp_base */ + 0, /*tp_dict */ + 0, /*tp_descr_get */ + 0, /*tp_descr_set */ + 0, /*tp_dictoffset */ + qpropertyTpInit, /*tp_init */ + 0, /*tp_alloc */ + qpropertyTpNew, /*tp_new */ + 0, /*tp_free */ + 0, /*tp_is_gc */ + 0, /*tp_bases */ + 0, /*tp_mro */ + 0, /*tp_cache */ + 0, /*tp_subclasses */ + 0, /*tp_weaklist */ + 0, /*tp_del */ + 0 /*tp_version_tag */ +}; + +static void qpropertyMetaCall(PySideProperty* pp, PyObject* self, QMetaObject::Call call, void** args) +{ + Shiboken::Conversions::SpecificConverter converter(pp->d->typeName); + Q_ASSERT(converter); + + QByteArray type(pp->d->typeName); + + switch(call) { + case QMetaObject::ReadProperty: + { + Shiboken::GilState gil; + PyObject* value = PySide::Property::getValue(pp, self); + if (value) { + converter.toCpp(value, args[0]); + Py_DECREF(value); + } + break; + } + + case QMetaObject::WriteProperty: + { + Shiboken::GilState gil; + Shiboken::AutoDecRef value(converter.toPython(args[0])); + PySide::Property::setValue(pp, self, value); + break; + } + + case QMetaObject::ResetProperty: + { + Shiboken::GilState gil; + PySide::Property::reset(pp, self); + break; + } + + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyUser: + // just to avoid gcc warnings + case QMetaObject::InvokeMetaMethod: + case QMetaObject::CreateInstance: + case QMetaObject::IndexOfMethod: + case QMetaObject::RegisterPropertyMetaType: + case QMetaObject::RegisterMethodArgumentMetaType: + break; + } +} + + +static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) +{ + PySideProperty* me = reinterpret_cast(subtype->tp_alloc(subtype, 0)); + me->d = new PySidePropertyPrivate; + memset(me->d, 0, sizeof(PySidePropertyPrivate)); + PySidePropertyPrivate* pData = me->d; + pData->designable = true; + pData->scriptable = true; + pData->stored = true; + return reinterpret_cast(me); +} + +int qpropertyTpInit(PyObject* self, PyObject* args, PyObject* kwds) +{ + PyObject* type = 0; + PySideProperty* data = reinterpret_cast(self); + PySidePropertyPrivate* pData = data->d; + pData->metaCallHandler = &qpropertyMetaCall; + + static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify", + "designable", "scriptable", "stored", "user", + "constant", "final", 0}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O|OOOOsObbbbbb:QtCore.QProperty", (char**) kwlist, + /*OO*/ &type, &(pData->fget), + /*OOO*/ &(pData->fset), &(pData->freset), &(pData->fdel), + /*s*/ &(pData->doc), + /*O*/ &(pData->notify), + /*bbbbbb*/ &(pData->designable), &(pData->scriptable), &(pData->stored), &(pData->user), &(pData->constant), &(pData->final))) { + return 0; + } + + + pData->typeName = PySide::Signal::getTypeName(type); + + if (!pData->typeName) + PyErr_SetString(PyExc_TypeError, "Invalid property type or type name."); + else if (pData->constant && (pData->fset || pData->notify)) + PyErr_SetString(PyExc_TypeError, "A constant property cannot have a WRITE method or a NOTIFY signal."); + + if (!PyErr_Occurred()) { + Py_XINCREF(pData->fget); + Py_XINCREF(pData->fset); + Py_XINCREF(pData->freset); + Py_XINCREF(pData->fdel); + Py_XINCREF(pData->notify); + return 1; + } else { + pData->fget = 0; + pData->fset = 0; + pData->freset = 0; + pData->fdel = 0; + pData->notify = 0; + return -1; + } +} + +void qpropertyDeAlloc(PyObject* self) +{ + qpropertyClear(self); + Py_TYPE(self)->tp_free(self); +} + +PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + PyObject *callback = PyTuple_GetItem(args, 0); + if (PyFunction_Check(callback)) { + PySideProperty *prop = reinterpret_cast(self); + PySidePropertyPrivate* pData = prop->d; + + Py_INCREF(callback); + pData->fget = callback; + + Py_INCREF(self); + return self; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid property usage."); + return 0; + } +} + +PyObject* qPropertySetter(PyObject* self, PyObject* callback) +{ + if (PyFunction_Check(callback)) { + PySideProperty *prop = reinterpret_cast(self); + PySidePropertyPrivate* pData = prop->d; + + Py_INCREF(callback); + pData->fset = callback; + + Py_INCREF(callback); + return callback; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid property setter agument."); + return 0; + } +} + +PyObject* qPropertyGetter(PyObject* self, PyObject* callback) +{ + if (PyFunction_Check(callback)) { + PySideProperty *prop = reinterpret_cast(self); + PySidePropertyPrivate* pData = prop->d; + + Py_INCREF(callback); + pData->fget = callback; + + Py_INCREF(callback); + return callback; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid property getter agument."); + return 0; + } +} + +static int qpropertyTraverse(PyObject* self, visitproc visit, void* arg) +{ + PySidePropertyPrivate* data = reinterpret_cast(self)->d; + if (!data) + return 0; + + Py_VISIT(data->fget); + Py_VISIT(data->fset); + Py_VISIT(data->freset); + Py_VISIT(data->fdel); + Py_VISIT(data->notify); + return 0; +} + +static int qpropertyClear(PyObject* self) +{ + PySidePropertyPrivate* data = reinterpret_cast(self)->d; + if (!data) + return 0; + + Py_CLEAR(data->fget); + Py_CLEAR(data->fset); + Py_CLEAR(data->freset); + Py_CLEAR(data->fdel); + Py_CLEAR(data->notify); + + + free(data->typeName); + free(data->doc); + free(data->notifySignature); + delete data; + reinterpret_cast(self)->d = 0; + return 0; +} + +} // extern "C" + +namespace { + +static PyObject* getFromType(PyTypeObject* type, PyObject* name) +{ + PyObject* attr = 0; + attr = PyDict_GetItem(type->tp_dict, name); + if (!attr) { + PyObject* bases = type->tp_bases; + int size = PyTuple_GET_SIZE(bases); + for(int i=0; i < size; i++) { + PyObject* base = PyTuple_GET_ITEM(bases, i); + attr = getFromType(reinterpret_cast(base), name); + if (attr) + return attr; + } + } + return attr; +} + +} //namespace + + +namespace PySide { namespace Property { + +void init(PyObject* module) +{ + if (PyType_Ready(&PySidePropertyType) < 0) + return; + + Py_INCREF(&PySidePropertyType); + PyModule_AddObject(module, QPROPERTY_CLASS_NAME, reinterpret_cast(&PySidePropertyType)); +} + +bool checkType(PyObject* pyObj) +{ + if (pyObj) { + return PyType_IsSubtype(pyObj->ob_type, &PySidePropertyType); + } + return false; +} + +bool isPropertyType(PyObject* pyObj) +{ + return checkType(pyObj); +} + +int setValue(PySideProperty* self, PyObject* source, PyObject* value) +{ + PyObject* fset = self->d->fset; + if (fset) { + Shiboken::AutoDecRef args(PyTuple_New(2)); + PyTuple_SET_ITEM(args, 0, source); + PyTuple_SET_ITEM(args, 1, value); + Py_INCREF(source); + Py_INCREF(value); + Shiboken::AutoDecRef result(PyObject_CallObject(fset, args)); + return (result.isNull() ? -1 : 0); + } else { + PyErr_SetString(PyExc_AttributeError, "Attibute read only"); + } + return -1; +} + +PyObject* getValue(PySideProperty* self, PyObject* source) +{ + PyObject* fget = self->d->fget; + if (fget) { + Shiboken::AutoDecRef args(PyTuple_New(1)); + Py_INCREF(source); + PyTuple_SET_ITEM(args, 0, source); + return PyObject_CallObject(fget, args); + } + return 0; +} + +int reset(PySideProperty* self, PyObject* source) +{ + PyObject* freset = self->d->freset; + if (freset) { + Shiboken::AutoDecRef args(PyTuple_New(1)); + Py_INCREF(source); + PyTuple_SET_ITEM(args, 0, source); + Shiboken::AutoDecRef result(PyObject_CallObject(freset, args)); + return (result.isNull() ? -1 : 0); + } + return -1; +} + +const char* getTypeName(const PySideProperty* self) +{ + return self->d->typeName; +} + +PySideProperty* getObject(PyObject* source, PyObject* name) +{ + PyObject* attr = 0; + + if (Shiboken::Object::isUserType(source)) { + PyObject* dict = reinterpret_cast(source)->ob_dict; + if (dict) + attr = PyDict_GetItem(dict, name); + } + + attr = getFromType(source->ob_type, name); + if (attr && checkType(attr)) { + Py_INCREF(attr); + return reinterpret_cast(attr); + } + + if (!attr) + PyErr_Clear(); //Clear possible error caused by PyObject_GenericGetAttr + + return 0; +} + +bool isReadable(const PySideProperty * /* self */) +{ + return true; +} + +bool isWritable(const PySideProperty* self) +{ + return (self->d->fset != 0); +} + +bool hasReset(const PySideProperty* self) +{ + return (self->d->freset != 0); +} + +bool isDesignable(const PySideProperty* self) +{ + return self->d->designable; +} + +bool isScriptable(const PySideProperty* self) +{ + return self->d->scriptable; +} + +bool isStored(const PySideProperty* self) +{ + return self->d->stored; +} + +bool isUser(const PySideProperty* self) +{ + return self->d->user; +} + +bool isConstant(const PySideProperty* self) +{ + return self->d->constant; +} + +bool isFinal(const PySideProperty* self) +{ + return self->d->final; +} + +const char* getNotifyName(PySideProperty* self) +{ + if (!self->d->notifySignature) { + PyObject* str = PyObject_Str(self->d->notify); + self->d->notifySignature = strdup(Shiboken::String::toCString(str)); + Py_DECREF(str); + } + + return self->d->notifySignature; +} + +void setMetaCallHandler(PySideProperty* self, MetaCallHandler handler) +{ + self->d->metaCallHandler = handler; +} + +void setTypeName(PySideProperty* self, const char* typeName) +{ + self->d->typeName = strdup(typeName); +} + +void setUserData(PySideProperty* self, void* data) +{ + self->d->userData = data; +} + +void* userData(PySideProperty* self) +{ + return self->d->userData; +} + +} //namespace Property +} //namespace PySide diff --git a/sources/pyside2/libpyside/pysideproperty.h b/sources/pyside2/libpyside/pysideproperty.h new file mode 100644 index 000000000..9ac61dc23 --- /dev/null +++ b/sources/pyside2/libpyside/pysideproperty.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_PROPERTY_H +#define PYSIDE_PROPERTY_H + +#include +#include +#include + +extern "C" +{ + extern PYSIDE_API PyTypeObject PySidePropertyType; + + struct PySidePropertyPrivate; + struct PYSIDE_API PySideProperty + { + PyObject_HEAD + PySidePropertyPrivate* d; + }; +}; + +namespace PySide { namespace Property { + +typedef void (*MetaCallHandler)(PySideProperty*,PyObject*,QMetaObject::Call, void**); + +PYSIDE_API bool checkType(PyObject* pyObj); + +/// @deprecated Use checkType +PYSIDE_DEPRECATED(PYSIDE_API bool isPropertyType(PyObject* pyObj)); + +/** + * This function call set property function and pass value as arg + * This function does not check the property object type + * + * @param self The property object + * @param source The QObject witch has the property + * @param value The value to set in property + * @return Return 0 if ok or -1 if this function fail + **/ +PYSIDE_API int setValue(PySideProperty* self, PyObject* source, PyObject* value); + +/** + * This function call get property function + * This function does not check the property object type + * + * @param self The property object + * @param source The QObject witch has the property + * @return Return the result of property get function or 0 if this fail + **/ +PYSIDE_API PyObject* getValue(PySideProperty* self, PyObject* source); + +/** + * This function return the notify name used on this property + * + * @param self The property object + * @return Return a const char with the notify name used + **/ +PYSIDE_API const char* getNotifyName(PySideProperty* self); + + +/** + * This function search in the source object for desired property + * + * @param source The QObject object + * @param name The property name + * @return Return a new reference to property object + **/ +PYSIDE_API PySideProperty* getObject(PyObject* source, PyObject* name); + +PYSIDE_API void setMetaCallHandler(PySideProperty* self, MetaCallHandler handler); + +PYSIDE_API void setTypeName(PySideProperty* self, const char* typeName); + +PYSIDE_API void setUserData(PySideProperty* self, void* data); +PYSIDE_API void* userData(PySideProperty* self); + +} //namespace Property +} //namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideproperty_p.h b/sources/pyside2/libpyside/pysideproperty_p.h new file mode 100644 index 000000000..95ed2a0f8 --- /dev/null +++ b/sources/pyside2/libpyside/pysideproperty_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_QPROPERTY_P_H +#define PYSIDE_QPROPERTY_P_H + +#include +#include +#include "pysideproperty.h" + +struct PySideProperty; + +extern "C" +{ + +struct PySidePropertyPrivate { + char* typeName; + PySide::Property::MetaCallHandler metaCallHandler; + PyObject* fget; + PyObject* fset; + PyObject* freset; + PyObject* fdel; + PyObject* notify; + char* notifySignature; + char* doc; + bool designable; + bool scriptable; + bool stored; + bool user; + bool constant; + bool final; + void* userData; +}; + +} // extern "C" + +namespace PySide { namespace Property { + +/** + * Init PySide QProperty support system + */ +void init(PyObject* module); + +/** + * This function call reset property function + * This function does not check the property object type + * + * @param self The property object + * @param source The QObject witch has the property + * @return Return 0 if ok or -1 if this function fail + **/ +int reset(PySideProperty* self, PyObject* source); + + +/** + * This function return the property type + * This function does not check the property object type + * + * @param self The property object + * @return Return the property type name + **/ +const char* getTypeName(const PySideProperty* self); + +/** + * This function check if property has read function + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isReadable(const PySideProperty* self); + +/** + * This function check if property has write function + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isWritable(const PySideProperty* self); + +/** + * This function check if property has reset function + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool hasReset(const PySideProperty* self); + +/** + * This function check if property has the flag DESIGNABLE setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isDesignable(const PySideProperty* self); + +/** + * This function check if property has the flag SCRIPTABLE setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isScriptable(const PySideProperty* self); + +/** + * This function check if property has the flag STORED setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isStored(const PySideProperty* self); + +/** + * This function check if property has the flag USER setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isUser(const PySideProperty* self); + +/** + * This function check if property has the flag CONSTANT setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isConstant(const PySideProperty* self); + +/** + * This function check if property has the flag FINAL setted + * This function does not check the property object type + * + * @param self The property object + * @return Return a boolean value + **/ +bool isFinal(const PySideProperty* self); + +} // namespace Property +} // namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideqflags.cpp b/sources/pyside2/libpyside/pysideqflags.cpp new file mode 100644 index 000000000..380c67f61 --- /dev/null +++ b/sources/pyside2/libpyside/pysideqflags.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "pysideqflags.h" +#include +#include + +extern "C" { + struct SbkConverter; + + /** + * Type of all QFlags + */ + struct PySideQFlagsType + { + PyHeapTypeObject super; + SbkConverter** converterPtr; + SbkConverter* converter; + }; + + #define PYSIDE_QFLAGS(X) reinterpret_cast(X) + + PyObject *PySideQFlagsNew(PyTypeObject *type, PyObject *args, PyObject * /* kwds */) + { + long val = 0; + if (PyTuple_GET_SIZE(args)) { + PyObject* arg = PyTuple_GET_ITEM(args, 0); + if (Shiboken::isShibokenEnum(arg)) {// faster call + val = Shiboken::Enum::getValue(arg); + } else if (PyNumber_Check(arg)) { + Shiboken::AutoDecRef number(PyNumber_Long(arg)); + val = PyLong_AsLong(number); + } else { + PyErr_SetString(PyExc_TypeError,"QFlags must be created using enums or numbers."); + return 0; + } + } + PySideQFlagsObject* self = PyObject_New(PySideQFlagsObject, type); + self->ob_value = val; + return reinterpret_cast(self); + } + + static long getNumberValue(PyObject* v) + { + Shiboken::AutoDecRef number(PyNumber_Long(v)); + return PyLong_AsLong(number); + } + + PyObject* PySideQFlagsRichCompare(PyObject* self, PyObject* other, int op) + { + int result = 0; + if (!PyNumber_Check(other)) { + PyErr_BadArgument(); + return NULL; + } + + long valA = PYSIDE_QFLAGS(self)->ob_value; + long valB = getNumberValue(other); + + if (self == other) { + result = 1; + } else { + switch (op) { + case Py_EQ: + result = (valA == valB); + break; + case Py_NE: + result = (valA != valB); + break; + case Py_LE: + result = (valA <= valB); + break; + case Py_GE: + result = (valA >= valB); + break; + case Py_LT: + result = (valA < valB); + break; + case Py_GT: + result = (valA > valB); + break; + default: + PyErr_BadArgument(); + return NULL; + } + } + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } +} + +namespace PySide +{ +namespace QFlags +{ + PyTypeObject* create(const char* name, PyNumberMethods* numberMethods) + { + PyTypeObject* type = reinterpret_cast(new PySideQFlagsType); + ::memset(type, 0, sizeof(PySideQFlagsType)); + Py_TYPE(type) = &PyType_Type; + type->tp_basicsize = sizeof(PySideQFlagsObject); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES; + type->tp_name = name; + type->tp_new = &PySideQFlagsNew; + type->tp_as_number = numberMethods; + type->tp_richcompare = &PySideQFlagsRichCompare; + + PySideQFlagsType* flagsType = reinterpret_cast(type); + flagsType->converterPtr = &flagsType->converter; + + if (PyType_Ready(type) < 0) + return 0; + + return type; + } + + PySideQFlagsObject* newObject(long value, PyTypeObject* type) + { + PySideQFlagsObject* qflags = PyObject_New(PySideQFlagsObject, type); + qflags->ob_value = value; + return qflags; + } + + long getValue(PySideQFlagsObject* self) + { + return self->ob_value; + } +} +} diff --git a/sources/pyside2/libpyside/pysideqflags.h b/sources/pyside2/libpyside/pysideqflags.h new file mode 100644 index 000000000..500727eaf --- /dev/null +++ b/sources/pyside2/libpyside/pysideqflags.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_QFLAGS_H +#define PYSIDE_QFLAGS_H + +#include +#include "pysidemacros.h" + + +extern "C" +{ + struct PYSIDE_API PySideQFlagsObject { + PyObject_HEAD + long ob_value; + }; + + PYSIDE_API PyObject* PySideQFlagsNew(PyTypeObject *type, PyObject *args, PyObject *kwds); + PYSIDE_API PyObject* PySideQFlagsRichCompare(PyObject *self, PyObject *other, int op); +} + + +namespace PySide +{ +namespace QFlags +{ + /** + * Creates a new QFlags type. + */ + PYSIDE_API PyTypeObject* create(const char* name, PyNumberMethods* numberMethods); + /** + * Creates a new QFlags instance of type \p type and value \p value. + */ + PYSIDE_API PySideQFlagsObject* newObject(long value, PyTypeObject* type); + /** + * Returns the value held by a QFlag. + */ + PYSIDE_API long getValue(PySideQFlagsObject* self); +} +} + +#endif + diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp new file mode 100644 index 000000000..e213ff069 --- /dev/null +++ b/sources/pyside2/libpyside/pysidesignal.cpp @@ -0,0 +1,1070 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 +#include "pysidesignal.h" +#include "pysidesignal_p.h" +#include "signalmanager.h" + +#include +#include + +#define SIGNAL_CLASS_NAME "Signal" +#define SIGNAL_INSTANCE_NAME "SignalInstance" +#define QT_SIGNAL_SENTINEL '2' + +namespace PySide { +namespace Signal { + //aux + class SignalSignature { + public: + SignalSignature() : m_attributes(QMetaMethod::Compatibility) {} + SignalSignature(QByteArray parameterTypes) : m_parameterTypes(parameterTypes), + m_attributes(QMetaMethod::Compatibility) {} + SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) : + m_parameterTypes(parameterTypes), + m_attributes(attributes) {} + QByteArray m_parameterTypes; + QMetaMethod::Attributes m_attributes; + }; + + static char* buildSignature(const char*, const char*); + static void appendSignature(PySideSignal*, const SignalSignature &); + static void instanceInitialize(PySideSignalInstance*, PyObject*, PySideSignal*, PyObject*, int); + static char* parseSignature(PyObject*); + static PyObject* buildQtCompatible(const char*); +} +} + +extern "C" +{ + +// Signal methods +static int signalTpInit(PyObject*, PyObject*, PyObject*); +static void signalFree(void*); +static void signalInstanceFree(void*); +static PyObject* signalGetItem(PyObject* self, PyObject* key); +static PyObject* signalToString(PyObject* self); + +// Signal Instance methods +static PyObject* signalInstanceConnect(PyObject*, PyObject*, PyObject*); +static PyObject* signalInstanceDisconnect(PyObject*, PyObject*); +static PyObject* signalInstanceEmit(PyObject*, PyObject*); +static PyObject* signalInstanceGetItem(PyObject*, PyObject*); + +static PyObject* signalInstanceCall(PyObject* self, PyObject* args, PyObject* kw); +static PyObject* signalCall(PyObject*, PyObject*, PyObject*); + +static PyObject* metaSignalCheck(PyObject*, PyObject*); + +static PyMappingMethods Signal_as_mapping = { + 0, + signalGetItem, + 0 +}; + +static PyMethodDef Signal_methods[] = { + {"__instancecheck__", (PyCFunction)metaSignalCheck, METH_O, NULL}, + {0, 0, 0, 0} +}; + +PyTypeObject PySideSignalMetaType = { + PyVarObject_HEAD_INIT(0, 0) + /*tp_name*/ "PySide2.QtCore.MetaSignal", + /*tp_basicsize*/ sizeof(PyTypeObject), + /*tp_itemsize*/ 0, + /*tp_dealloc*/ 0, + /*tp_print*/ 0, + /*tp_getattr*/ 0, + /*tp_setattr*/ 0, + /*tp_compare*/ 0, + /*tp_repr*/ 0, + /*tp_as_number*/ 0, + /*tp_as_sequence*/ 0, + /*tp_as_mapping*/ 0, + /*tp_hash*/ 0, + /*tp_call*/ 0, + /*tp_str*/ 0, + /*tp_getattro*/ 0, + /*tp_setattro*/ 0, + /*tp_as_buffer*/ 0, + /*tp_flags*/ Py_TPFLAGS_DEFAULT, + /*tp_doc*/ 0, + /*tp_traverse*/ 0, + /*tp_clear*/ 0, + /*tp_richcompare*/ 0, + /*tp_weaklistoffset*/ 0, + /*tp_iter*/ 0, + /*tp_iternext*/ 0, + /*tp_methods*/ Signal_methods, + /*tp_members*/ 0, + /*tp_getset*/ 0, + /*tp_base*/ &PyType_Type, + /*tp_dict*/ 0, + /*tp_descr_get*/ 0, + /*tp_descr_set*/ 0, + /*tp_dictoffset*/ 0, + /*tp_init*/ 0, + /*tp_alloc*/ 0, + /*tp_new*/ 0, + /*tp_free*/ 0, + /*tp_is_gc*/ 0, + /*tp_bases*/ 0, + /*tp_mro*/ 0, + /*tp_cache*/ 0, + /*tp_subclasses*/ 0, + /*tp_weaklist*/ 0, + /*tp_del*/ 0, + /*tp_version_tag*/ 0 +}; + +PyTypeObject PySideSignalType = { + PyVarObject_HEAD_INIT(&PySideSignalMetaType, 0) + /*tp_name*/ "PySide2.QtCore." SIGNAL_CLASS_NAME, + /*tp_basicsize*/ sizeof(PySideSignal), + /*tp_itemsize*/ 0, + /*tp_dealloc*/ 0, + /*tp_print*/ 0, + /*tp_getattr*/ 0, + /*tp_setattr*/ 0, + /*tp_compare*/ 0, + /*tp_repr*/ 0, + /*tp_as_number*/ 0, + /*tp_as_sequence*/ 0, + /*tp_as_mapping*/ &Signal_as_mapping, + /*tp_hash*/ 0, + /*tp_call*/ signalCall, + /*tp_str*/ signalToString, + /*tp_getattro*/ 0, + /*tp_setattro*/ 0, + /*tp_as_buffer*/ 0, + /*tp_flags*/ Py_TPFLAGS_DEFAULT, + /*tp_doc*/ SIGNAL_CLASS_NAME, + /*tp_traverse*/ 0, + /*tp_clear*/ 0, + /*tp_richcompare*/ 0, + /*tp_weaklistoffset*/ 0, + /*tp_iter*/ 0, + /*tp_iternext*/ 0, + /*tp_methods*/ 0, + /*tp_members*/ 0, + /*tp_getset*/ 0, + /*tp_base*/ 0, + /*tp_dict*/ 0, + /*tp_descr_get*/ 0, + /*tp_descr_set*/ 0, + /*tp_dictoffset*/ 0, + /*tp_init*/ signalTpInit, + /*tp_alloc*/ 0, + /*tp_new*/ PyType_GenericNew, + /*tp_free*/ signalFree, + /*tp_is_gc*/ 0, + /*tp_bases*/ 0, + /*tp_mro*/ 0, + /*tp_cache*/ 0, + /*tp_subclasses*/ 0, + /*tp_weaklist*/ 0, + /*tp_del*/ 0, + /*tp_version_tag*/ 0 +}; + +static PyMethodDef SignalInstance_methods[] = { + {"connect", (PyCFunction)signalInstanceConnect, METH_VARARGS|METH_KEYWORDS, 0}, + {"disconnect", signalInstanceDisconnect, METH_VARARGS, 0}, + {"emit", signalInstanceEmit, METH_VARARGS, 0}, + {0, 0, 0, 0} /* Sentinel */ +}; + +static PyMappingMethods SignalInstance_as_mapping = { + 0, + signalInstanceGetItem, + 0 +}; + +PyTypeObject PySideSignalInstanceType = { + PyVarObject_HEAD_INIT(0, 0) + /*tp_name*/ "PySide2.QtCore." SIGNAL_INSTANCE_NAME, + /*tp_basicsize*/ sizeof(PySideSignalInstance), + /*tp_itemsize*/ 0, + /*tp_dealloc*/ 0, + /*tp_print*/ 0, + /*tp_getattr*/ 0, + /*tp_setattr*/ 0, + /*tp_compare*/ 0, + /*tp_repr*/ 0, + /*tp_as_number*/ 0, + /*tp_as_sequence*/ 0, + /*tp_as_mapping*/ &SignalInstance_as_mapping, + /*tp_hash*/ 0, + /*tp_call*/ signalInstanceCall, + /*tp_str*/ 0, + /*tp_getattro*/ 0, + /*tp_setattro*/ 0, + /*tp_as_buffer*/ 0, + /*tp_flags*/ Py_TPFLAGS_DEFAULT, + /*tp_doc*/ SIGNAL_INSTANCE_NAME, + /*tp_traverse*/ 0, + /*tp_clear*/ 0, + /*tp_richcompare*/ 0, + /*tp_weaklistoffset*/ 0, + /*tp_iter*/ 0, + /*tp_iternext*/ 0, + /*tp_methods*/ SignalInstance_methods, + /*tp_members*/ 0, + /*tp_getset*/ 0, + /*tp_base*/ 0, + /*tp_dict*/ 0, + /*tp_descr_get*/ 0, + /*tp_descr_set*/ 0, + /*tp_dictoffset*/ 0, + /*tp_init*/ 0, + /*tp_alloc*/ 0, + /*tp_new*/ PyType_GenericNew, + /*tp_free*/ signalInstanceFree, + /*tp_is_gc*/ 0, + /*tp_bases*/ 0, + /*tp_mro*/ 0, + /*tp_cache*/ 0, + /*tp_subclasses*/ 0, + /*tp_weaklist*/ 0, + /*tp_del*/ 0, + /*tp_version_tag*/ 0 +}; + +int signalTpInit(PyObject* self, PyObject* args, PyObject* kwds) +{ + static PyObject* emptyTuple = 0; + static const char* kwlist[] = {"name", 0}; + char* argName = 0; + + if (emptyTuple == 0) + emptyTuple = PyTuple_New(0); + + if (!PyArg_ParseTupleAndKeywords(emptyTuple, kwds, + "|s:QtCore." SIGNAL_CLASS_NAME, const_cast(kwlist), &argName)) + return 0; + + bool tupledArgs = false; + PySideSignal* data = reinterpret_cast(self); + if (argName) { + data->signalName = strdup(argName); + } + + for (Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) { + PyObject* arg = PyTuple_GET_ITEM(args, i); + if (PySequence_Check(arg) && !Shiboken::String::check(arg)) { + tupledArgs = true; + char *sig = PySide::Signal::parseSignature(arg); + PySide::Signal::appendSignature( + data, + PySide::Signal::SignalSignature(sig)); + free(sig); + } + } + + if (!tupledArgs) { + char *sig = PySide::Signal::parseSignature(args); + PySide::Signal::appendSignature( + data, + PySide::Signal::SignalSignature(sig)); + free(sig); + } + + return 1; +} + +void signalFree(void* self) +{ + PyObject* pySelf = reinterpret_cast(self); + PySideSignal* data = reinterpret_cast(self); + + for (int i = 0, i_max = data->signaturesSize; i < i_max; i++) { + if (data->signatures[i]) + free(data->signatures[i]); + } + + free(data->signatures); + free(data->signatureAttributes); + free(data->signalName); + data->initialized = 0; + data->signaturesSize = 0; + Py_XDECREF(data->homonymousMethod); + data->homonymousMethod = 0; + + pySelf->ob_type->tp_base->tp_free(self); +} + +PyObject* signalGetItem(PyObject* self, PyObject* key) +{ + PySideSignal* data = reinterpret_cast(self); + char* sigKey; + if (key) { + sigKey = PySide::Signal::parseSignature(key); + } else { + if (data->signatures[0]) + sigKey = strdup(data->signatures[0]); + else + sigKey = strdup("void"); + } + char* sig = PySide::Signal::buildSignature(data->signalName, sigKey); + free(sigKey); + PyObject* pySignature = Shiboken::String::fromCString(sig); + free(sig); + return pySignature; +} + + +PyObject* signalToString(PyObject* self) +{ + return signalGetItem(self, 0); +} + +void signalInstanceFree(void* self) +{ + PyObject* pySelf = reinterpret_cast(self); + PySideSignalInstance* data = reinterpret_cast(self); + + PySideSignalInstancePrivate* dataPvt = data->d; + free(dataPvt->signalName); + free(dataPvt->signature); + + Py_XDECREF(dataPvt->homonymousMethod); + + if (dataPvt->next) { + Py_DECREF(dataPvt->next); + dataPvt->next = 0; + } + delete dataPvt; + data->d = 0; + pySelf->ob_type->tp_base->tp_free(self); +} + +PyObject* signalInstanceConnect(PyObject* self, PyObject* args, PyObject* kwds) +{ + PyObject* slot = 0; + PyObject* type = 0; + static const char* kwlist[] = {"slot", "type", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O|O:" SIGNAL_INSTANCE_NAME, const_cast(kwlist), &slot, &type)) + return 0; + + PySideSignalInstance* source = reinterpret_cast(self); + Shiboken::AutoDecRef pyArgs(PyList_New(0)); + + bool match = false; + if (slot->ob_type == &PySideSignalInstanceType) { + PySideSignalInstance* sourceWalk = source; + PySideSignalInstance* targetWalk; + + //find best match + while (sourceWalk && !match) { + targetWalk = reinterpret_cast(slot); + while (targetWalk && !match) { + if (QMetaObject::checkConnectArgs(sourceWalk->d->signature, targetWalk->d->signature)) { + PyList_Append(pyArgs, sourceWalk->d->source); + Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(sourceWalk->d->signature)); + PyList_Append(pyArgs, sourceSignature); + + PyList_Append(pyArgs, targetWalk->d->source); + Shiboken::AutoDecRef targetSignature(PySide::Signal::buildQtCompatible(targetWalk->d->signature)); + PyList_Append(pyArgs, targetSignature); + + match = true; + } + targetWalk = reinterpret_cast(targetWalk->d->next); + } + sourceWalk = reinterpret_cast(sourceWalk->d->next); + } + } else { + //try the first signature + PyList_Append(pyArgs, source->d->source); + Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature)); + PyList_Append(pyArgs, signature); + + PyList_Append(pyArgs, slot); + match = true; + } + + if (type) + PyList_Append(pyArgs, type); + + if (match) { + Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(source->d->source, "connect")); + if (pyMethod.isNull()) { // PYSIDE-79: check if pyMethod exists. + PyErr_SetString(PyExc_RuntimeError, "method 'connect' vanished!"); + return 0; + } + PyObject* result = PyObject_CallObject(pyMethod, tupleArgs); + if (result == Py_True) + return result; + else + Py_XDECREF(result); + } + if (!PyErr_Occurred()) // PYSIDE-79: inverse the logic. A Null return needs an error. + PyErr_Format(PyExc_RuntimeError, "Failed to connect signal %s.", source->d->signature); + return 0; +} + +int argCountInSignature(const char *signature) +{ + return QByteArray(signature).count(",") + 1; +} + +PyObject* signalInstanceEmit(PyObject* self, PyObject* args) +{ + PySideSignalInstance* source = reinterpret_cast(self); + + Shiboken::AutoDecRef pyArgs(PyList_New(0)); + int numArgsGiven = PySequence_Fast_GET_SIZE(args); + int numArgsInSignature = argCountInSignature(source->d->signature); + + // If number of arguments given to emit is smaller than the first source signature expects, + // it is possible it's a case of emitting a signal with default parameters. + // Search through all the overloaded signals with the same name, and try to find a signature + // with the same number of arguments as given to emit, and is also marked as a cloned method + // (which in metaobject parlance means a signal with default parameters). + // @TODO: This should be improved to take into account argument types as well. The current + // assumption is there are no signals which are both overloaded on argument types and happen to + // have signatures with default parameters. + if (numArgsGiven < numArgsInSignature) { + PySideSignalInstance *possibleDefaultInstance = source; + while ((possibleDefaultInstance = possibleDefaultInstance->d->next)) { + if (possibleDefaultInstance->d->attributes & QMetaMethod::Cloned + && argCountInSignature(possibleDefaultInstance->d->signature) == numArgsGiven) { + source = possibleDefaultInstance; + break; + } + } + } + Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(source->d->signature)); + + PyList_Append(pyArgs, sourceSignature); + for (Py_ssize_t i = 0, max = PyTuple_Size(args); i < max; i++) + PyList_Append(pyArgs, PyTuple_GetItem(args, i)); + + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(source->d->source, "emit")); + + Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); + return PyObject_CallObject(pyMethod, tupleArgs); +} + +PyObject* signalInstanceGetItem(PyObject* self, PyObject* key) +{ + PySideSignalInstance* data = reinterpret_cast(self); + char* sigKey = PySide::Signal::parseSignature(key); + char* sig = PySide::Signal::buildSignature(data->d->signalName, sigKey); + free(sigKey); + const char* sigName = data->d->signalName; + + while (data) { + if (strcmp(data->d->signature, sig) == 0) { + free(sig); + PyObject* result = reinterpret_cast(data); + Py_INCREF(result); + return result; + } + data = reinterpret_cast(data->d->next); + } + + PyErr_Format(PyExc_IndexError, "Signature %s not found for signal: %s", sig, sigName); + free(sig); + return 0; +} + +PyObject* signalInstanceDisconnect(PyObject* self, PyObject* args) +{ + PySideSignalInstance* source = reinterpret_cast(self); + Shiboken::AutoDecRef pyArgs(PyList_New(0)); + + PyObject* slot; + if (PyTuple_Check(args) && PyTuple_GET_SIZE(args)) + slot = PyTuple_GET_ITEM(args, 0); + else + slot = Py_None; + + bool match = false; + if (slot->ob_type == &PySideSignalInstanceType) { + PySideSignalInstance* target = reinterpret_cast(slot); + if (QMetaObject::checkConnectArgs(source->d->signature, target->d->signature)) { + PyList_Append(pyArgs, source->d->source); + Shiboken::AutoDecRef source_signature(PySide::Signal::buildQtCompatible(source->d->signature)); + PyList_Append(pyArgs, source_signature); + + PyList_Append(pyArgs, target->d->source); + Shiboken::AutoDecRef target_signature(PySide::Signal::buildQtCompatible(target->d->signature)); + PyList_Append(pyArgs, target_signature); + match = true; + } + } else { + //try the first signature + PyList_Append(pyArgs, source->d->source); + Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature)); + PyList_Append(pyArgs, signature); + + // disconnect all, so we need to use the c++ signature disconnect(qobj, signal, 0, 0) + if (slot == Py_None) + PyList_Append(pyArgs, slot); + PyList_Append(pyArgs, slot); + match = true; + } + + if (match) { + Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(source->d->source, "disconnect")); + PyObject* result = PyObject_CallObject(pyMethod, tupleArgs); + if (!result || result == Py_True) + return result; + else + Py_DECREF(result); + } + + PyErr_Format(PyExc_RuntimeError, "Failed to disconnect signal %s.", source->d->signature); + return 0; +} + +PyObject* signalCall(PyObject* self, PyObject* args, PyObject* kw) +{ + PySideSignal* signal = reinterpret_cast(self); + + if (!signal->homonymousMethod) { + PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable"); + return 0; + } + + descrgetfunc getDescriptor = signal->homonymousMethod->ob_type->tp_descr_get; + Shiboken::AutoDecRef homonymousMethod(getDescriptor(signal->homonymousMethod, 0, 0)); + + if (PyCFunction_GET_FLAGS(homonymousMethod.object()) & METH_STATIC) + return PyCFunction_Call(homonymousMethod, args, kw); + + ternaryfunc callFunc = signal->homonymousMethod->ob_type->tp_call; + return callFunc(homonymousMethod, args, kw); +} + +PyObject* signalInstanceCall(PyObject* self, PyObject* args, PyObject* kw) +{ + PySideSignalInstance* PySideSignal = reinterpret_cast(self); + if (!PySideSignal->d->homonymousMethod) { + PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable"); + return 0; + } + + descrgetfunc getDescriptor = PySideSignal->d->homonymousMethod->ob_type->tp_descr_get; + Shiboken::AutoDecRef homonymousMethod(getDescriptor(PySideSignal->d->homonymousMethod, PySideSignal->d->source, 0)); + return PyCFunction_Call(homonymousMethod, args, kw); +} + +static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject* args) +{ + if (PyType_IsSubtype(args->ob_type, &PySideSignalInstanceType)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +} // extern "C" + +namespace PySide { +namespace Signal { + +void init(PyObject* module) +{ + if (PyType_Ready(&PySideSignalMetaType) < 0) + return; + + if (PyType_Ready(&PySideSignalType) < 0) + return; + + Py_INCREF(&PySideSignalType); + PyModule_AddObject(module, SIGNAL_CLASS_NAME, reinterpret_cast(&PySideSignalType)); + + if (PyType_Ready(&PySideSignalInstanceType) < 0) + return; + + Py_INCREF(&PySideSignalInstanceType); +} + +bool checkType(PyObject* pyObj) +{ + if (pyObj) + return PyType_IsSubtype(pyObj->ob_type, &PySideSignalType); + return false; +} + +void updateSourceObject(PyObject* source) +{ + PyTypeObject* objType = reinterpret_cast(PyObject_Type(source)); + + Py_ssize_t pos = 0; + PyObject* value; + PyObject* key; + + while (PyDict_Next(objType->tp_dict, &pos, &key, &value)) { + if (PyObject_TypeCheck(value, &PySideSignalType)) { + Shiboken::AutoDecRef signalInstance(reinterpret_cast(PyObject_New(PySideSignalInstance, &PySideSignalInstanceType))); + instanceInitialize(signalInstance.cast(), key, reinterpret_cast(value), source, 0); + PyObject_SetAttr(source, key, signalInstance); + } + } + + Py_XDECREF(objType); +} + +char* getTypeName(PyObject* type) +{ + if (PyType_Check(type)) { + char* typeName = NULL; + if (PyType_IsSubtype(reinterpret_cast(type), reinterpret_cast(&SbkObject_Type))) { + SbkObjectType* objType = reinterpret_cast(type); + typeName = strdup(Shiboken::ObjectType::getOriginalName(objType)); + } else { + // Translate python types to Qt names + PyTypeObject* objType = reinterpret_cast(type); + if (Shiboken::String::checkType(objType)) + typeName = strdup("QString"); + else if (objType == &PyInt_Type) + typeName = strdup("int"); + else if (objType == &PyLong_Type) + typeName = strdup("long"); + else if (objType == &PyFloat_Type) + typeName = strdup("double"); + else if (objType == &PyBool_Type) + typeName = strdup("bool"); + else if (Py_TYPE(objType) == &SbkEnumType_Type) + typeName = strdup(Shiboken::Enum::getCppName(objType)); + else + typeName = strdup("PyObject"); + } + return typeName; + } else if (type == Py_None) { // Must be checked before as Shiboken::String::check accepts Py_None + return strdup("void"); + } else if (Shiboken::String::check(type)) { + const char *result = Shiboken::String::toCString(type); + if (!strcmp(result, "qreal")) + result = sizeof(qreal) == sizeof(double) ? "double" : "float"; + return strdup(result); + } + return 0; +} + +char* buildSignature(const char* name, const char* signature) +{ + QByteArray signal(name); + signal += '('; + signal += signature; + signal += ')'; + return strdup(QMetaObject::normalizedSignature(signal)); +} + +char* parseSignature(PyObject* args) +{ + char* signature = 0; + if (args && (Shiboken::String::check(args) || !PySequence_Check(args))) + return getTypeName(args); + + for (Py_ssize_t i = 0, i_max = PySequence_Size(args); i < i_max; i++) { + Shiboken::AutoDecRef arg(PySequence_ITEM(args, i)); + char* typeName = getTypeName(arg); + if (typeName) { + if (signature) { + signature = reinterpret_cast(realloc(signature, (strlen(signature) + 1 + strlen(typeName)) * sizeof(char*))); + signature = strcat(signature, ","); + signature = strcat(signature, typeName); + free(typeName); + } else { + signature = typeName; + } + } + } + return signature; +} + +void appendSignature(PySideSignal* self, const SignalSignature &signature) +{ + self->signaturesSize++; + + if (self->signaturesSize > 1) { + self->signatures = reinterpret_cast(realloc(self->signatures, sizeof(char *) * self->signaturesSize)); + self->signatureAttributes = reinterpret_cast(realloc(self->signatureAttributes, sizeof(int) * self->signaturesSize)); + } else { + self->signatures = reinterpret_cast(malloc(sizeof(char *))); + self->signatureAttributes = reinterpret_cast(malloc(sizeof(int))); + } + self->signatures[self->signaturesSize - 1] = strdup(signature.m_parameterTypes); + self->signatureAttributes[self->signaturesSize - 1] = signature.m_attributes; +} + +PySideSignalInstance* initialize(PySideSignal* self, PyObject* name, PyObject* object) +{ + PySideSignalInstance* instance = PyObject_New(PySideSignalInstance, &PySideSignalInstanceType); + SbkObject* sbkObj = reinterpret_cast(object); + if (!Shiboken::Object::wasCreatedByPython(sbkObj)) + Py_INCREF(object); // PYSIDE-79: this flag was crucial for a wrapper call. + instanceInitialize(instance, name, self, object, 0); + return instance; +} + +void instanceInitialize(PySideSignalInstance* self, PyObject* name, PySideSignal* data, PyObject* source, int index) +{ + self->d = new PySideSignalInstancePrivate; + PySideSignalInstancePrivate* selfPvt = self->d; + selfPvt->next = 0; + if (data->signalName) + selfPvt->signalName = strdup(data->signalName); + else { + selfPvt->signalName = strdup(Shiboken::String::toCString(name)); + data->signalName = strdup(selfPvt->signalName); + } + + selfPvt->source = source; + selfPvt->signature = buildSignature(self->d->signalName, data->signatures[index]); + selfPvt->attributes = data->signatureAttributes[index]; + selfPvt->homonymousMethod = 0; + if (data->homonymousMethod) { + selfPvt->homonymousMethod = data->homonymousMethod; + Py_INCREF(selfPvt->homonymousMethod); + } + index++; + + if (index < data->signaturesSize) { + selfPvt->next = PyObject_New(PySideSignalInstance, &PySideSignalInstanceType); + instanceInitialize(selfPvt->next, name, data, source, index); + } +} + +bool connect(PyObject* source, const char* signal, PyObject* callback) +{ + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(source, "connect")); + if (pyMethod.isNull()) + return false; + + Shiboken::AutoDecRef pySignature(Shiboken::String::fromCString(signal)); + Shiboken::AutoDecRef pyArgs(PyTuple_Pack(3, source, pySignature.object(), callback)); + PyObject* result = PyObject_CallObject(pyMethod, pyArgs); + if (result == Py_False) { + PyErr_Format(PyExc_RuntimeError, "Failed to connect signal %s, to python callable object.", signal); + Py_DECREF(result); + result = 0; + } + return result; +} + +PySideSignalInstance* newObjectFromMethod(PyObject* source, const QList& methodList) +{ + PySideSignalInstance* root = 0; + PySideSignalInstance* previous = 0; + foreach (const QMetaMethod &m, methodList) { + PySideSignalInstance* item = PyObject_New(PySideSignalInstance, &PySideSignalInstanceType); + if (!root) + root = item; + + if (previous) + previous->d->next = item; + + item->d = new PySideSignalInstancePrivate; + PySideSignalInstancePrivate* selfPvt = item->d; + selfPvt->source = source; + Py_INCREF(selfPvt->source); // PYSIDE-79: an INCREF is missing. + QByteArray cppName(m.methodSignature()); + cppName.truncate(cppName.indexOf('(')); + // separe SignalName + selfPvt->signalName = strdup(cppName.data()); + selfPvt->signature = strdup(m.methodSignature()); + selfPvt->attributes = m.attributes(); + selfPvt->homonymousMethod = 0; + selfPvt->next = 0; + } + return root; +} + +PySideSignal* newObject(const char* name, ...) +{ + va_list listSignatures; + char* sig = 0; + PySideSignal* self = PyObject_New(PySideSignal, &PySideSignalType); + self->signalName = strdup(name); + self->signaturesSize = 0; + self->signatures = 0; + self->signatureAttributes = 0; + self->initialized = 0; + self->homonymousMethod = 0; + + va_start(listSignatures, name); + sig = va_arg(listSignatures, char*); + + while (sig != NULL) { + if (strcmp(sig, "void") == 0) + appendSignature(self, SignalSignature("")); + else + appendSignature(self, SignalSignature(sig)); + + sig = va_arg(listSignatures, char*); + } + + va_end(listSignatures); + + return self; +} + +template +static typename T::value_type join(T t, const char* sep) +{ + typename T::value_type res; + if (!t.size()) + return res; + + typename T::const_iterator it = t.begin(); + typename T::const_iterator end = t.end(); + res += *it; + ++it; + + while (it != end) { + res += sep; + res += *it; + ++it; + } + return res; +} + +static void _addSignalToWrapper(SbkObjectType* wrapperType, const char* signalName, PySideSignal* signal) +{ + PyObject* typeDict = wrapperType->super.ht_type.tp_dict; + PyObject* homonymousMethod; + if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) { + Py_INCREF(homonymousMethod); + signal->homonymousMethod = homonymousMethod; + } + PyDict_SetItemString(typeDict, signalName, reinterpret_cast(signal)); +} + +// This function is used by qStableSort to promote empty signatures +static bool compareSignals(const SignalSignature &sig1, const SignalSignature &) +{ + return sig1.m_parameterTypes.isEmpty(); +} + +void registerSignals(SbkObjectType* pyObj, const QMetaObject* metaObject) +{ + typedef QHash > SignalSigMap; + SignalSigMap signalsFound; + for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) { + QMetaMethod method = metaObject->method(i); + + if (method.methodType() == QMetaMethod::Signal) { + QByteArray methodName(method.methodSignature()); + methodName.chop(methodName.size() - methodName.indexOf('(')); + SignalSignature signature; + signature.m_parameterTypes = join(method.parameterTypes(), ","); + if (method.attributes() & QMetaMethod::Cloned) + signature.m_attributes = QMetaMethod::Cloned; + signalsFound[methodName] << signature; + } + } + + SignalSigMap::Iterator it = signalsFound.begin(); + SignalSigMap::Iterator end = signalsFound.end(); + for (; it != end; ++it) { + PySideSignal* self = PyObject_New(PySideSignal, &PySideSignalType); + self->signalName = strdup(it.key().constData()); + self->signaturesSize = 0; + self->signatures = 0; + self->signatureAttributes = 0; + self->initialized = 0; + self->homonymousMethod = 0; + + // Empty signatures comes first! So they will be the default signal signature + qStableSort(it.value().begin(), it.value().end(), &compareSignals); + SignalSigMap::mapped_type::const_iterator j = it.value().begin(); + SignalSigMap::mapped_type::const_iterator endJ = it.value().end(); + for (; j != endJ; ++j) { + const SignalSignature &sig = *j; + appendSignature(self, sig); + } + + _addSignalToWrapper(pyObj, it.key(), self); + Py_DECREF(reinterpret_cast(self)); + } +} + +PyObject* buildQtCompatible(const char* signature) +{ + QByteArray ba; + ba.append(QT_SIGNAL_SENTINEL); + ba.append(signature); + return Shiboken::String::fromStringAndSize(ba, ba.size()); +} + +void addSignalToWrapper(SbkObjectType* wrapperType, const char* signalName, PySideSignal* signal) +{ + _addSignalToWrapper(wrapperType, signalName, signal); +} + +PyObject* getObject(PySideSignalInstance* signal) +{ + return signal->d->source; +} + +const char* getSignature(PySideSignalInstance* signal) +{ + return signal->d->signature; +} + +const char** getSignatures(PyObject* signal, int* size) +{ + PySideSignal* self = reinterpret_cast(signal); + *size = self->signaturesSize; + return const_cast(self->signatures); +} + +QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit) +{ + const QString qsignature = QLatin1String(signature); + QStringList result; + QRegExp splitRegex(QLatin1String("\\s*,\\s*")); + + if (isShortCircuit) + *isShortCircuit = !qsignature.contains(QLatin1Char('(')); + if (qsignature.contains(QLatin1String("()")) || qsignature.contains(QLatin1String("(void)"))) { + return result; + } else if (qsignature.contains(QLatin1Char('('))) { + static QRegExp regex(QLatin1String(".+\\((.*)\\)")); + //get args types + QString types = qsignature; + types.replace(regex, QLatin1String("\\1")); + result = types.split(splitRegex); + } + return result; +} + +QString getCallbackSignature(const char* signal, QObject* receiver, PyObject* callback, bool encodeName) +{ + QByteArray functionName; + int numArgs = -1; + bool useSelf = false; + bool isMethod = PyMethod_Check(callback); + bool isFunction = PyFunction_Check(callback); + + if (isMethod || isFunction) { + PyObject* function = isMethod ? PyMethod_GET_FUNCTION(callback) : callback; + PyCodeObject* objCode = reinterpret_cast(PyFunction_GET_CODE(function)); + functionName = Shiboken::String::toCString(reinterpret_cast(function)->func_name); + useSelf = isMethod; + numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; + } else if (PyCFunction_Check(callback)) { + const PyCFunctionObject *funcObj = reinterpret_cast(callback); + functionName = funcObj->m_ml->ml_name; + useSelf = funcObj->m_self; + const int flags = funcObj->m_ml->ml_flags; + + if (receiver) { + //Search for signature on metaobject + const QMetaObject* mo = receiver->metaObject(); + QByteArray prefix(functionName); + prefix += '('; + for (int i = 0; i < mo->methodCount(); i++) { + QMetaMethod me = mo->method(i); + if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) && + QMetaObject::checkConnectArgs(signal, me.methodSignature())) { + numArgs = me.parameterTypes().size() + useSelf; + break; + } + } + } + + if (numArgs == -1) { + if (flags & METH_VARARGS) + numArgs = -1; + else if (flags & METH_NOARGS) + numArgs = 0; + } + } else if (PyCallable_Check(callback)) { + functionName = "__callback" + QByteArray::number((qlonglong)callback); + } + + Q_ASSERT(!functionName.isEmpty()); + + bool isShortCircuit = false; + + const QString functionNameS = QLatin1String(functionName); + QString signature = encodeName ? codeCallbackName(callback, functionNameS) : functionNameS; + QStringList args = getArgsFromSignature(signal, &isShortCircuit); + + if (!isShortCircuit) { + signature.append(QLatin1Char('(')); + if (numArgs == -1) + numArgs = std::numeric_limits::max(); + while (args.count() && (args.count() > (numArgs - useSelf))) { + args.removeLast(); + } + signature.append(args.join(QLatin1Char(','))); + signature.append(QLatin1Char(')')); + } + return signature; +} + +bool isQtSignal(const char* signal) +{ + return (signal && signal[0] == QT_SIGNAL_SENTINEL); +} + +bool checkQtSignal(const char* signal) +{ + if (!isQtSignal(signal)) { + PyErr_SetString(PyExc_TypeError, "Use the function PySide2.QtCore.SIGNAL on signals"); + return false; + } + return true; +} + +QString codeCallbackName(PyObject* callback, const QString& funcName) +{ + if (PyMethod_Check(callback)) { + PyObject* self = PyMethod_GET_SELF(callback); + PyObject* func = PyMethod_GET_FUNCTION(callback); + return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); + } else { + return funcName + QString::number(quint64(callback), 16); + } +} + +} //namespace Signal +} //namespace PySide + diff --git a/sources/pyside2/libpyside/pysidesignal.h b/sources/pyside2/libpyside/pysidesignal.h new file mode 100644 index 000000000..ead62198c --- /dev/null +++ b/sources/pyside2/libpyside/pysidesignal.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_SIGNAL_H +#define PYSIDE_SIGNAL_H + +#include +#include +#include + +#include +#include +#include + +extern "C" +{ + extern PYSIDE_API PyTypeObject PySideSignalType; + extern PYSIDE_API PyTypeObject PySideSignalInstanceType; + + // Internal object + struct PYSIDE_API PySideSignal; + + struct PySideSignalInstancePrivate; + struct PYSIDE_API PySideSignalInstance + { + PyObject_HEAD + PySideSignalInstancePrivate* d; + }; +}; // extern "C" + +namespace PySide { +namespace Signal { + +PYSIDE_API bool checkType(PyObject* type); + +/** + * This function creates a Signal object which stays attached to QObject class + * + * @param name of the Signal to be registered on meta object + * @param signatures a list of signatures supported by this signal, ended with a NULL pointer + * @return Return a new reference to PyObject* of type PySideSignal + * @deprecated Use registerSignals + **/ +PYSIDE_DEPRECATED(PYSIDE_API PySideSignal* newObject(const char* name, ...)); + +/** + * Register all C++ signals of a QObject on Python type. + */ +PYSIDE_API void registerSignals(SbkObjectType* pyObj, const QMetaObject* metaObject); + +/** + * This function creates a Signal object which stays attached to QObject class based on a list of QMetaMethods + * + * @param source of the Signal to be registered on meta object + * @param methods a list of QMetaMethod wich contains the supported signature + * @return Return a new reference to PyObject* of type PySideSignal + **/ +PYSIDE_API PySideSignalInstance* newObjectFromMethod(PyObject* source, const QList& methods); + +/** + * This function initializes the Signal object by creating a PySideSignalInstance + * + * @param self a Signal object used as base to PySideSignalInstance + * @param name the name to be used on PySideSignalInstance + * @param object the PyObject where the signal will be attached + * @return Return a new reference to PySideSignalInstance + **/ +PYSIDE_API PySideSignalInstance* initialize(PySideSignal* signal, PyObject* name, PyObject* object); + +/** + * This function is used to retrieve the object in which the signal is attached + * + * @param self The Signal object + * @return Return the internal reference to the parent object of the signal + **/ +PYSIDE_API PyObject* getObject(PySideSignalInstance* signal); + +/** + * This function is used to retrieve the signal signature + * + * @param self The Signal object + * @return Return the signal signature + **/ +PYSIDE_API const char* getSignature(PySideSignalInstance* signal); + +/** + * This function is used to retrieve the signal signature + * + * @param self The Signal object + * @return Return the signal signature + **/ +PYSIDE_API void updateSourceObject(PyObject* source); + +/** + * @deprecated Use registerSignals + **/ +PYSIDE_DEPRECATED(PYSIDE_API void addSignalToWrapper(SbkObjectType* wrapperType, const char* signalName, PySideSignal* signal)); + +/** + * This function verifies if the signature is a QtSignal base on SIGNAL flag + * @param signature The signal signature + * @return Return true if this is a Qt Signal, otherwise return false + **/ +PYSIDE_API bool isQtSignal(const char* signature); + +/** + * This function is similar to isQtSignal, however if it fails, it'll raise a Python error instead. + * + * @param signature The signal signature + * @return Return true if this is a Qt Signal, otherwise return false + **/ +PYSIDE_API bool checkQtSignal(const char* signature); + +/** + * This function is used to retrieve the signature base on Signal and receiver callback + * @param signature The signal signature + * @param receiver The QObject which will receive the signal + * @param callback Callback function which will connect to the signal + * @param encodeName Used to specify if the returned signature will be encoded with Qt signal/slot style + * @return Return the callback signature + **/ +PYSIDE_API QString getCallbackSignature(const char* signal, QObject* receiver, PyObject* callback, bool encodeName); + +/** + * This function parses the signature and then returns a list of argument types. + * + * @param signature The signal signature + * @param isShortCircuit If this is a shortCircuit(python<->python) signal + * @return Return true if this is a Qt Signal, otherwise return false + * @todo replace return type by QList + **/ +QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit = 0); + +} // namespace Signal +} // namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysidesignal_p.h b/sources/pyside2/libpyside/pysidesignal_p.h new file mode 100644 index 000000000..b692b96ad --- /dev/null +++ b/sources/pyside2/libpyside/pysidesignal_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_QSIGNAL_P_H +#define PYSIDE_QSIGNAL_P_H + +#include + +extern "C" +{ + extern PyTypeObject PySideSignalType; + + struct PySideSignal { + PyObject_HEAD + bool initialized; + char* signalName; + char** signatures; + int* signatureAttributes; + int signaturesSize; + PyObject* homonymousMethod; + }; + + struct PySideSignalInstance; + struct PySideSignalInstancePrivate { + char* signalName; + char* signature; + int attributes; + PyObject* source; + PyObject* homonymousMethod; + PySideSignalInstance* next; + }; + + +}; //extern "C" + +namespace PySide { namespace Signal { + + void init(PyObject* module); + bool connect(PyObject* source, const char* signal, PyObject* callback); + char* getTypeName(PyObject*); + const char** getSignatures(PyObject* self, int *size); + QString codeCallbackName(PyObject* callback, const QString& funcName); + +}} //namespace PySide + +#endif diff --git a/sources/pyside2/libpyside/pysideslot.cpp b/sources/pyside2/libpyside/pysideslot.cpp new file mode 100644 index 000000000..c562e41e2 --- /dev/null +++ b/sources/pyside2/libpyside/pysideslot.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "dynamicqmetaobject_p.h" +#include "pysidesignal_p.h" +#include "pysideslot_p.h" + +#include +#include +#include + +#define SLOT_DEC_NAME "Slot" + +typedef struct +{ + PyObject_HEAD + char* slotName; + char* args; + char* resultType; +} PySideSlot; + +extern "C" +{ + +static int slotTpInit(PyObject*, PyObject*, PyObject*); +static PyObject* slotCall(PyObject*, PyObject*, PyObject*); + +// Class Definition ----------------------------------------------- +static PyTypeObject PySideSlotType = { + PyVarObject_HEAD_INIT(0, 0) + "PySide2.QtCore." SLOT_DEC_NAME, /*tp_name*/ + sizeof(PySideSlot), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + slotCall, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + SLOT_DEC_NAME, /*tp_doc */ + 0, /*tp_traverse */ + 0, /*tp_clear */ + 0, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + 0, /*tp_iter */ + 0, /*tp_iternext */ + 0, /*tp_methods */ + 0, /*tp_members */ + 0, /*tp_getset */ + 0, /*tp_base */ + 0, /*tp_dict */ + 0, /*tp_descr_get */ + 0, /*tp_descr_set */ + 0, /*tp_dictoffset */ + slotTpInit, /*tp_init */ + 0, /*tp_alloc */ + PyType_GenericNew, /*tp_new */ + 0, /*tp_free */ + 0, /*tp_is_gc */ + 0, /*tp_bases */ + 0, /*tp_mro */ + 0, /*tp_cache */ + 0, /*tp_subclasses */ + 0, /*tp_weaklist */ + 0, /*tp_del */ + 0 /*tp_version_tag*/ +}; + +int slotTpInit(PyObject *self, PyObject *args, PyObject *kw) +{ + static PyObject *emptyTuple = 0; + static const char *kwlist[] = {"name", "result", 0}; + char* argName = 0; + PyObject* argResult = 0; + + if (emptyTuple == 0) + emptyTuple = PyTuple_New(0); + + if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore." SLOT_DEC_NAME, (char**) kwlist, &argName, &argResult)) + return 0; + + PySideSlot *data = reinterpret_cast(self); + for(Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) { + PyObject *argType = PyTuple_GET_ITEM(args, i); + char *typeName = PySide::Signal::getTypeName(argType); + if (typeName) { + if (data->args) { + data->args = reinterpret_cast(realloc(data->args, (strlen(data->args) + 1 + strlen(typeName)) * sizeof(char*))); + data->args = strcat(data->args, ","); + data->args = strcat(data->args, typeName); + free(typeName); + } else { + data->args = typeName; + } + } else { + PyErr_Format(PyExc_TypeError, "Unknown signal argument type: %s", argType->ob_type->tp_name); + return -1; + } + } + + if (argName) + data->slotName = strdup(argName); + + if (argResult) + data->resultType = PySide::Signal::getTypeName(argResult); + else + data->resultType = strdup("void"); + + return 1; +} + +PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + static PyObject* pySlotName = 0; + PyObject* callback; + callback = PyTuple_GetItem(args, 0); + Py_INCREF(callback); + + if (PyFunction_Check(callback)) { + PySideSlot *data = reinterpret_cast(self); + + if (!data->slotName) { + PyObject *funcName = reinterpret_cast(callback)->func_name; + data->slotName = strdup(Shiboken::String::toCString(funcName)); + } + + + QByteArray returnType = QMetaObject::normalizedType(data->resultType); + QByteArray signature = QString().sprintf("%s(%s)", data->slotName, data->args).toUtf8(); + signature = returnType + " " + signature; + + if (!pySlotName) + pySlotName = Shiboken::String::fromCString(PYSIDE_SLOT_LIST_ATTR); + + PyObject *pySignature = Shiboken::String::fromCString(signature); + PyObject *signatureList = 0; + if (PyObject_HasAttr(callback, pySlotName)) { + signatureList = PyObject_GetAttr(callback, pySlotName); + } else { + signatureList = PyList_New(0); + PyObject_SetAttr(callback, pySlotName, signatureList); + Py_DECREF(signatureList); + } + + PyList_Append(signatureList, pySignature); + Py_DECREF(pySignature); + + //clear data + free(data->slotName); + data->slotName = 0; + free(data->resultType); + data->resultType = 0; + free(data->args); + data->args = 0; + return callback; + } + return callback; +} + +} // extern "C" + +namespace PySide { namespace Slot { + +void init(PyObject* module) +{ + if (PyType_Ready(&PySideSlotType) < 0) + return; + + Py_INCREF(&PySideSlotType); + PyModule_AddObject(module, SLOT_DEC_NAME, reinterpret_cast(&PySideSlotType)); +} + +} // namespace Slot +} // namespace PySide diff --git a/sources/pyside2/libpyside/pysideslot_p.h b/sources/pyside2/libpyside/pysideslot_p.h new file mode 100644 index 000000000..33e881c18 --- /dev/null +++ b/sources/pyside2/libpyside/pysideslot_p.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 PYSIDE_SLOT_P_H +#define PYSIDE_SLOT_P_H + +#include +#define PYSIDE_SLOT_LIST_ATTR "_slots" + +namespace PySide { namespace Slot { + void init(PyObject* module); +}} + +#endif diff --git a/sources/pyside2/libpyside/pysideweakref.cpp b/sources/pyside2/libpyside/pysideweakref.cpp new file mode 100644 index 000000000..dfbf9e90c --- /dev/null +++ b/sources/pyside2/libpyside/pysideweakref.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "pysideweakref.h" + +#include + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + PySideWeakRefFunction weakref_func; + void* user_data; +} PySideCallableObject; + +static PyObject* CallableObject_call(PyObject* callable_object, PyObject* args, PyObject* kw); + +static PyTypeObject PySideCallableObjectType = { + PyVarObject_HEAD_INIT(0, 0) + const_cast("PySide.Callable"), + sizeof(PySideCallableObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + CallableObject_call, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0 /* tp_version_tag */ +}; + +static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject * /* kw */) +{ + PySideCallableObject* obj = reinterpret_cast(callable_object); + obj->weakref_func(obj->user_data); + + Py_XDECREF(PyTuple_GET_ITEM(args, 0)); //kill weak ref object + Py_RETURN_NONE; +} + +namespace PySide { namespace WeakRef { + +PyObject* create(PyObject* obj, PySideWeakRefFunction func, void* userData) +{ + if (obj == Py_None) + return 0; + + if (Py_TYPE(&PySideCallableObjectType) == 0) + { + Py_TYPE(&PySideCallableObjectType) = &PyType_Type; + PyType_Ready(&PySideCallableObjectType); + } + + PySideCallableObject* callable = PyObject_New(PySideCallableObject, &PySideCallableObjectType); + if (!callable || PyErr_Occurred()) + return 0; + + PyObject* weak = PyWeakref_NewRef(obj, reinterpret_cast(callable)); + if (!weak || PyErr_Occurred()) + return 0; + + callable->weakref_func = func; + callable->user_data = userData; + Py_DECREF(callable); // PYSIDE-79: after decref the callable is undefined (theoretically) + + return reinterpret_cast(weak); +} + +} } //namespace + diff --git a/sources/pyside2/libpyside/pysideweakref.h b/sources/pyside2/libpyside/pysideweakref.h new file mode 100644 index 000000000..7a7082345 --- /dev/null +++ b/sources/pyside2/libpyside/pysideweakref.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 __PYSIDEWEAKREF__ +#define __PYSIDEWEAKREF__ + +#include +#include + +typedef void (*PySideWeakRefFunction)(void* userData); + +namespace PySide { namespace WeakRef { + +PYSIDE_API PyObject* create(PyObject* ob, PySideWeakRefFunction func, void* userData); + +} //PySide +} //WeakRef + + +#endif diff --git a/sources/pyside2/libpyside/signalmanager.cpp.in b/sources/pyside2/libpyside/signalmanager.cpp.in new file mode 100644 index 000000000..473057cbc --- /dev/null +++ b/sources/pyside2/libpyside/signalmanager.cpp.in @@ -0,0 +1,726 @@ +// -*- mode: cpp;-*- +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "signalmanager.h" +#include "pysidesignal.h" +#include "pysideproperty.h" +#include "pysideproperty_p.h" +#include "pyside.h" +#include "dynamicqmetaobject.h" +#include "pysidemetafunction_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// These private headers are needed to throw JavaScript exceptions +#if @QML_PRIVATE_API_SUPPORT@ + #include + #include + #include +#if QT_VERSION < 0x050700 + #include +#endif +#endif + +#if QSLOT_CODE != 1 || QSIGNAL_CODE != 2 +#error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value! +#endif +#define PYSIDE_SLOT '1' +#define PYSIDE_SIGNAL '2' +#include "globalreceiverv2.h" +#include "globalreceiver.h" + +#define PYTHON_TYPE "PyObject" + +namespace { + static PyObject *metaObjectAttr = 0; + + static int callMethod(QObject* object, int id, void** args); + static PyObject* parseArguments(const QList< QByteArray >& paramTypes, void** args); + static bool emitShortCircuitSignal(QObject* source, int signalIndex, PyObject* args); + +#ifdef IS_PY3K + static void destroyMetaObject(PyObject* obj) + { + void* ptr = PyCapsule_GetPointer(obj, 0); + PySide::DynamicQMetaObject* meta = reinterpret_cast(ptr); + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); + if (wrapper) + Shiboken::BindingManager::instance().releaseWrapper(wrapper); + delete meta; + } + +#else + static void destroyMetaObject(void* obj) + { + PySide::DynamicQMetaObject* meta = reinterpret_cast(obj); + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); + if (wrapper) + Shiboken::BindingManager::instance().releaseWrapper(wrapper); + delete meta; + } +#endif +} + +namespace PySide { + + +PyObjectWrapper::PyObjectWrapper() + :m_me(Py_None) +{ + Py_INCREF(m_me); +} + +PyObjectWrapper::PyObjectWrapper(PyObject* me) + : m_me(me) +{ + Py_INCREF(m_me); +} + +PyObjectWrapper::PyObjectWrapper(const PyObjectWrapper &other) + : m_me(other.m_me) +{ + Py_INCREF(m_me); +} + +PyObjectWrapper::~PyObjectWrapper() +{ + // Check that Python is still initialized as sometimes this is called by a static destructor + // after Python interpeter is shutdown. + if (!Py_IsInitialized()) + return; + + Shiboken::GilState gil; + Py_DECREF(m_me); +} + +PyObjectWrapper& PyObjectWrapper::operator=(const PySide::PyObjectWrapper& other) +{ + Py_INCREF(other.m_me); + Py_DECREF(m_me); + m_me = other.m_me; + return *this; +} + +PyObjectWrapper::operator PyObject*() const +{ + return m_me; +} + +QDataStream &operator<<(QDataStream& out, const PyObjectWrapper& myObj) +{ + if (Py_IsInitialized() == 0) { + qWarning() << "Stream operator for PyObject called without python interpreter."; + return out; + } + + static PyObject *reduce_func = 0; + + Shiboken::GilState gil; + if (!reduce_func) { + Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle")); + reduce_func = PyObject_GetAttrString(pickleModule, "dumps"); + } + Shiboken::AutoDecRef repr(PyObject_CallFunctionObjArgs(reduce_func, (PyObject*)myObj, NULL)); + if (repr.object()) { + const char* buff = 0; + Py_ssize_t size = 0; + if (PyBytes_Check(repr.object())) { + buff = PyBytes_AS_STRING(repr.object()); + size = PyBytes_GET_SIZE(repr.object()); + } else if (Shiboken::String::check(repr.object())) { + buff = Shiboken::String::toCString(repr.object()); + size = Shiboken::String::len(repr.object()); + } + QByteArray data(buff, size); + out << data; + } + return out; +} + +QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj) +{ + if (Py_IsInitialized() == 0) { + qWarning() << "Stream operator for PyObject called without python interpreter."; + return in; + } + + static PyObject *eval_func = 0; + + Shiboken::GilState gil; + if (!eval_func) { + Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle")); + eval_func = PyObject_GetAttrString(pickleModule, "loads"); + } + + QByteArray repr; + in >> repr; + Shiboken::AutoDecRef pyCode(PyBytes_FromStringAndSize(repr.data(), repr.size())); + Shiboken::AutoDecRef value(PyObject_CallFunctionObjArgs(eval_func, pyCode.object(), 0)); + if (!value.object()) { + value = Py_None; + } + myObj = PyObjectWrapper(value); + return in; +} + +}; + +namespace Shiboken { + +template<> +struct Converter +{ + static PySide::PyObjectWrapper toCpp(PyObject* obj) + { + return PySide::PyObjectWrapper(obj); + } + + static PyObject* toPython(void* obj) + { + return toPython(*reinterpret_cast(obj)); + } + + static PyObject* toPython(const PySide::PyObjectWrapper& obj) + { + Py_INCREF((PyObject*)obj); + return obj; + } +}; + +}; + +using namespace PySide; + +struct SignalManager::SignalManagerPrivate +{ + SharedMap m_globalReceivers; + + //Deprecated + GlobalReceiver m_globalReceiver; + + SignalManagerPrivate() + { + m_globalReceivers = SharedMap( new QMap() ); + } + + ~SignalManagerPrivate() + { + if (!m_globalReceivers.isNull()) { + // Delete receivers by always retrieving the current first element, because deleting a + // receiver can indirectly delete another one, and if we use qDeleteAll, that could + // cause either a double delete, or iterator invalidation, and thus undefined behavior. + while (!m_globalReceivers->isEmpty()) + delete *m_globalReceivers->cbegin(); + Q_ASSERT(m_globalReceivers->isEmpty()); + } + } +}; + +static void clearSignalManager() +{ + PySide::SignalManager::instance().clear(); +} + +static void PyObject_PythonToCpp_PyObject_PTR(PyObject* pyIn, void* cppOut) +{ + *((PyObject**)cppOut) = pyIn; +} +static PythonToCppFunc is_PyObject_PythonToCpp_PyObject_PTR_Convertible(PyObject* pyIn) +{ + return PyObject_PythonToCpp_PyObject_PTR; +} +static PyObject* PyObject_PTR_CppToPython_PyObject(const void* cppIn) +{ + PyObject* pyOut = (PyObject*)cppIn; + if (pyOut) + Py_INCREF(pyOut); + return pyOut; +} + +SignalManager::SignalManager() : m_d(new SignalManagerPrivate) +{ + // Register Qt primitive typedefs used on signals. + using namespace Shiboken; + + // Register PyObject type to use in queued signal and slot connections + qRegisterMetaType(PYTHON_TYPE); + qRegisterMetaTypeStreamOperators(PYTHON_TYPE); + qRegisterMetaTypeStreamOperators("PyObjectWrapper"); + qRegisterMetaTypeStreamOperators("PySide::PyObjectWrapper"); + + SbkConverter* converter = Shiboken::Conversions::createConverter(&PyBaseObject_Type, 0); + Shiboken::Conversions::setCppPointerToPythonFunction(converter, PyObject_PTR_CppToPython_PyObject); + Shiboken::Conversions::setPythonToCppPointerFunctions(converter, PyObject_PythonToCpp_PyObject_PTR, is_PyObject_PythonToCpp_PyObject_PTR_Convertible); + Shiboken::Conversions::registerConverterName(converter, PYTHON_TYPE); + Shiboken::Conversions::registerConverterName(converter, "object"); + Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper"); + Shiboken::Conversions::registerConverterName(converter, "PySide::PyObjectWrapper"); + + PySide::registerCleanupFunction(clearSignalManager); + + if (!metaObjectAttr) + metaObjectAttr = Shiboken::String::fromCString("__METAOBJECT__"); +} + +void SignalManager::clear() +{ + delete m_d; + m_d = new SignalManagerPrivate(); +} + +SignalManager::~SignalManager() +{ + delete m_d; +} + +SignalManager& SignalManager::instance() +{ + static SignalManager me; + return me; +} + +QObject* SignalManager::globalReceiver() +{ + return &m_d->m_globalReceiver; +} + +void SignalManager::globalReceiverConnectNotify(QObject* source, int slotIndex) +{ + m_d->m_globalReceiver.connectNotify(source, slotIndex); +} + +void SignalManager::globalReceiverDisconnectNotify(QObject* source, int slotIndex) +{ + m_d->m_globalReceiver.disconnectNotify(source, slotIndex); +} + +void SignalManager::addGlobalSlot(const char* slot, PyObject* callback) +{ + addGlobalSlotGetIndex(slot, callback); +} + +int SignalManager::addGlobalSlotGetIndex(const char* slot, PyObject* callback) +{ + return m_d->m_globalReceiver.addSlot(slot, callback); +} + +QObject* SignalManager::globalReceiver(QObject *sender, PyObject *callback) +{ + SharedMap globalReceivers = m_d->m_globalReceivers; + QByteArray hash = GlobalReceiverV2::hash(callback); + GlobalReceiverV2* gr = 0; + if (!globalReceivers->contains(hash)) { + gr = (*globalReceivers)[hash] = new GlobalReceiverV2(callback, globalReceivers); + if (sender) { + gr->incRef(sender); // create a link reference + gr->decRef(); // remove extra reference + } + } else { + gr = (*globalReceivers)[hash]; + if (sender) + gr->incRef(sender); + } + + return reinterpret_cast(gr); +} + +int SignalManager::countConnectionsWith(const QObject *object) +{ + int count = 0; + for (GlobalReceiverV2Map::const_iterator it = m_d->m_globalReceivers->cbegin(), end = m_d->m_globalReceivers->cend(); it != end; ++it) { + if (it.value()->refCount(object)) + count++; + } + return count; +} + +void SignalManager::notifyGlobalReceiver(QObject* receiver) +{ + reinterpret_cast(receiver)->notify(); +} + +void SignalManager::releaseGlobalReceiver(const QObject* source, QObject* receiver) +{ + GlobalReceiverV2* gr = reinterpret_cast(receiver); + gr->decRef(source); +} + +int SignalManager::globalReceiverSlotIndex(QObject* receiver, const char* signature) const +{ + return reinterpret_cast(receiver)->addSlot(signature); +} + +bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* args) +{ + if (!Signal::checkQtSignal(signal)) + return false; + signal++; + + int signalIndex = source->metaObject()->indexOfSignal(signal); + if (signalIndex != -1) { + // cryptic but works! + // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find + // returned the string null terminator. + bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '('); + if (isShortCircuit) + return emitShortCircuitSignal(source, signalIndex, args); + else + return MetaFunction::call(source, signalIndex, args); + } + return false; +} + +int SignalManager::qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args) +{ + const QMetaObject* metaObject = object->metaObject(); + PySideProperty* pp = 0; + PyObject* pp_name = 0; + QMetaProperty mp; + PyObject* pySelf = 0; + int methodCount = metaObject->methodCount(); + int propertyCount = metaObject->propertyCount(); + + if (call != QMetaObject::InvokeMetaMethod) { + mp = metaObject->property(id); + if (!mp.isValid()) { + return id - methodCount; + } + + Shiboken::GilState gil; + pySelf = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(object); + Q_ASSERT(pySelf); + pp_name = Shiboken::String::fromCString(mp.name()); + pp = Property::getObject(pySelf, pp_name); + if (!pp) { + qWarning("Invalid property: %s.", mp.name()); + Py_XDECREF(pp_name); + return id - methodCount; + } + } + + switch(call) { +#ifndef QT_NO_PROPERTIES + case QMetaObject::ReadProperty: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyUser: + pp->d->metaCallHandler(pp, pySelf, call, args); + break; +#endif + case QMetaObject::InvokeMetaMethod: + id = callMethod(object, id, args); + break; + + default: + qWarning("Unsupported meta invocation type."); + } + + // WARNING Isn't safe to call any metaObject and/or object methods beyond this point + // because the object can be deleted inside the called slot. + + if (call == QMetaObject::InvokeMetaMethod) { + id = id - methodCount; + } else { + id = id - propertyCount; + } + + if (pp || pp_name) { + Shiboken::GilState gil; + Py_XDECREF(pp); + Py_XDECREF(pp_name); + } + + // Bubbles Python exceptions up to the Javascript engine, if called from one + { + Shiboken::GilState gil; + + if (PyErr_Occurred()) { + +#if @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()) { + QV4::ExecutionEngine *engine = data->jsWrapper.engine(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + QV4::Heap::ExecutionContext *ctx = engine->current; +#elif QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + QV4::Heap::ExecutionContext *ctx = engine->currentContext(); +#else + QV4::ExecutionContext *ctx = engine->currentContext(); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + if (ctx->type == QV4::Heap::ExecutionContext::Type_CallContext || + ctx->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { +#elif QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) + if (ctx->d()->type == QV4::ExecutionContext::Type_CallContext || + ctx->d()->type == QV4::ExecutionContext::Type_SimpleCallContext) { +#else + if (ctx->type == QV4::ExecutionContext::Type_CallContext || + ctx->type == QV4::ExecutionContext::Type_SimpleCallContext) { +#endif + 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 QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + if (isSyntaxError) { + return engine->throwSyntaxError(errString); + } else if (isTypeError) { + return engine->throwTypeError(errString); + } else { + return engine->throwError(errString); + } +#else + if (isSyntaxError) { + return ctx->throwSyntaxError(errString); + } else if (isTypeError) { + return ctx->throwTypeError(errString); + } else { + return ctx->throwError(errString); + } +#endif + } + } +#endif + + int reclimit = Py_GetRecursionLimit(); + // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function. + // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion + // error again. Don't do it when the limit is already insanely high, to avoid overflow. + if (reclimit < (1 << 30)) + Py_SetRecursionLimit(reclimit + 5); + PyErr_Print(); + Py_SetRecursionLimit(reclimit); + } + } + + return id; +} + +int SignalManager::callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* pyMethod, bool isShortCuit) +{ + Q_ASSERT(pyMethod); + + Shiboken::GilState gil; + PyObject* pyArguments = 0; + + if (isShortCuit){ + pyArguments = reinterpret_cast(args[1]); + } else { + pyArguments = parseArguments(method.parameterTypes(), args); + } + + if (pyArguments) { + Shiboken::Conversions::SpecificConverter* retConverter = NULL; + const char* returnType = method.typeName(); + if (returnType && std::strcmp("", returnType) && std::strcmp("void", returnType)) { + retConverter = new Shiboken::Conversions::SpecificConverter(returnType); + if (!retConverter || !*retConverter) { + PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s' to call Python meta method.", returnType); + return -1; + } + } + + Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments)); + + if (!isShortCuit && pyArguments){ + Py_DECREF(pyArguments); + } + + if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter) { + retConverter->toCpp(retval, args[0]); + } + delete retConverter; + } + + return -1; +} + +bool SignalManager::registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type) +{ + int ret = registerMetaMethodGetIndex(source, signature, type); + return (ret != -1); +} + +int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type) +{ + Q_ASSERT(source); + const QMetaObject* metaObject = source->metaObject(); + int methodIndex = metaObject->indexOfMethod(signature); + // Create the dynamic signal is needed + if (methodIndex == -1) { + SbkObject* self = Shiboken::BindingManager::instance().retrieveWrapper(source); + if (!Shiboken::Object::hasCppWrapper(self)) { + qWarning() << "Invalid Signal signature:" << signature; + return -1; + } else { + DynamicQMetaObject *dmo = 0; + PyObject *pySelf = reinterpret_cast(self); + PyObject* dict = self->ob_dict; + + // Create a instance meta object + if (!dict || !PyDict_Contains(dict, metaObjectAttr)) { + dmo = new DynamicQMetaObject(pySelf->ob_type, metaObject); +#ifdef IS_PY3K + PyObject* pyDmo = PyCapsule_New(dmo, 0, destroyMetaObject); +#else + PyObject* pyDmo = PyCObject_FromVoidPtr(dmo, destroyMetaObject); +#endif + + PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo); + Py_DECREF(pyDmo); + } else { + dmo = reinterpret_cast(const_cast(metaObject)); + } + + if (type == QMetaMethod::Signal) + return dmo->addSignal(signature); + else + return dmo->addSlot(signature); + } + } + return methodIndex; +} + +bool SignalManager::hasConnectionWith(const QObject *object) +{ + return m_d->m_globalReceiver.hasConnectionWith(object); +} + +const QMetaObject* SignalManager::retriveMetaObject(PyObject *self) +{ + Shiboken::GilState gil; + DynamicQMetaObject *mo = 0; + Q_ASSERT(self); + + PyObject* dict = reinterpret_cast(self)->ob_dict; + if (dict && PyDict_Contains(dict, metaObjectAttr)) { + PyObject *pyMo = PyDict_GetItem(dict, metaObjectAttr); + +#ifdef IS_PY3K + mo = reinterpret_cast(PyCapsule_GetPointer(pyMo, 0)); +#else + mo = reinterpret_cast(PyCObject_AsVoidPtr(pyMo)); +#endif + } else { + mo = reinterpret_cast(Shiboken::Object::getTypeUserData(reinterpret_cast(self))); + } + + mo->update(); + return mo; +} + +namespace { + +static int callMethod(QObject* object, int id, void** args) +{ + const QMetaObject* metaObject = object->metaObject(); + QMetaMethod method = metaObject->method(id); + + if (method.methodType() == QMetaMethod::Signal) { + // emit python signal + QMetaObject::activate(object, id, args); + } else { + Shiboken::GilState gil; + PyObject* self = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(object); + QByteArray methodName = method.methodSignature(); + methodName.truncate(methodName.indexOf('(')); + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(self, methodName)); + return SignalManager::callPythonMetaMethod(method, args, pyMethod, false); + } + return -1; +} + + +static PyObject* parseArguments(const QList& paramTypes, void** args) +{ + int argsSize = paramTypes.count(); + PyObject* preparedArgs = PyTuple_New(argsSize); + + for (int i = 0, max = argsSize; i < max; ++i) { + void* data = args[i+1]; + const char* dataType = paramTypes[i].constData(); + Shiboken::Conversions::SpecificConverter converter(dataType); + if (converter) { + PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data)); + } else { + PyErr_Format(PyExc_TypeError, "Can't call meta function because I have no idea how to handle %s", dataType); + Py_DECREF(preparedArgs); + return 0; + } + } + return preparedArgs; +} + +static bool emitShortCircuitSignal(QObject* source, int signalIndex, PyObject* args) +{ + void* signalArgs[2] = {0, args}; + source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs); + return true; +} + +} //namespace diff --git a/sources/pyside2/libpyside/signalmanager.h b/sources/pyside2/libpyside/signalmanager.h new file mode 100644 index 000000000..6f2201ed0 --- /dev/null +++ b/sources/pyside2/libpyside/signalmanager.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 SIGNALMANAGER_H +#define SIGNALMANAGER_H + +#include "pysidemacros.h" +#include +#include +#include +#include + +namespace PySide +{ + +/// Thin wrapper for PyObject which increases the reference count at the constructor but *NOT* at destructor. +class PYSIDE_API PyObjectWrapper +{ +public: + PyObjectWrapper(); + PyObjectWrapper(PyObject* me); + PyObjectWrapper(const PyObjectWrapper &other); + ~PyObjectWrapper(); + operator PyObject*() const; + PyObjectWrapper& operator=(const PyObjectWrapper &other); +private: + PyObject* m_me; + void* m_data; //future +}; + +PYSIDE_API QDataStream &operator<<(QDataStream& out, const PyObjectWrapper& myObj); +PYSIDE_API QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj); + +class PYSIDE_API SignalManager +{ +public: + static SignalManager& instance(); + + QObject* globalReceiver(QObject* sender, PyObject* callback); + void releaseGlobalReceiver(const QObject* sender, QObject* receiver); + int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const; + void notifyGlobalReceiver(QObject* receiver); + + bool emitSignal(QObject* source, const char* signal, PyObject* args); + static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args); + + // Used to register a new signal/slot on QMetaobject of source. + static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type); + static int registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type); + + // used to discovery metaobject + static const QMetaObject* retriveMetaObject(PyObject* self); + + // Used to discovery if SignalManager was connected with object "destroyed()" signal. + int countConnectionsWith(const QObject *object); + + // Disconnect all signals managed by Globalreceiver + void clear(); + + // Utility function to call a python method usign args received in qt_metacall + static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit); + + PYSIDE_DEPRECATED(QObject* globalReceiver()); + PYSIDE_DEPRECATED(void addGlobalSlot(const char* slot, PyObject* callback)); + PYSIDE_DEPRECATED(int addGlobalSlotGetIndex(const char* slot, PyObject* callback)); + + PYSIDE_DEPRECATED(void globalReceiverConnectNotify(QObject *sender, int slotIndex)); + PYSIDE_DEPRECATED(void globalReceiverDisconnectNotify(QObject *sender, int slotIndex)); + PYSIDE_DEPRECATED(bool hasConnectionWith(const QObject *object)); + +private: + struct SignalManagerPrivate; + SignalManagerPrivate* m_d; + + SignalManager(); + ~SignalManager(); + + // disable copy + SignalManager(const SignalManager&); + SignalManager operator=(const SignalManager&); +}; + +} + +Q_DECLARE_METATYPE(PySide::PyObjectWrapper) + +#endif -- cgit v1.2.3