aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-06-20 10:22:51 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-06-20 10:23:16 +0200
commit54e7da4895cd5d9228cef3e4e3ebc61c5109ed12 (patch)
tree46208372751b0f9f99de50babe5a5e635d8d6985
parent44cb6278265ddb2d970d59469a5676d18e547871 (diff)
parent479a6bfbf7fe82a387d313a8bed95f026d9d7922 (diff)
Merge remote-tracking branch 'origin/5.15' into dev
-rw-r--r--build_scripts/platforms/windows_desktop.py7
-rw-r--r--examples/widgetbinding/CMakeLists.txt275
-rw-r--r--examples/widgetbinding/README.md74
-rw-r--r--examples/widgetbinding/bindings.h54
-rw-r--r--examples/widgetbinding/bindings.xml56
-rw-r--r--examples/widgetbinding/dialog.py77
-rw-r--r--examples/widgetbinding/macros.h63
-rw-r--r--examples/widgetbinding/main.py61
-rw-r--r--examples/widgetbinding/wigglywidget.cpp111
-rw-r--r--examples/widgetbinding/wigglywidget.h81
-rw-r--r--examples/widgetbinding/wigglywidget.py97
-rw-r--r--sources/pyside2/PySide2/QtCore/typesystem_core_common.xml10
-rw-r--r--sources/pyside2/PySide2/glue/qtcore.cpp8
-rw-r--r--sources/pyside2/PySide2/glue/qtuitools.cpp11
-rw-r--r--sources/pyside2/doc/extras/QtCore.QEnum.rst92
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/style.qss23
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling-no.pngbin0 -> 26444 bytes
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-no.pngbin0 -> 3834 bytes
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-yes.pngbin0 -> 4743 bytes
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling-yes.pngbin0 -> 32311 bytes
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling.py95
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgetstyling.rst169
-rw-r--r--sources/pyside2/doc/tutorials/index.rst1
-rw-r--r--sources/pyside2/libpyside/CMakeLists.txt2
-rw-r--r--sources/pyside2/libpyside/dynamicqmetaobject.cpp66
-rw-r--r--sources/pyside2/libpyside/dynamicqmetaobject.h5
-rw-r--r--sources/pyside2/libpyside/pyside.cpp3
-rw-r--r--sources/pyside2/libpyside/pysideqenum.cpp258
-rw-r--r--sources/pyside2/libpyside/pysideqenum.h57
-rw-r--r--sources/pyside2/libpyside/pysidesignal.cpp1
-rw-r--r--sources/pyside2/tests/QtCore/qenum_test.py119
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp37
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp30
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.h1
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp2
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testnestedtypes.cpp4
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp84
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.h10
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_enums.h19
-rw-r--r--sources/shiboken2/ApiExtractor/typesystemparser.cpp25
-rw-r--r--sources/shiboken2/doc/typesystem_codeinjection.rst133
-rw-r--r--sources/shiboken2/generator/generator.cpp37
-rw-r--r--sources/shiboken2/generator/main.cpp2
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp251
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.h5
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.cpp6
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.cpp6
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.cpp16
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.h12
-rw-r--r--sources/shiboken2/libshiboken/sbkenum.cpp15
-rw-r--r--sources/shiboken2/libshiboken/sbkpython.h1
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.cpp5
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.h5
-rw-r--r--sources/shiboken2/tests/samplebinding/typesystem_sample.xml22
54 files changed, 2281 insertions, 323 deletions
diff --git a/build_scripts/platforms/windows_desktop.py b/build_scripts/platforms/windows_desktop.py
index 750a064b4..49c384bc7 100644
--- a/build_scripts/platforms/windows_desktop.py
+++ b/build_scripts/platforms/windows_desktop.py
@@ -251,7 +251,8 @@ def copy_msvc_redist_files(vars, redist_target_path):
"vcamp140.dll",
"vccorlib140.dll",
"vcomp140.dll",
- "vcruntime140.dll"
+ "vcruntime140.dll",
+ "vcruntime140_1.dll"
]
# Make a directory where the files should be extracted.
@@ -262,9 +263,9 @@ def copy_msvc_redist_files(vars, redist_target_path):
in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None)
if in_coin is not None:
redist_url = "http://download.qt.io/development_releases/prebuilt/vcredist/"
- zip_file = "pyside_qt_deps_64.7z"
+ zip_file = "pyside_qt_deps_64_2019.7z"
if "{target_arch}".format(**vars) == "32":
- zip_file = "pyside_qt_deps_32.7z"
+ zip_file = "pyside_qt_deps_32_2019.7z"
download_and_extract_7z(redist_url + zip_file, redist_target_path)
else:
print("Qt dependency DLLs (MSVC redist) will not be downloaded and extracted.")
diff --git a/examples/widgetbinding/CMakeLists.txt b/examples/widgetbinding/CMakeLists.txt
new file mode 100644
index 000000000..a557f90ec
--- /dev/null
+++ b/examples/widgetbinding/CMakeLists.txt
@@ -0,0 +1,275 @@
+cmake_minimum_required(VERSION 3.1)
+cmake_policy(VERSION 3.1)
+
+# Enable policy to not use RPATH settings for install_name on macOS.
+if(POLICY CMP0068)
+ cmake_policy(SET CMP0068 NEW)
+endif()
+
+# Enable policy to run automoc on generated files.
+if(POLICY CMP0071)
+ cmake_policy(SET CMP0071 NEW)
+endif()
+
+# Consider changing the project name to something relevant for you.
+project(wiggly LANGUAGES CXX)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+find_package(Qt5 5.12 REQUIRED COMPONENTS Core Gui Widgets)
+
+# ================================ General configuration ======================================
+
+# Set CPP standard to C++11 minimum.
+set(CMAKE_CXX_STANDARD 11)
+
+# The wiggly library for which we will create bindings. You can change the name to something
+# relevant for your project.
+set(wiggly_library "libwiggly")
+
+# The name of the generated bindings module (as imported in Python). You can change the name
+# to something relevant for your project.
+set(bindings_library "wiggly")
+
+# The header file with all the types and functions for which bindings will be generated.
+# Usually it simply includes other headers of the library you are creating bindings for.
+set(wrapped_header ${CMAKE_SOURCE_DIR}/bindings.h)
+
+# The typesystem xml file which defines the relationships between the C++ types / functions
+# and the corresponding Python equivalents.
+set(typesystem_file ${CMAKE_SOURCE_DIR}/bindings.xml)
+
+# Specify which C++ files will be generated by shiboken. This includes the module wrapper
+# and a '.cpp' file per C++ type. These are needed for generating the module shared
+# library.
+set(generated_sources
+ ${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/wiggly_module_wrapper.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/wigglywidget_wrapper.cpp)
+
+
+# ================================== Shiboken detection ======================================
+# Use provided python interpreter if given.
+if(NOT python_interpreter)
+ find_program(python_interpreter "python")
+endif()
+message(STATUS "Using python interpreter: ${python_interpreter}")
+
+# Macro to get various pyside / python include / link flags and paths.
+# Uses the not entirely supported utils/pyside2_config.py file.
+macro(pyside2_config option output_var)
+ if(${ARGC} GREATER 2)
+ set(is_list ${ARGV2})
+ else()
+ set(is_list "")
+ endif()
+
+ execute_process(
+ COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py"
+ ${option}
+ OUTPUT_VARIABLE ${output_var}
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if ("${${output_var}}" STREQUAL "")
+ message(FATAL_ERROR "Error: Calling pyside2_config.py ${option} returned no output.")
+ endif()
+ if(is_list)
+ string (REPLACE " " ";" ${output_var} "${${output_var}}")
+ endif()
+endmacro()
+
+# Query for the shiboken generator path, Python path, include paths and linker flags.
+pyside2_config(--shiboken2-module-path shiboken2_module_path)
+pyside2_config(--shiboken2-generator-path shiboken2_generator_path)
+pyside2_config(--pyside2-path pyside2_path)
+pyside2_config(--pyside2-include-path pyside2_include_dir 1)
+pyside2_config(--python-include-path python_include_dir)
+pyside2_config(--shiboken2-generator-include-path shiboken_include_dir 1)
+pyside2_config(--shiboken2-module-shared-libraries-cmake shiboken_shared_libraries 0)
+pyside2_config(--python-link-flags-cmake python_linking_data 0)
+pyside2_config(--pyside2-shared-libraries-cmake pyside2_shared_libraries 0)
+
+set(shiboken_path "${shiboken2_generator_path}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}")
+if(NOT EXISTS ${shiboken_path})
+ message(FATAL_ERROR "Shiboken executable not found at path: ${shiboken_path}")
+endif()
+
+
+# ==================================== RPATH configuration ====================================
+
+
+# =============================================================================================
+# !!! (The section below is deployment related, so in a real world application you will want to
+# take care of this properly with some custom script or tool).
+# =============================================================================================
+# Enable rpaths so that the built shared libraries find their dependencies.
+set(CMAKE_SKIP_BUILD_RPATH FALSE)
+set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+set(CMAKE_INSTALL_RPATH ${shiboken2_module_path} ${CMAKE_CURRENT_SOURCE_DIR})
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+# =============================================================================================
+# !!! End of dubious section.
+# =============================================================================================
+
+
+# =============================== CMake target - wiggly_library ===============================
+
+
+# Get all relevant Qt include dirs, to pass them on to shiboken.
+get_property(QT_CORE_INCLUDE_DIRS TARGET Qt5::Core PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+get_property(QT_GUI_INCLUDE_DIRS TARGET Qt5::Gui PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+get_property(QT_WIDGETS_INCLUDE_DIRS TARGET Qt5::Widgets PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+set(QT_INCLUDE_DIRS ${QT_CORE_INCLUDE_DIRS} ${QT_GUI_INCLUDE_DIRS} ${QT_WIDGETS_INCLUDE_DIRS})
+set(INCLUDES "")
+foreach(INCLUDE_DIR ${QT_INCLUDE_DIRS})
+ list(APPEND INCLUDES "-I${INCLUDE_DIR}")
+endforeach()
+
+# On macOS, check if Qt is a framework build. This affects how include paths should be handled.
+get_target_property(QtCore_is_framework Qt5::Core FRAMEWORK)
+if (QtCore_is_framework)
+ get_target_property(qt_core_library_location Qt5::Core LOCATION)
+ get_filename_component(qt_core_library_location_dir "${qt_core_library_location}" DIRECTORY)
+ get_filename_component(lib_dir "${qt_core_library_location_dir}/../" ABSOLUTE)
+ list(APPEND INCLUDES "--framework-include-paths=${lib_dir}")
+endif()
+
+# We need to include the headers for the module bindings that we use.
+set(pyside2_additional_includes "")
+foreach(INCLUDE_DIR ${pyside2_include_dir})
+ list(APPEND pyside2_additional_includes "${INCLUDE_DIR}/QtCore")
+ list(APPEND pyside2_additional_includes "${INCLUDE_DIR}/QtGui")
+ list(APPEND pyside2_additional_includes "${INCLUDE_DIR}/QtWidgets")
+endforeach()
+
+
+# Define the wiggly shared library for which we will create bindings.
+set(${wiggly_library}_sources wigglywidget.cpp)
+add_library(${wiggly_library} SHARED ${${wiggly_library}_sources})
+set_property(TARGET ${wiggly_library} PROPERTY PREFIX "")
+
+# Needed mostly on Windows to export symbols, and create a .lib file, otherwise the binding
+# library can't link to the wiggly library.
+target_compile_definitions(${wiggly_library} PRIVATE BINDINGS_BUILD)
+
+
+# ====================== Shiboken target for generating binding C++ files ====================
+
+
+# Set up the options to pass to shiboken.
+set(shiboken_options --generator-set=shiboken --enable-parent-ctor-heuristic
+ --enable-pyside-extensions --enable-return-value-heuristic --use-isnull-as-nb_nonzero
+ --avoid-protected-hack
+ ${INCLUDES}
+ -I${CMAKE_SOURCE_DIR}
+ -T${CMAKE_SOURCE_DIR}
+ -T${pyside2_path}/typesystems
+ --output-directory=${CMAKE_CURRENT_BINARY_DIR}
+ )
+
+set(generated_sources_dependencies ${wrapped_header} ${typesystem_file})
+
+# Add custom target to run shiboken to generate the binding cpp files.
+add_custom_command(OUTPUT ${generated_sources}
+ COMMAND ${shiboken_path}
+ ${shiboken_options} ${wrapped_header} ${typesystem_file}
+ DEPENDS ${generated_sources_dependencies}
+ #IMPLICIT_DEPENDS CXX ${wrapped_header}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Running generator for ${typesystem_file}.")
+
+
+# =============================== CMake target - bindings_library =============================
+
+
+# Set the cpp files which will be used for the bindings library.
+set(${bindings_library}_sources ${generated_sources})
+
+# Define and build the bindings library.
+add_library(${bindings_library} SHARED ${${bindings_library}_sources})
+
+
+# Apply relevant include and link flags.
+target_include_directories(${bindings_library} PRIVATE ${pyside2_additional_includes})
+target_include_directories(${bindings_library} PRIVATE ${pyside2_include_dir})
+target_include_directories(${bindings_library} PRIVATE ${python_include_dir})
+target_include_directories(${bindings_library} PRIVATE ${shiboken_include_dir})
+
+target_link_libraries(${wiggly_library} PRIVATE Qt5::Widgets)
+target_link_libraries(${bindings_library} PRIVATE Qt5::Widgets)
+target_link_libraries(${bindings_library} PRIVATE ${wiggly_library})
+target_link_libraries(${bindings_library} PRIVATE ${pyside2_shared_libraries})
+target_link_libraries(${bindings_library} PRIVATE ${shiboken_shared_libraries})
+
+# Adjust the name of generated module.
+set_property(TARGET ${bindings_library} PROPERTY PREFIX "")
+set_property(TARGET ${bindings_library} PROPERTY OUTPUT_NAME
+ "${bindings_library}${PYTHON_EXTENSION_SUFFIX}")
+if(WIN32)
+ set_property(TARGET ${bindings_library} PROPERTY SUFFIX ".pyd")
+endif()
+
+# Make sure the linker doesn't complain about not finding Python symbols on macOS.
+if(APPLE)
+ set_target_properties(${bindings_library} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
+endif(APPLE)
+
+# Find and link to the python import library only on Windows.
+# On Linux and macOS, the undefined symbols will get resolved by the dynamic linker
+# (the symbols will be picked up in the Python executable).
+if (WIN32)
+ list(GET python_linking_data 0 python_libdir)
+ list(GET python_linking_data 1 python_lib)
+ find_library(python_link_flags ${python_lib} PATHS ${python_libdir} HINTS ${python_libdir})
+ target_link_libraries(${bindings_library} PRIVATE ${python_link_flags})
+endif()
+
+# ================================= Dubious deployment section ================================
+
+set(windows_shiboken_shared_libraries)
+
+if(WIN32)
+ # =========================================================================================
+ # !!! (The section below is deployment related, so in a real world application you will
+ # want to take care of this properly (this is simply to eliminate errors that users usually
+ # encounter.
+ # =========================================================================================
+ # Circumvent some "#pragma comment(lib)"s in "include/pyconfig.h" which might force to link
+ # against a wrong python shared library.
+
+ set(python_versions_list 3 32 33 34 35 36 37 38)
+ set(python_additional_link_flags "")
+ foreach(ver ${python_versions_list})
+ set(python_additional_link_flags
+ "${python_additional_link_flags} /NODEFAULTLIB:\"python${ver}_d.lib\"")
+ set(python_additional_link_flags
+ "${python_additional_link_flags} /NODEFAULTLIB:\"python${ver}.lib\"")
+ endforeach()
+
+ set_target_properties(${bindings_library}
+ PROPERTIES LINK_FLAGS "${python_additional_link_flags}")
+
+ # Compile a list of shiboken shared libraries to be installed, so that
+ # the user doesn't have to set the PATH manually to point to the PySide2 package.
+ foreach(library_path ${shiboken_shared_libraries})
+ string(REGEX REPLACE ".lib$" ".dll" library_path ${library_path})
+ file(TO_CMAKE_PATH ${library_path} library_path)
+ list(APPEND windows_shiboken_shared_libraries "${library_path}")
+ endforeach()
+ # =========================================================================================
+ # !!! End of dubious section.
+ # =========================================================================================
+endif()
+
+# =============================================================================================
+# !!! (The section below is deployment related, so in a real world application you will want to
+# take care of this properly with some custom script or tool).
+# =============================================================================================
+# Install the library and the bindings module into the source folder near the main.py file, so
+# that the Python interpeter successfully imports the used module.
+install(TARGETS ${bindings_library} ${wiggly_library}
+ LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}
+ RUNTIME DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+install(FILES ${windows_shiboken_shared_libraries} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
+# =============================================================================================
+# !!! End of dubious section.
+# =============================================================================================
diff --git a/examples/widgetbinding/README.md b/examples/widgetbinding/README.md
new file mode 100644
index 000000000..f58a49627
--- /dev/null
+++ b/examples/widgetbinding/README.md
@@ -0,0 +1,74 @@
+# WigglyWidget
+
+The original Qt/C++ example can be found here:
+https://doc.qt.io/qt-5/qtwidgets-widgets-wiggly-example.html
+
+This example shows how to interact with a custom widget from two
+different ways:
+
+ * A full Python translation from a C++ example,
+ * A Python binding generated from the C++ file.
+
+
+The original example contained three different files:
+ * `main.cpp/h`, which was translated to `main.py`,
+ * `dialog.cpp/h`, which was translated to `dialog.py`,
+ * `wigglywidget.cpp/h`, which was translated to `wigglywidget.py`,
+ but also remains as is, to enable the binding generation through
+ Shiboken.
+
+In the `dialog.py` file you will find two imports that will be related
+to each of the two approaches described before::
+
+
+ # Python translated file
+ from wigglywidget import WigglyWidget
+
+ # Binding module create with Shiboken
+ from wiggly import WigglyWidget
+
+
+## Steps to build the bindings
+
+The most important files are:
+ * `bindings.xml`, to specify the class that we want to expose from C++
+ to Python,
+ * `bindings.h` to include the header of the classes we want to expose
+ * `CMakeList.txt`, with all the instructions to build the shared libraries
+ (DLL, or dylib)
+ * `pyside2_config.py` which is located in the utils directory, one level
+ up, to get the path for Shiboken and PySide.
+
+Now create a `build/` directory, and from inside run `cmake ..` to use
+the provided `CMakeLists.txt`.
+To build, just run `make`, and `make install` to copy the generated files
+to the main example directory to be able to run the final example:
+`python main.py`.
+You should be able to see two identical custom widgets, one being the
+Python translation, and the other one being the C++ one.
+
+### Windows
+
+For windows it's recommended to use either `nmake`, `jom` or `ninja`,
+when running cmake.
+
+```bash
+cmake -H.. -B. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release # for nmake
+cmake -H.. -B. -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release # for jom
+cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release # for ninja
+```
+
+### Linux, macOS
+
+Generally using `make` will be enough, but as in the Windows case, you can use
+ninja to build the project.
+
+```bash
+cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
+```
+
+## Final words
+
+Since this example originated by mixing the concepts of the `scriptableapplication`
+and `samplebinding` examples, you can complement this README with the ones in
+those directories.
diff --git a/examples/widgetbinding/bindings.h b/examples/widgetbinding/bindings.h
new file mode 100644
index 000000000..d59222656
--- /dev/null
+++ b/examples/widgetbinding/bindings.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BINDINGS_H
+#define BINDINGS_H
+#include "wigglywidget.h"
+#endif // BINDINGS_H
diff --git a/examples/widgetbinding/bindings.xml b/examples/widgetbinding/bindings.xml
new file mode 100644
index 000000000..07f1c89c9
--- /dev/null
+++ b/examples/widgetbinding/bindings.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+-->
+<typesystem package="wiggly">
+ <load-typesystem name="typesystem_widgets.xml" generate="no"/>
+ <object-type name="WigglyWidget"/>
+</typesystem>
diff --git a/examples/widgetbinding/dialog.py b/examples/widgetbinding/dialog.py
new file mode 100644
index 000000000..e52155999
--- /dev/null
+++ b/examples/widgetbinding/dialog.py
@@ -0,0 +1,77 @@
+############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## 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.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+############################################################################
+
+from PySide2.QtWidgets import QDialog, QLineEdit, QVBoxLayout
+
+# Python binding from the C++ widget
+from wiggly import WigglyWidget as WigglyWidgetCPP
+
+# Python-only widget
+from wigglywidget import WigglyWidget as WigglyWidgetPY
+
+
+class Dialog(QDialog):
+ def __init__(self, parent=None):
+ super(Dialog, self).__init__(parent)
+ wiggly_widget_py = WigglyWidgetPY(self)
+ wiggly_widget_cpp = WigglyWidgetCPP(self)
+ lineEdit = QLineEdit(self)
+
+ layout = QVBoxLayout(self)
+ layout.addWidget(wiggly_widget_py)
+ layout.addWidget(wiggly_widget_cpp)
+ layout.addWidget(lineEdit)
+
+ lineEdit.textChanged.connect(wiggly_widget_py.setText)
+ lineEdit.textChanged.connect(wiggly_widget_cpp.setText)
+ lineEdit.setText("Hello world!")
+
+ self.setWindowTitle("Wiggly")
+ self.resize(360, 145)
diff --git a/examples/widgetbinding/macros.h b/examples/widgetbinding/macros.h
new file mode 100644
index 000000000..224fada68
--- /dev/null
+++ b/examples/widgetbinding/macros.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MACROS_H
+#define MACROS_H
+
+#include <QtCore/qglobal.h>
+
+// Export symbols when creating .dll and .lib, and import them when using .lib.
+#if BINDINGS_BUILD
+# define BINDINGS_API Q_DECL_EXPORT
+#else
+# define BINDINGS_API Q_DECL_IMPORT
+#endif
+
+#endif // MACROS_H
diff --git a/examples/widgetbinding/main.py b/examples/widgetbinding/main.py
new file mode 100644
index 000000000..556eb2638
--- /dev/null
+++ b/examples/widgetbinding/main.py
@@ -0,0 +1,61 @@
+############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## 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.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+############################################################################
+
+import sys
+
+from PySide2.QtWidgets import QApplication
+
+from dialog import Dialog
+
+if __name__ == "__main__":
+ app = QApplication()
+ w = Dialog()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/examples/widgetbinding/wigglywidget.cpp b/examples/widgetbinding/wigglywidget.cpp
new file mode 100644
index 000000000..ab549ef07
--- /dev/null
+++ b/examples/widgetbinding/wigglywidget.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "wigglywidget.h"
+
+#include <QFontMetrics>
+#include <QPainter>
+#include <QTimerEvent>
+
+//! [0]
+WigglyWidget::WigglyWidget(QWidget *parent)
+ : QWidget(parent), step(0)
+{
+ setBackgroundRole(QPalette::Midlight);
+ setAutoFillBackground(true);
+
+ QFont newFont = font();
+ newFont.setPointSize(newFont.pointSize() + 20);
+ setFont(newFont);
+
+ timer.start(60, this);
+}
+//! [0]
+
+//! [1]
+void WigglyWidget::paintEvent(QPaintEvent * /* event */)
+//! [1] //! [2]
+{
+ static constexpr int sineTable[16] = {
+ 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38
+ };
+
+ QFontMetrics metrics(font());
+ int x = (width() - metrics.horizontalAdvance(text)) / 2;
+ int y = (height() + metrics.ascent() - metrics.descent()) / 2;
+ QColor color;
+//! [2]
+
+//! [3]
+ QPainter painter(this);
+//! [3] //! [4]
+ for (int i = 0; i < text.size(); ++i) {
+ int index = (step + i) % 16;
+ color.setHsv((15 - index) * 16, 255, 191);
+ painter.setPen(color);
+ painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400),
+ QString(text[i]));
+ x += metrics.horizontalAdvance(text[i]);
+ }
+}
+//! [4]
+
+//! [5]
+void WigglyWidget::timerEvent(QTimerEvent *event)
+//! [5] //! [6]
+{
+ if (event->timerId() == timer.timerId()) {
+ ++step;
+ update();
+ } else {
+ QWidget::timerEvent(event);
+ }
+//! [6]
+}
diff --git a/examples/widgetbinding/wigglywidget.h b/examples/widgetbinding/wigglywidget.h
new file mode 100644
index 000000000..d08db05d5
--- /dev/null
+++ b/examples/widgetbinding/wigglywidget.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WIGGLYWIDGET_H
+#define WIGGLYWIDGET_H
+
+#include "macros.h"
+
+#include <QWidget>
+#include <QBasicTimer>
+
+//! [0]
+class BINDINGS_API WigglyWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ WigglyWidget(QWidget *parent = nullptr);
+
+public slots:
+ void setText(const QString &newText) { text = newText; }
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ QBasicTimer timer;
+ QString text;
+ int step;
+};
+//! [0]
+
+#endif
diff --git a/examples/widgetbinding/wigglywidget.py b/examples/widgetbinding/wigglywidget.py
new file mode 100644
index 000000000..50a061074
--- /dev/null
+++ b/examples/widgetbinding/wigglywidget.py
@@ -0,0 +1,97 @@
+############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## 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.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+############################################################################
+
+from PySide2.QtCore import QBasicTimer
+from PySide2.QtGui import QColor, QFontMetrics, QPainter, QPalette
+from PySide2.QtWidgets import QWidget
+
+
+class WigglyWidget(QWidget):
+ def __init__(self, parent=None):
+ super(WigglyWidget, self).__init__(parent)
+ self.step = 0
+ self.text = ""
+ self.setBackgroundRole(QPalette.Midlight)
+ self.setAutoFillBackground(True)
+
+ newFont = self.font()
+ newFont.setPointSize(newFont.pointSize() + 20)
+ self.setFont(newFont)
+
+ self.timer = QBasicTimer()
+ self.timer.start(60, self)
+
+ def paintEvent(self, event):
+ sineTable = [0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100,
+ -92, -71, -38]
+
+ metrics = QFontMetrics(self.font())
+ x = (self.width() - metrics.horizontalAdvance(self.text)) / 2
+ y = (self.height() + metrics.ascent() - metrics.descent()) / 2
+ color = QColor()
+
+ painter = QPainter(self)
+ for i in range(len(self.text)):
+ index = (self.step + i) % 16
+ color.setHsv((15 - index) * 16, 255, 191)
+ painter.setPen(color)
+ painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400),
+ str(self.text[i]))
+ x += metrics.horizontalAdvance(self.text[i])
+
+ def timerEvent(self, event):
+ if event.timerId() == self.timer.timerId():
+ self.step += 1
+ self.update()
+ else:
+ QWidget.timerEvent(event)
+
+ def setText(self, text):
+ self.text = text
diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
index 0cdd5e2f7..e79123398 100644
--- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
+++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
@@ -656,6 +656,13 @@
</namespace-type>
+ <add-function signature="QEnum(PyObject*)" return-type="PyObject*">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-qenum"/>
+ </add-function>
+ <add-function signature="QFlag(PyObject*)" return-type="PyObject*">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-qflag"/>
+ </add-function>
+
<add-function signature="qAbs(double)" return-type="double">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-qabs"/>
</add-function>
@@ -1467,6 +1474,7 @@
<modify-function signature="msleep(unsigned long)" allow-thread="yes"/>
<modify-function signature="sleep(unsigned long)" allow-thread="yes"/>
<modify-function signature="usleep(unsigned long)" allow-thread="yes"/>
+ <modify-function signature="wait(QDeadlineTimer)" allow-thread="yes"/>
<modify-function signature="wait(unsigned long)" allow-thread="yes"/>
<modify-function signature="yieldCurrentThread()" allow-thread="yes"/>
<modify-function signature="start(QThread::Priority)" allow-thread="yes">
@@ -2420,7 +2428,9 @@
</modify-function>
</object-type>
<object-type name="QWaitCondition">
+ <modify-function signature="wait(QMutex*,QDeadlineTimer)" allow-thread="yes"/>
<modify-function signature="wait(QMutex*,unsigned long)" allow-thread="yes"/>
+ <modify-function signature="wait(QReadWriteLock*,QDeadlineTimer)" allow-thread="yes"/>
<modify-function signature="wait(QReadWriteLock*,unsigned long)" allow-thread="yes"/>
</object-type>
<object-type name="QFileSystemWatcher">
diff --git a/sources/pyside2/PySide2/glue/qtcore.cpp b/sources/pyside2/PySide2/glue/qtcore.cpp
index 1e9d9636e..8bd2baac1 100644
--- a/sources/pyside2/PySide2/glue/qtcore.cpp
+++ b/sources/pyside2/PySide2/glue/qtcore.cpp
@@ -587,6 +587,14 @@ Py_END_ALLOW_THREADS
PySide::runCleanupFunctions();
// @snippet moduleshutdown
+// @snippet qt-qenum
+%PYARG_0 = PySide::QEnum::QEnumMacro(%1, false);
+// @snippet qt-qenum
+
+// @snippet qt-qflag
+%PYARG_0 = PySide::QEnum::QEnumMacro(%1, true);
+// @snippet qt-qflag
+
// @snippet qt-pysideinit
Shiboken::Conversions::registerConverterName(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], "unicode");
Shiboken::Conversions::registerConverterName(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], "str");
diff --git a/sources/pyside2/PySide2/glue/qtuitools.cpp b/sources/pyside2/PySide2/glue/qtuitools.cpp
index 668b512e4..d81f6205a 100644
--- a/sources/pyside2/PySide2/glue/qtuitools.cpp
+++ b/sources/pyside2/PySide2/glue/qtuitools.cpp
@@ -137,16 +137,19 @@ if (uiFileName.isEmpty()) {
Py_RETURN_NONE;
}
-QString uicBin("uic");
-QStringList uicArgs = {"-g", "python", QString::fromUtf8(uiFileName)};
+// Use the 'pyside2-uic' wrapper instead of 'uic'
+// This approach is better than rely on 'uic' since installing
+// the wheels cover this case.
+QString uicBin("pyside2-uic");
+QStringList uicArgs = {QString::fromUtf8(uiFileName)};
QProcess uicProcess;
uicProcess.start(uicBin, uicArgs);
if (!uicProcess.waitForFinished()) {
- qCritical() << "Cannot run 'uic': " << uicProcess.errorString() << " - "
+ qCritical() << "Cannot run 'pyside2-uic': " << uicProcess.errorString() << " - "
<< "Exit status " << uicProcess.exitStatus()
<< " (" << uicProcess.exitCode() << ")\n"
- << "Check if 'uic' is in PATH";
+ << "Check if 'pyside2-uic' is in PATH";
Py_RETURN_NONE;
}
QByteArray uiFileContent = uicProcess.readAllStandardOutput();
diff --git a/sources/pyside2/doc/extras/QtCore.QEnum.rst b/sources/pyside2/doc/extras/QtCore.QEnum.rst
new file mode 100644
index 000000000..a5a2e31fd
--- /dev/null
+++ b/sources/pyside2/doc/extras/QtCore.QEnum.rst
@@ -0,0 +1,92 @@
+.. currentmodule:: PySide2.QtCore
+.. _QEnum:
+
+QEnum/QFlag
+***********
+
+This class decorator is equivalent to the `Q_ENUM` macro from Qt.
+The decorator is used to register an Enum to the meta-object system,
+which is available via `QObject.staticMetaObject`.
+The enumerator must be in a QObject derived class to be registered.
+
+
+Example
+-------
+
+::
+
+ from enum import Enum, Flag, auto
+
+ from PySide2.QtCore import QEnum, QFlag, QObject
+
+ class Demo(QObject):
+
+ @QEnum
+ class Orientation(Enum):
+ North, East, South, West = range(4)
+
+ class Color(Flag):
+ RED = auto()
+ BLUE = auto()
+ GREEN = auto()
+ WHITE = RED | BLUE | GREEN
+
+ QFlag(Color) # identical to @QFlag usage
+
+
+Caution:
+--------
+
+QEnum registers a Python Enum derived class.
+QFlag treats a variation of the Python Enum, the Flag class.
+
+Please do not confuse that with the Qt QFlags concept. Python does
+not use that concept, it has its own class hierarchy, instead.
+For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
+
+
+Details about Qt Flags:
+-----------------------
+
+There are some small differences between Qt flags and Python flags.
+In Qt, we have for instance these declarations:
+
+::
+
+ enum QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
+ HighQualityAntialiasing, NonCosmeticDefaultPen }
+ flags QtGui::RenderHints
+
+The equivalent Python notation would look like this:
+
+::
+
+ @QFlag
+ class RenderHints(enum.Flag)
+ Antialiasing = auto()
+ TextAntialiasing = auto()
+ SmoothPixmapTransform = auto()
+ HighQualityAntialiasing = auto()
+ NonCosmeticDefaultPen = auto()
+
+
+As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
+name, but 'Alignment' as the type name. Non flag enums have the same type and
+enum names.
+
+::
+
+ enum Qt::AlignmentFlag
+ flags Qt::Alignment
+
+The Python way to specify this would be
+
+::
+
+ @QFlag
+ class Alignment(enum.Flag):
+ ...
+
+We are considering to map all builtin enums and flags to Python enums as well
+in a later release.
+
diff --git a/sources/pyside2/doc/tutorials/basictutorial/style.qss b/sources/pyside2/doc/tutorials/basictutorial/style.qss
new file mode 100644
index 000000000..b84b98f05
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/style.qss
@@ -0,0 +1,23 @@
+QListWidget {
+ color: #FFFFFF;
+ background-color: #33373B;
+}
+
+QListWidget::item {
+ height: 50px;
+}
+
+QListWidget::item:selected {
+ background-color: #2ABf9E;
+}
+
+QLabel {
+ background-color: #FFFFFF;
+ qproperty-alignment: AlignCenter;
+}
+
+QPushButton {
+ background-color: #2ABf9E;
+ padding: 20px;
+ font-size: 18px;
+}
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-no.png b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-no.png
new file mode 100644
index 000000000..c30dd621b
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-no.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-no.png b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-no.png
new file mode 100644
index 000000000..eb90e216d
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-no.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-yes.png b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-yes.png
new file mode 100644
index 000000000..5a714977e
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-simple-yes.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-yes.png b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-yes.png
new file mode 100644
index 000000000..8ba49bd26
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling-yes.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.py b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.py
new file mode 100644
index 000000000..41af464c8
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.py
@@ -0,0 +1,95 @@
+#############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+
+from PySide2.QtCore import Qt
+from PySide2.QtWidgets import (QApplication, QHBoxLayout, QLabel, QListWidget,
+ QListWidgetItem, QPushButton, QVBoxLayout,
+ QWidget)
+
+_placeholder = """
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
+velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum
+"""
+
+
+class Widget(QWidget):
+ def __init__(self, parent=None):
+ super(Widget, self).__init__(parent)
+
+ menu_widget = QListWidget()
+ for i in range(10):
+ item = QListWidgetItem("Item {}".format(i))
+ item.setTextAlignment(Qt.AlignCenter)
+ menu_widget.addItem(item)
+
+ text_widget = QLabel(_placeholder)
+ button = QPushButton("Something")
+
+ content_layout = QVBoxLayout()
+ content_layout.addWidget(text_widget)
+ content_layout.addWidget(button)
+ main_widget = QWidget()
+ main_widget.setLayout(content_layout)
+
+ layout = QHBoxLayout()
+ layout.addWidget(menu_widget, 1)
+ layout.addWidget(main_widget, 4)
+ self.setLayout(layout)
+
+
+if __name__ == "__main__":
+ app = QApplication()
+
+ w = Widget()
+ w.show()
+
+ _style = None
+ with open("style.qss", "r") as f:
+ _style = f.read()
+ app.setStyleSheet(_style)
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.rst b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.rst
new file mode 100644
index 000000000..a79f9c898
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgetstyling.rst
@@ -0,0 +1,169 @@
+Widget Styling
+**************
+
+Qt Widgets application use a default theme depending on the platform.
+In some cases, there are system-wide configurations that modify the Qt theme,
+and applications are displayed differently.
+
+However, you can take care of your own widgets and provide a custom style
+to each component. As an example, look at the following simple snippet:
+
+.. code-block:: python
+
+ import sys
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QLabel
+
+ if __name__ == "__main__":
+ app = QApplication()
+ w = QLabel("This is a placeholder text")
+ w.setAlignment(Qt.AlignCenter)
+ w.show()
+ sys.exit(app.exec_())
+
+When you execute this code, you will see a simple `QLabel` aligned at the
+center, and with a placeholder text.
+
+.. image:: widgetstyling-simple-no.png
+ :alt: Simple Widget with no style
+
+You can style your application using the CSS-like syntax.
+For more information, see `Qt Style Sheets Reference`_.
+
+A `QLabel` can be styled differently by setting some of its CSS
+properties, such as `background-color` and `font-family`,
+so let's see how does the code look like with these changes:
+
+.. code-block:: python
+
+ import sys
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QLabel
+
+ if __name__ == "__main__":
+ app = QApplication()
+ w = QLabel("This is a placeholder text")
+ w.setAlignment(Qt.AlignCenter)
+ w.setStyleSheet("""
+ background-color: #262626;
+ color: #FFFFFF;
+ font-family: Titillium;
+ font-size: 18px;
+ """)
+ w.show()
+ sys.exit(app.exec_())
+
+Now when you run the code, notice that the `QLabel` looks different with your
+custom style:
+
+.. image:: widgetstyling-simple-yes.png
+ :alt: Simple Widget with Style
+
+
+.. note::
+
+ If you don't have the font `Titillium` installed, you can try with any
+ other you prefer.
+ Remember you can list your installed fonts using `QFontDatabase`,
+ specifically the `families()` method.
+
+
+Styling each UI element separately like you did in the previous snippet is a
+lot of work. The easier alternative for this is to use Qt Style Sheets,
+which is one or more `.qss` files defining the style for the UI elements in
+your application.
+
+More examples can be found in the `Qt Style Sheet Examples`_ documentation
+page.
+
+
+.. _`Qt Style Sheets Reference`: https://doc.qt.io/qt-5/stylesheet-reference.html
+.. _`Qt Style Sheet Examples`: https://doc.qt.io/qt-5/stylesheet-examples.html
+
+Qt Style Sheets
+===============
+
+.. warning::
+
+ Before starting modifying your application, keep in mind that you will be
+ responsible for all the graphical details of the application.
+ Altering margins, and sizes might end up looking strange or incorrect, so you
+ need to be careful when altering the style.
+ It's recommended to create a full new Qt style to cover all the possible
+ corner cases.
+
+A `qss` file is quite similar to a CSS file, but you need to specify the Widget
+component and optionally the name of the object::
+
+ QLabel {
+ background-color: red;
+ }
+
+ QLabel#title {
+ font-size: 20px;
+ }
+
+The first style defines a `background-color` for all `QLabel` objects in your
+application, whereas the later one styles the `title` object only.
+
+.. note::
+
+ You can set object names with the `setObjectName(str)` function to any Qt
+ object, for example: for a `label = QLabel("Test")`, you can write
+ `label.setObjectName("title")`
+
+
+Once you have a `qss` file for your application, you can apply it by reading
+the file and using the `QApplication.setStyleSheet(str)` function:
+
+.. code-block:: python
+
+ if __name__ == "__main__":
+ app = QApplication()
+
+ w = Widget()
+ w.show()
+
+ with open("style.qss", "r") as f:
+ _style = f.read()
+ app.setStyleSheet(_style)
+
+ sys.exit(app.exec_())
+
+Having a general `qss` file allows you to decouple the styling aspects of
+the code, without mixing it in the middle of the general functionality, and you
+can simply enable it or disable it.
+
+Look at this new example, with more widgets components:
+
+.. literalinclude:: widgetstyling.py
+ :linenos:
+ :lines: 59-81
+
+This displays a two column widget, with a `QListWidget` on the left and a
+`QLabel` and a `QPushButton` on the right. It looks like this when you run the
+code:
+
+.. image:: widgetstyling-no.png
+ :alt: Widget with no style
+
+If you add content to the previously described `style.qss` file, you can modify
+the look-n-feel of the previous example:
+
+.. literalinclude:: style.qss
+ :linenos:
+
+The style changes mainly the color of the different widgets, alter the
+alignment, and includes some spacing.
+You can also use state-based styling on the QListWidget *items* for example, to
+style them differently depending on whether they are *selected* or not.
+
+After applying all the styling alternatives you explored in this topic, notice
+that the `QLabel` example looks a lot different now.
+Try running the code to check its new look:
+
+.. image:: widgetstyling-yes.png
+ :alt: Widget with style
+
+You have the freedom to tune your style sheets and provide a really nice
+look-n-feel to all your applications.
diff --git a/sources/pyside2/doc/tutorials/index.rst b/sources/pyside2/doc/tutorials/index.rst
index 598b42ca1..9739eee97 100644
--- a/sources/pyside2/doc/tutorials/index.rst
+++ b/sources/pyside2/doc/tutorials/index.rst
@@ -19,6 +19,7 @@ Basic tutorials
basictutorial/dialog.rst
basictutorial/uifiles.rst
basictutorial/qrcfiles.rst
+ basictutorial/widgetstyling.rst
Real use-cases applications
---------------------------
diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt
index 11342ec71..31f68749a 100644
--- a/sources/pyside2/libpyside/CMakeLists.txt
+++ b/sources/pyside2/libpyside/CMakeLists.txt
@@ -43,6 +43,7 @@ set(libpyside_SRC
signalmanager.cpp
globalreceiverv2.cpp
pysideclassinfo.cpp
+ pysideqenum.cpp
pysidemetafunction.cpp
pysidesignal.cpp
pysideslot.cpp
@@ -125,6 +126,7 @@ endif()
set(libpyside_HEADERS
dynamicqmetaobject.h
pysideclassinfo.h
+ pysideqenum.h
pysidemacros.h
signalmanager.h
pyside.h
diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp
index dae9e2059..efdf33ac9 100644
--- a/sources/pyside2/libpyside/dynamicqmetaobject.cpp
+++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp
@@ -44,6 +44,7 @@
#include "pysideproperty.h"
#include "pysideproperty_p.h"
#include "pysideslot_p.h"
+#include "pysideqenum.h"
#include <shiboken.h>
@@ -91,6 +92,10 @@ public:
int addProperty(const QByteArray &property, PyObject *data);
void addInfo(const QByteArray &key, const QByteArray &value);
void addInfo(const QMap<QByteArray, QByteArray> &info);
+ void addEnumerator(const char *name,
+ bool flag,
+ bool scoped,
+ const QVector<QPair<QByteArray, int> > &entries);
void removeProperty(int index);
const QMetaObject *update();
@@ -357,6 +362,28 @@ void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info)
m_d->addInfo(info);
}
+void MetaObjectBuilder::addEnumerator(const char *name, bool flag, bool scoped,
+ const QVector<QPair<QByteArray, int> > &entries)
+{
+ m_d->addEnumerator(name, flag, scoped, entries);
+}
+
+void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool scoped,
+ const QVector<QPair<QByteArray, int> > &entries)
+{
+ auto builder = ensureBuilder();
+ int have_already = builder->indexOfEnumerator(name);
+ if (have_already >= 0)
+ builder->removeEnumerator(have_already);
+ auto enumbuilder = builder->addEnumerator(name);
+ enumbuilder.setIsFlag(flag);
+ enumbuilder.setIsScoped(scoped);
+
+ for (auto item : entries)
+ enumbuilder.addKey(item.first, item.second);
+ m_dirty = true;
+}
+
void MetaObjectBuilderPrivate::removeProperty(int index)
{
index -= m_baseObject->propertyCount();
@@ -430,6 +457,8 @@ const QMetaObject *MetaObjectBuilder::update()
return m_d->update();
}
+using namespace Shiboken;
+
void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
{
// Get all non-QObject-derived base types in method resolution order, filtering out the types
@@ -439,7 +468,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
// existing connections.
const PyObject *mro = type->tp_mro;
const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro);
- PyTypeObject *qObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ PyTypeObject *qObjectType = Conversions::getPythonTypeObject("QObject*");
std::vector<PyTypeObject *> basesToCheck;
// Prepend the actual type that we are parsing.
@@ -470,7 +499,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
// Register signals.
auto data = reinterpret_cast<PySideSignal *>(value);
if (data->data->signalName.isEmpty())
- data->data->signalName = Shiboken::String::toCString(key);
+ data->data->signalName = String::toCString(key);
for (const auto &s : data->data->signatures) {
const auto sig = data->data->signalName + '(' + s.signature + ')';
if (m_baseObject->indexOfSignal(sig) == -1) {
@@ -489,7 +518,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
}
}
- Shiboken::AutoDecRef slotAttrName(Shiboken::String::fromCString(PYSIDE_SLOT_LIST_ATTR));
+ AutoDecRef slotAttrName(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.
@@ -501,16 +530,16 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
while (PyDict_Next(attrs, &pos, &key, &value)) {
if (Property::checkType(value)) {
- const int index = m_baseObject->indexOfProperty(Shiboken::String::toCString(key));
+ const int index = m_baseObject->indexOfProperty(String::toCString(key));
if (index == -1)
- addProperty(Shiboken::String::toCString(key), value);
+ addProperty(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 *pySignature = PyList_GET_ITEM(signatureList, i);
- QByteArray signature(Shiboken::String::toCString(pySignature));
+ QByteArray signature(String::toCString(pySignature));
// Split the slot type and its signature.
QByteArray type;
const int spacePos = signature.indexOf(' ');
@@ -530,4 +559,29 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
}
}
}
+ // PYSIDE-957: Collect the delayed QEnums
+ auto collectedEnums = PySide::QEnum::resolveDelayedQEnums(type);
+ for (PyObject *obEnumType : collectedEnums) {
+ bool isFlag = PySide::QEnum::isFlag(obEnumType);
+ AutoDecRef obName(PyObject_GetAttr(obEnumType, PyMagicName::name()));
+ // Everything has been checked already in resolveDelayedQEnums.
+ // Therefore, we don't need to error-check here again.
+ auto name = String::toCString(obName);
+ AutoDecRef members(PyObject_GetAttr(obEnumType, PyMagicName::members()));
+ AutoDecRef items(PepMapping_Items(members));
+ Py_ssize_t nr_items = PySequence_Length(items);
+
+ QVector<QPair<QByteArray, int> > entries;
+ for (Py_ssize_t idx = 0; idx < nr_items; ++idx) {
+ AutoDecRef item(PySequence_GetItem(items, idx));
+ AutoDecRef key(PySequence_GetItem(item, 0));
+ AutoDecRef member(PySequence_GetItem(item, 1));
+ AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
+ auto ckey = String::toCString(key);
+ auto ivalue = PyInt_AsSsize_t(value); // int/long cheating
+ auto thing = QPair<QByteArray, int>(ckey, int(ivalue));
+ entries.push_back(thing);
+ }
+ addEnumerator(name, isFlag, true, entries);
+ }
}
diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.h b/sources/pyside2/libpyside/dynamicqmetaobject.h
index 1fbe73ea4..7279d5c26 100644
--- a/sources/pyside2/libpyside/dynamicqmetaobject.h
+++ b/sources/pyside2/libpyside/dynamicqmetaobject.h
@@ -68,7 +68,10 @@ public:
int addProperty(const char *property, PyObject *data);
void addInfo(const char *key, const char *value);
void addInfo(const QMap<QByteArray, QByteArray> &info);
-
+ void addEnumerator(const char *name,
+ bool flag,
+ bool scoped,
+ const QVector<QPair<QByteArray, int> > &entries);
void removeProperty(int index);
const QMetaObject *update();
diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp
index 4a554de58..66e931164 100644
--- a/sources/pyside2/libpyside/pyside.cpp
+++ b/sources/pyside2/libpyside/pyside.cpp
@@ -223,8 +223,7 @@ std::size_t getSizeOfQObject(SbkObjectType *type)
void initDynamicMetaObject(SbkObjectType *type, const QMetaObject *base, std::size_t cppObjSize)
{
//create DynamicMetaObject based on python type
- auto userData =
- new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
+ auto userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
userData->mo.update();
Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>);
diff --git a/sources/pyside2/libpyside/pysideqenum.cpp b/sources/pyside2/libpyside/pysideqenum.cpp
new file mode 100644
index 000000000..f46b5536c
--- /dev/null
+++ b/sources/pyside2/libpyside/pysideqenum.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <shiboken.h>
+
+#include "pysideqenum.h"
+#include "dynamicqmetaobject.h"
+#include "pyside_p.h"
+
+
+///////////////////////////////////////////////////////////////
+//
+// PYSIDE-957: Create QEnum dynamically from Python Enum
+//
+//
+extern "C" {
+
+using namespace Shiboken;
+
+static PyObject *analyzePyEnum(PyObject *pyenum, PyObject *container = nullptr)
+{
+ /*
+ * This is the straight-forward implementation of QEnum/QFlag. It does no
+ * longer create an equivalent Qt enum but takes the Python enum as-is.
+ *
+ * It parses an Enum/Flag derived Python enum completely so that
+ * registering can be done without error checks. This would be impossible
+ * in MetaObjectBuilderPrivate::parsePythonType.
+ */
+ AutoDecRef members(PyObject_GetAttr(pyenum, Shiboken::PyMagicName::members()));
+ if (members.isNull())
+ return nullptr;
+ AutoDecRef items(PepMapping_Items(members));
+ if (items.isNull())
+ return nullptr;
+ int iflag = PySide::QEnum::isFlag(pyenum);
+ if (iflag < 0)
+ return nullptr;
+ Py_ssize_t nr_items = PySequence_Length(items);
+ if (nr_items < 0)
+ return nullptr;
+
+ for (Py_ssize_t idx = 0; idx < nr_items; ++idx) {
+ AutoDecRef item(PySequence_GetItem(items, idx));
+ if (item.isNull())
+ return nullptr;
+
+ // The item should be a 2-element sequence of the key name and an
+ // object containing the value.
+ AutoDecRef key(PySequence_GetItem(item, 0));
+ AutoDecRef member(PySequence_GetItem(item, 1));
+ if (key.isNull() || member.isNull())
+ return nullptr;
+ if (!Shiboken::String::check(key)) {
+ // '%.200s' is the safety stringbuffer size of most CPython functions.
+ PyErr_Format(PyExc_TypeError,
+ "QEnum expected a string mapping as __members__, got '%.200s'",
+ Py_TYPE(key)->tp_name);
+ return nullptr;
+ }
+
+ // Get the value.
+ AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
+ if (value.isNull())
+ return nullptr;
+ if (!PyInt_Check(value)) { // int/long cheating
+ PyErr_Format(PyExc_TypeError,
+ "QEnum expected an int value as '%.200s', got '%.200s'",
+ Shiboken::String::toCString(key), Py_TYPE(value)->tp_name);
+ return nullptr;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static Py_ssize_t get_lineno()
+{
+ PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ AutoDecRef ob_lineno(PyObject_GetAttr(frame, Shiboken::PyName::f_lineno()));
+ if (ob_lineno.isNull() || !PyInt_Check(ob_lineno)) // int/long cheating
+ return -1;
+ return PyInt_AsSsize_t(ob_lineno); // int/long cheating
+}
+
+static bool is_module_code()
+{
+ PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ AutoDecRef ob_code(PyObject_GetAttr(frame, Shiboken::PyName::f_code()));
+ if (ob_code.isNull())
+ return false;
+ AutoDecRef ob_name(PyObject_GetAttr(ob_code, Shiboken::PyName::co_name()));
+ if (ob_name.isNull())
+ return false;
+ const char *codename = Shiboken::String::toCString(ob_name);
+ return strcmp(codename, "<module>") == 0;
+}
+
+} // extern "C"
+
+namespace PySide { namespace QEnum {
+
+static std::map<int, PyObject *> enumCollector;
+
+int isFlag(PyObject *obType)
+{
+ /*
+ * Find out if this is an Enum or a Flag derived class.
+ * It checks also if things come from the enum module and if it is
+ * an Enum or Flag class at all.
+ *
+ * The function is called in MetaObjectBuilderPrivate::parsePythonType
+ * again to obtain the flag value.
+ */
+ if (!PyType_Check(obType)) {
+ PyErr_Format(PyExc_TypeError, "a class argument was expected, not a '%.200s' instance",
+ Py_TYPE(obType)->tp_name);
+ return -1;
+ };
+ auto *type = reinterpret_cast<PyTypeObject *>(obType);
+ PyObject *mro = type->tp_mro;
+ Py_ssize_t i, n = PyTuple_GET_SIZE(mro);
+ bool right_module = false;
+ bool have_enum = false;
+ bool have_flag = false;
+ bool have_members = PyObject_HasAttr(obType, PyMagicName::members());
+ for (i = 0; i < n; i++) {
+ obType = PyTuple_GET_ITEM(mro, i);
+ type = reinterpret_cast<PyTypeObject *>(obType);
+ AutoDecRef mod(PyObject_GetAttr(obType, PyMagicName::module()));
+ QByteArray cmod = String::toCString(mod);
+ QByteArray cname = type->tp_name;
+ if (cmod == "enum") {
+ right_module = true;
+ if (cname == "Enum")
+ have_enum = true;
+ else if (cname == "Flag")
+ have_flag = true;
+ }
+ }
+ if (!right_module || !(have_enum || have_flag) || !have_members) {
+ PyErr_Format(PyExc_TypeError, "type %.200s does not inherit from 'Enum' or 'Flag'",
+ type->tp_name);
+ return -1;
+ }
+ return bool(have_flag);
+}
+
+PyObject *QEnumMacro(PyObject *pyenum, bool flag)
+{
+ /*
+ * This is the official interface of 'QEnum'. It first calls 'analyzePyEnum'.
+ * When called as toplevel enum, it simply returns after some checks.
+ * Otherwise, 'pyenum' is stored for later use by the meta class registation.
+ */
+ int computedFlag = isFlag(pyenum);
+ if (computedFlag < 0)
+ return nullptr;
+ if (bool(computedFlag) != flag) {
+ AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::qualname()));
+ auto cname = String::toCString(name);
+ const char *e = "Enum";
+ const char *f = "Flag";
+ PyErr_Format(PyExc_TypeError, "expected '%s' but got '%s' (%.200s)",
+ flag ? f : e, flag ? e : f, cname);
+ return nullptr;
+ }
+ auto ok = analyzePyEnum(pyenum);
+ if (ok == nullptr)
+ return nullptr;
+ if (is_module_code()) {
+ // This is a toplevel enum which we resolve immediately.
+ Py_INCREF(pyenum);
+ return pyenum;
+ }
+
+ Py_ssize_t lineno = get_lineno();
+ if (lineno < 0)
+ return nullptr;
+ // Handle the rest via line number and the meta class.
+ Py_INCREF(pyenum);
+ Py_XDECREF(enumCollector[lineno]);
+ enumCollector[lineno] = pyenum;
+ Py_RETURN_NONE;
+}
+
+std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *containerType)
+{
+ /*
+ * This is the internal interface of 'QEnum'.
+ * It is called at the end of the meta class call 'SbkObjectTypeTpNew' via
+ * MetaObjectBuilderPrivate::parsePythonType and resolves the collected
+ * Python Enum arguments. The result is then registered.
+ */
+ if (enumCollector.empty())
+ return {};
+ PyObject *obContainerType = reinterpret_cast<PyObject *>(containerType);
+ Py_ssize_t lineno = get_lineno();
+
+ std::vector<PyObject *> result;
+
+ auto it = enumCollector.begin();
+ while (it != enumCollector.end()) {
+ int nr = it->first;
+ PyObject *pyenum = it->second;
+ if (nr >= lineno) {
+ AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::name()));
+ if (name.isNull() || PyObject_SetAttr(obContainerType, name, pyenum) < 0)
+ return {};
+ result.push_back(pyenum);
+ it = enumCollector.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return result;
+}
+
+} // namespace Enum
+} // namespace Shiboken
+
+//
+///////////////////////////////////////////////////////////////
diff --git a/sources/pyside2/libpyside/pysideqenum.h b/sources/pyside2/libpyside/pysideqenum.h
new file mode 100644
index 000000000..fc4e55982
--- /dev/null
+++ b/sources/pyside2/libpyside/pysideqenum.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PYSIDE_QENUM_H
+#define PYSIDE_QENUM_H
+
+#include <pysidemacros.h>
+#include <vector>
+
+namespace PySide { namespace QEnum {
+
+// PYSIDE-957: Support the QEnum macro
+PYSIDE_API PyObject *QEnumMacro(PyObject *, bool);
+PYSIDE_API int isFlag(PyObject *);
+PYSIDE_API std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *);
+PYSIDE_API void init();
+
+} // namespace QEnum
+} // namespace PySide
+
+#endif
diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp
index 39ed1a6bc..32e1bb0c6 100644
--- a/sources/pyside2/libpyside/pysidesignal.cpp
+++ b/sources/pyside2/libpyside/pysidesignal.cpp
@@ -54,7 +54,6 @@
#include <utility>
#define QT_SIGNAL_SENTINEL '2'
-#define PyEnumMeta_Check(x) (strcmp(Py_TYPE(arg)->tp_name, "EnumMeta") == 0)
namespace PySide {
namespace Signal {
diff --git a/sources/pyside2/tests/QtCore/qenum_test.py b/sources/pyside2/tests/QtCore/qenum_test.py
index 1edb8981a..f99a893d9 100644
--- a/sources/pyside2/tests/QtCore/qenum_test.py
+++ b/sources/pyside2/tests/QtCore/qenum_test.py
@@ -2,7 +2,7 @@
#############################################################################
##
-## Copyright (C) 2016 The Qt Company Ltd.
+## Copyright (C) 2020 The Qt Company Ltd.
## Contact: https://www.qt.io/licensing/
##
## This file is part of the test suite of Qt for Python.
@@ -40,7 +40,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from init_paths import init_test_paths
init_test_paths(False)
-from PySide2.QtCore import Qt, QIODevice
+from PySide2.QtCore import Qt, QIODevice, QObject, QEnum, QFlag
class TestEnum(unittest.TestCase):
@@ -68,28 +68,30 @@ class TestEnum(unittest.TestCase):
# Floats
with self.assertRaises(TypeError):
- a = k+2.0
+ a = k + 2.0
with self.assertRaises(TypeError):
- a = k-2.0
+ a = k - 2.0
with self.assertRaises(TypeError):
- a = k*2.0
+ a = k * 2.0
- @unittest.skipUnless(getattr(sys, "getobjects", None), "requires debug build")
+ @unittest.skipUnless(getattr(sys, "getobjects", None), "requires --with-trace-refs")
+ @unittest.skipUnless(getattr(sys, "gettotalrefcount", None), "requires --with-pydebug")
def testEnumNew_NoLeak(self):
gc.collect()
total = sys.gettotalrefcount()
for idx in range(1000):
ret = Qt.Key(42)
+
gc.collect()
delta = sys.gettotalrefcount() - total
print("delta total refcount =", delta)
if abs(delta) >= 10:
- all = sys.getobjects(0)
- all.sort(key=lambda x: sys.getrefcount(x), reverse=True)
+ all = [(sys.getrefcount(x), x) for x in sys.getobjects(0)]
+ all.sort(key=lambda x: x[0], reverse=True)
for ob in all[:10]:
- print(sys.getrefcount(ob), ob)
+ print(ob)
self.assertTrue(abs(delta) < 10)
@@ -141,6 +143,105 @@ class TestEnumPickling(unittest.TestCase):
else:
func()
+# PYSIDE-957: The QEnum macro
+
+try:
+ import enum
+ HAVE_ENUM = True
+except ImportError:
+ HAVE_ENUM = False
+ QEnum = QFlag = lambda x: x
+ import types
+ class Enum: pass
+ enum = types.ModuleType("enum")
+ enum.Enum = enum.Flag = enum.IntEnum = enum.IntFlag = Enum
+ Enum.__module__ = "enum"
+ Enum.__members__ = {}
+ del Enum
+ enum.auto = lambda: 42
+
+HAVE_FLAG = hasattr(enum, "Flag")
+
+@QEnum
+class OuterEnum(enum.Enum):
+ A = 1
+ B = 2
+
+class SomeClass(QObject):
+
+ @QEnum
+ class SomeEnum(enum.Enum):
+ A = 1
+ B = 2
+ C = 3
+
+ @QEnum
+ class OtherEnum(enum.IntEnum):
+ A = 1
+ B = 2
+ C = 3
+
+ class InnerClass(QObject):
+
+ @QEnum
+ class InnerEnum(enum.Enum):
+ X = 42
+
+ class SomeEnum(enum.Enum):
+ A = 4
+ B = 5
+ C = 6
+
+ QEnum(SomeEnum) # works even without the decorator assignment
+
+
+@unittest.skipUnless(HAVE_ENUM, "requires 'enum' module (use 'pip install enum34' for Python 2)")
+class TestQEnumMacro(unittest.TestCase):
+ def testTopLevel(self):
+ self.assertEqual(type(OuterEnum).__module__, "enum")
+ self.assertEqual(type(OuterEnum).__name__, "EnumMeta")
+ self.assertEqual(len(OuterEnum.__members__), 2)
+
+ def testSomeClass(self):
+ self.assertEqual(type(SomeClass.SomeEnum).__module__, "enum")
+ self.assertEqual(type(SomeClass.SomeEnum).__name__, "EnumMeta")
+ self.assertEqual(len(SomeClass.SomeEnum.__members__), 3)
+ with self.assertRaises(TypeError):
+ int(SomeClass.SomeEnum.C) == 6
+ self.assertEqual(SomeClass.OtherEnum.C, 3)
+
+ @unittest.skipIf(sys.version_info[0] < 3, "we cannot support nested classes in Python 2")
+ def testInnerClass(self):
+ self.assertEqual(SomeClass.InnerClass.InnerEnum.__qualname__,
+ "SomeClass.InnerClass.InnerEnum")
+ with self.assertRaises(TypeError):
+ int(SomeClass.InnerClass.InnerEnum.X) == 42
+
+ @unittest.skipUnless(HAVE_FLAG, "some older Python versions have no 'Flag'")
+ def testEnumFlag(self):
+ with self.assertRaises(TypeError):
+ class WrongFlagForEnum(QObject):
+ @QEnum
+ class Bad(enum.Flag):
+ pass
+ with self.assertRaises(TypeError):
+ class WrongEnuForFlag(QObject):
+ @QFlag
+ class Bad(enum.Enum):
+ pass
+
+ def testIsRegistered(self):
+ mo = SomeClass.staticMetaObject
+ self.assertEqual(mo.enumeratorCount(), 2)
+ self.assertEqual(mo.enumerator(0).name(), "OtherEnum")
+ self.assertEqual(mo.enumerator(0).scope(), "SomeClass")
+ self.assertEqual(mo.enumerator(1).name(), "SomeEnum")
+ moi = SomeClass.InnerClass.staticMetaObject
+ self.assertEqual(moi.enumerator(0).name(), "InnerEnum")
+ ## Question: Should that scope not better be "SomeClass.InnerClass"?
+ ## But we have __qualname__ already:
+ self.assertEqual(moi.enumerator(0).scope(), "InnerClass")
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
index 310a751e0..1eaa36540 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -620,27 +620,52 @@ long clang_EnumDecl_isScoped4(BaseVisitor *bv, const CXCursor &cursor)
}
#endif // CLANG_NO_ENUMDECL_ISSCOPED
+// Resolve declaration and type of a base class
+
+struct TypeDeclaration
+{
+ CXType type;
+ CXCursor declaration;
+};
+
+static TypeDeclaration resolveBaseSpecifier(const CXCursor &cursor)
+{
+ Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier);
+ CXType inheritedType = clang_getCursorType(cursor);
+ CXCursor decl = clang_getTypeDeclaration(inheritedType);
+ if (inheritedType.kind != CXType_Unexposed) {
+ while (true) {
+ auto kind = clang_getCursorKind(decl);
+ if (kind != CXCursor_TypeAliasDecl && kind != CXCursor_TypedefDecl)
+ break;
+ inheritedType = clang_getTypedefDeclUnderlyingType(decl);
+ decl = clang_getTypeDeclaration(inheritedType);
+ }
+ }
+ return {inheritedType, decl};
+}
+
// Add a base class to the current class from CXCursor_CXXBaseSpecifier
void BuilderPrivate::addBaseClass(const CXCursor &cursor)
{
+ Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier);
// Note: spelling has "struct baseClass", use type
QString baseClassName;
- const CXType inheritedType = clang_getCursorType(cursor);
- if (inheritedType.kind == CXType_Unexposed) {
+ const auto decl = resolveBaseSpecifier(cursor);
+ if (decl.type.kind == CXType_Unexposed) {
// The type is unexposed when the base class is a template type alias:
// "class QItemSelection : public QList<X>" where QList is aliased to QVector.
// Try to resolve via code model.
- TypeInfo info = createTypeInfo(inheritedType);
+ TypeInfo info = createTypeInfo(decl.type);
auto parentScope = m_scopeStack.at(m_scopeStack.size() - 2); // Current is class.
auto resolved = TypeInfo::resolveType(info, parentScope);
if (resolved != info)
baseClassName = resolved.toString();
}
if (baseClassName.isEmpty())
- baseClassName = getTypeName(inheritedType);
+ baseClassName = getTypeName(decl.type);
- const CXCursor declCursor = clang_getTypeDeclaration(inheritedType);
- const CursorClassHash::const_iterator it = m_cursorClassHash.constFind(declCursor);
+ auto it = m_cursorClassHash.constFind(decl.declaration);
const CodeModel::AccessPolicy access = accessPolicy(clang_getCXXAccessSpecifier(cursor));
if (it == m_cursorClassHash.constEnd()) {
// Set unqualified name. This happens in cases like "class X : public std::list<...>"
diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp
index f2e15fdb0..f0aa4a318 100644
--- a/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp
+++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp
@@ -588,4 +588,34 @@ void TestAbstractMetaClass::testIsPolymorphic()
QVERIFY(!a->isPolymorphic());
}
+void TestAbstractMetaClass::testClassTypedefedBaseClass()
+{
+ const char cppCode[] =R"CPP(
+class Base {
+};
+
+using BaseAlias1 = Base;
+using BaseAlias2 = BaseAlias1;
+
+class Derived : public BaseAlias2 {
+};
+)CPP";
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <object-type name='Base'/>
+ <object-type name='Derived'/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(!builder.isNull());
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.count(), 2);
+ auto base = AbstractMetaClass::findClass(classes, QLatin1String("Base"));
+ QVERIFY(base);
+ auto derived = AbstractMetaClass::findClass(classes, QLatin1String("Derived"));
+ QVERIFY(derived);
+ QCOMPARE(derived->baseClasses().value(0), base);
+}
+
QTEST_APPLESS_MAIN(TestAbstractMetaClass)
diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.h b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.h
index e19973625..1d9f8d8f6 100644
--- a/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.h
+++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.h
@@ -51,6 +51,7 @@ private slots:
void testAbstractClassDefaultConstructors();
void testObjectTypesMustNotHaveCopyConstructors();
void testIsPolymorphic();
+ void testClassTypedefedBaseClass();
};
#endif // TESTABSTRACTMETACLASS_H
diff --git a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp
index c50084b8e..ca4af9a10 100644
--- a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp
+++ b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp
@@ -283,7 +283,7 @@ void TestAddFunction::testAddFunctionAtModuleLevel()
QCOMPARE(mods.size(), 1);
QVERIFY(mods.first().isCodeInjection());
CodeSnip snip = mods.first().snips.first();
- QCOMPARE(snip.code(), QLatin1String("custom_code();"));
+ QCOMPARE(snip.code().trimmed(), QLatin1String("custom_code();"));
}
void TestAddFunction::testAddFunctionWithVarargs()
diff --git a/sources/shiboken2/ApiExtractor/tests/testnestedtypes.cpp b/sources/shiboken2/ApiExtractor/tests/testnestedtypes.cpp
index 10194eb34..e61418467 100644
--- a/sources/shiboken2/ApiExtractor/tests/testnestedtypes.cpp
+++ b/sources/shiboken2/ApiExtractor/tests/testnestedtypes.cpp
@@ -69,7 +69,7 @@ void TestNestedTypes::testNestedTypesModifications()
QCOMPARE(ins->functions().count(), 1);
QCOMPARE(ins->typeEntry()->codeSnips().count(), 1);
CodeSnip snip = ins->typeEntry()->codeSnips().first();
- QCOMPARE(snip.code(), QLatin1String("custom_code1();"));
+ QCOMPARE(snip.code().trimmed(), QLatin1String("custom_code1();"));
AbstractMetaFunction* addedFunc = ins->functions().first();
QVERIFY(addedFunc->isUserAdded());
@@ -80,7 +80,7 @@ void TestNestedTypes::testNestedTypesModifications()
QCOMPARE(addedFunc->modifications().size(), 1);
QVERIFY(addedFunc->modifications().first().isCodeInjection());
snip = addedFunc->modifications().first().snips.first();
- QCOMPARE(snip.code(), QLatin1String("custom_code2();"));
+ QCOMPARE(snip.code().trimmed(), QLatin1String("custom_code2();"));
const AbstractMetaClass *sc = AbstractMetaClass::findClass(classes, QLatin1String("OuterNamespace::InnerNamespace::SomeClass"));
QVERIFY(ins);
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp
index 920da9e10..729e6b32b 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken2/ApiExtractor/typesystem.cpp
@@ -34,6 +34,7 @@
#include <QtCore/QSet>
#include <algorithm>
+#include <limits>
static QString strings_Object = QLatin1String("Object");
static QString strings_String = QLatin1String("String");
@@ -212,7 +213,7 @@ QString TemplateInstance::expandCode() const
if (!code.startsWith(QLatin1Char('\n')))
result += QLatin1Char('\n');
result += code;
- result += QLatin1String("\n// TEMPLATE - ") + m_name + QLatin1String(" - END");
+ result += QLatin1String("\n// TEMPLATE - ") + m_name + QLatin1String(" - END\n");
return result;
}
@@ -226,6 +227,82 @@ QString CodeSnipAbstract::code() const
return res;
}
+void CodeSnipAbstract::addCode(const QString &code)
+{
+ codeList.append(CodeSnipFragment(fixSpaces(code)));
+}
+
+template <class String> // QString, QStringRef
+static inline int firstNonBlank(const String &s)
+{
+ const auto it = std::find_if(s.cbegin(), s.cend(),
+ [] (QChar c) { return !c.isSpace(); });
+ return int(it - s.cbegin());
+}
+
+template <class String> // QString, QStringRef
+static inline bool isEmpty(const String &s)
+{
+ return s.isEmpty()
+ || std::all_of(s.cbegin(), s.cend(),
+ [] (QChar c) { return c.isSpace(); });
+}
+
+QString CodeSnipAbstract::dedent(const QString &code)
+{
+ if (code.isEmpty())
+ return code;
+ // Right trim if indent=0, or trim if single line
+ if (!code.at(0).isSpace() || !code.contains(QLatin1Char('\n')))
+ return code.trimmed();
+ const auto lines = code.splitRef(QLatin1Char('\n'));
+ int spacesToRemove = std::numeric_limits<int>::max();
+ for (const auto &line : lines) {
+ if (!isEmpty(line)) {
+ const int nonSpacePos = firstNonBlank(line);
+ if (nonSpacePos < spacesToRemove)
+ spacesToRemove = nonSpacePos;
+ if (spacesToRemove == 0)
+ return code;
+ }
+ }
+ QString result;
+ for (const auto &line : lines) {
+ if (!isEmpty(line) && spacesToRemove < line.size())
+ result += line.mid(spacesToRemove).toString();
+ result += QLatin1Char('\n');
+ }
+ return result;
+}
+
+QString CodeSnipAbstract::fixSpaces(QString code)
+{
+ code.remove(QLatin1Char('\r'));
+ // Check for XML <tag>\n<space>bla...
+ if (code.startsWith(QLatin1String("\n ")))
+ code.remove(0, 1);
+ while (!code.isEmpty() && code.back().isSpace())
+ code.chop(1);
+ code = dedent(code);
+ if (!code.isEmpty() && !code.endsWith(QLatin1Char('\n')))
+ code.append(QLatin1Char('\n'));
+ return code;
+}
+
+// Prepend a line to the code, observing indentation
+void CodeSnipAbstract::prependCode(QString *code, QString firstLine)
+{
+ while (!code->isEmpty() && code->front() == QLatin1Char('\n'))
+ code->remove(0, 1);
+ if (!code->isEmpty() && code->front().isSpace()) {
+ const int indent = firstNonBlank(*code);
+ firstLine.prepend(QString(indent, QLatin1Char(' ')));
+ }
+ if (!firstLine.endsWith(QLatin1Char('\n')))
+ firstLine += QLatin1Char('\n');
+ code->prepend(firstLine);
+}
+
QString CodeSnipFragment::code() const
{
return m_instance ? m_instance->expandCode() : m_code;
@@ -1184,3 +1261,8 @@ TypeEntry *ObjectTypeEntry::clone() const
}
ObjectTypeEntry::ObjectTypeEntry(const ObjectTypeEntry &) = default;
+
+void DocModification::setCode(const QString &code)
+{
+ m_code = CodeSnipAbstract::fixSpaces(code);
+}
diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h
index 6d2d4bb44..c6995b64d 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.h
+++ b/sources/shiboken2/ApiExtractor/typesystem.h
@@ -115,7 +115,7 @@ class CodeSnipAbstract
public:
QString code() const;
- void addCode(const QString &code) { codeList.append(CodeSnipFragment(code)); }
+ void addCode(const QString &code);
void addCode(const QStringRef &code) { addCode(code.toString()); }
void addTemplateInstance(TemplateInstance *ti)
@@ -124,6 +124,10 @@ public:
}
QVector<CodeSnipFragment> codeList;
+
+ static QString fixSpaces(QString code);
+ static QString dedent(const QString &code);
+ static void prependCode(QString *code, QString firstLine);
};
class CustomFunction : public CodeSnipAbstract
@@ -510,8 +514,8 @@ public:
explicit DocModification(TypeSystem::DocModificationMode mode, const QString& signature) :
m_signature(signature), m_mode(mode) {}
- void setCode(const QString& code) { m_code = code; }
- void setCode(const QStringRef& code) { m_code = code.toString(); }
+ void setCode(const QString& code);
+ void setCode(const QStringRef& code) { setCode(code.toString()); }
QString code() const
{
diff --git a/sources/shiboken2/ApiExtractor/typesystem_enums.h b/sources/shiboken2/ApiExtractor/typesystem_enums.h
index 120c9417f..f6b4b6fa6 100644
--- a/sources/shiboken2/ApiExtractor/typesystem_enums.h
+++ b/sources/shiboken2/ApiExtractor/typesystem_enums.h
@@ -36,21 +36,9 @@ enum Language {
TargetLangCode = 0x0001,
NativeCode = 0x0002,
ShellCode = 0x0004,
- ShellDeclaration = 0x0008,
- PackageInitializer = 0x0010,
- DestructorFunction = 0x0020,
- Constructors = 0x0040,
- Interface = 0x0080,
// masks
- All = TargetLangCode
- | NativeCode
- | ShellCode
- | ShellDeclaration
- | PackageInitializer
- | Constructors
- | Interface
- | DestructorFunction,
+ All = TargetLangCode | NativeCode | ShellCode,
TargetLangAndNativeCode = TargetLangCode | NativeCode
};
@@ -72,12 +60,7 @@ enum Ownership {
enum CodeSnipPosition {
CodeSnipPositionBeginning,
CodeSnipPositionEnd,
- CodeSnipPositionAfterThis,
- // QtScript
CodeSnipPositionDeclaration,
- CodeSnipPositionPrototypeInitialization,
- CodeSnipPositionConstructorInitialization,
- CodeSnipPositionConstructor,
CodeSnipPositionAny,
CodeSnipPositionInvalid
};
diff --git a/sources/shiboken2/ApiExtractor/typesystemparser.cpp b/sources/shiboken2/ApiExtractor/typesystemparser.cpp
index 9fdf81821..0c4d43e76 100644
--- a/sources/shiboken2/ApiExtractor/typesystemparser.cpp
+++ b/sources/shiboken2/ApiExtractor/typesystemparser.cpp
@@ -149,7 +149,7 @@ static QString extractSnippet(const QString &code, const QString &snippetLabel)
} else if (useLine)
result += line.toString() + QLatin1Char('\n');
}
- return result;
+ return CodeSnipAbstract::fixSpaces(result);
}
template <class EnumType, Qt::CaseSensitivity cs = Qt::CaseInsensitive>
@@ -207,13 +207,8 @@ ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive,
languageFromAttribute, TypeSystem::NoLanguage)
{
{u"all", TypeSystem::All}, // sorted!
- {u"constructors", TypeSystem::Constructors},
- {u"destructor-function", TypeSystem::DestructorFunction},
- {u"interface", TypeSystem::Interface},
- {u"library-initializer", TypeSystem::PackageInitializer},
{u"native", TypeSystem::NativeCode}, // em algum lugar do cpp
{u"shell", TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe
- {u"shell-declaration", TypeSystem::ShellDeclaration},
{u"target", TypeSystem::TargetLangCode} // em algum lugar do cpp
};
ENUM_LOOKUP_BINARY_SEARCH()
@@ -272,10 +267,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive,
{
{u"beginning", TypeSystem::CodeSnipPositionBeginning},
{u"end", TypeSystem::CodeSnipPositionEnd},
- {u"declaration", TypeSystem::CodeSnipPositionDeclaration},
- {u"prototype-initialization", TypeSystem::CodeSnipPositionPrototypeInitialization},
- {u"constructor-initialization", TypeSystem::CodeSnipPositionConstructorInitialization},
- {u"constructor", TypeSystem::CodeSnipPositionConstructor}
+ {u"declaration", TypeSystem::CodeSnipPositionDeclaration}
};
ENUM_LOOKUP_LINEAR_SEARCH()
@@ -2475,7 +2467,7 @@ bool TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes, CodeSni
"// START of custom code block [file: "
<< source << "]\n"
<< extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel)
- << "\n// END of custom code block [file: " << source
+ << "// END of custom code block [file: " << source
<< "]\n// ========================================================================\n";
snip->addCode(content);
return true;
@@ -2520,19 +2512,8 @@ bool TypeSystemParser::parseInjectCode(const QXmlStreamReader &,
snip.position = position;
snip.language = lang;
- if (snip.language == TypeSystem::Interface
- && topElement.type != StackElement::InterfaceTypeEntry) {
- m_error = QLatin1String("Interface code injections must be direct child of an interface type entry");
- return false;
- }
-
if (topElement.type == StackElement::ModifyFunction
|| topElement.type == StackElement::AddFunction) {
- if (snip.language == TypeSystem::ShellDeclaration) {
- m_error = QLatin1String("no function implementation in shell declaration in which to inject code");
- return false;
- }
-
FunctionModification &mod = m_contextStack.top()->functionMods.last();
mod.snips << snip;
if (!snip.code().isEmpty())
diff --git a/sources/shiboken2/doc/typesystem_codeinjection.rst b/sources/shiboken2/doc/typesystem_codeinjection.rst
index c891fd2cd..684630dd4 100644
--- a/sources/shiboken2/doc/typesystem_codeinjection.rst
+++ b/sources/shiboken2/doc/typesystem_codeinjection.rst
@@ -47,71 +47,74 @@ inject-code tag
The following table describes the semantics of ``inject-code`` tag as used on
|project|.
- +---------------+------+---------+--------------------------------------------------------------+
- |Parent Tag |Class |Position |Meaning |
- +===============+======+=========+==============================================================+
- |value-type, |native|beginning|Write to the beginning of a class wrapper ``.cpp`` file, right|
- |object-type | | |after the ``#include`` clauses. A common use would be to write|
- | | | |prototypes for custom functions whose definitions are put on a|
- | | | |``native/end`` code injection. |
- | | +---------+--------------------------------------------------------------+
- | | |end |Write to the end of a class wrapper ``.cpp`` file. Could be |
- | | | |used to write custom/helper functions definitions for |
- | | | |prototypes declared on ``native/beginning``. |
- | +------+---------+--------------------------------------------------------------+
- | |target|beginning|Put custom code on the beginning of the wrapper initializer |
- | | | |function (``init_CLASS(PyObject *module)``). This could be |
- | | | |used to manipulate the ``PyCLASS_Type`` structure before |
- | | | |registering it on Python. |
- | | +---------+--------------------------------------------------------------+
- | | |end |Write the given custom code at the end of the class wrapper |
- | | | |initializer function (``init_CLASS(PyObject *module)``). The |
- | | | |code here will be executed after all the wrapped class |
- | | | |components have been initialized. |
- +---------------+------+---------+--------------------------------------------------------------+
- |modify-function|native|beginning|Code here is put on the virtual method override of a C++ |
- | | | |wrapper class (the one responsible for passing C++ calls to a |
- | | | |Python override, if there is any), right after the C++ |
- | | | |arguments have been converted but before the Python call. |
- | | +---------+--------------------------------------------------------------+
- | | |end |This code injection is put in a virtual method override on the|
- | | | |C++ wrapper class, after the call to Python and before |
- | | | |dereferencing the Python method and tuple of arguments. |
- | +------+---------+--------------------------------------------------------------+
- | |target|beginning|This code is injected on the Python method wrapper |
- | | | |(``PyCLASS_METHOD(...)``), right after the decisor have found |
- | | | |which signature to call and also after the conversion of the |
- | | | |arguments to be used, but before the actual call. |
- | | +---------+--------------------------------------------------------------+
- | | |end |This code is injected on the Python method wrapper |
- | | | |(``PyCLASS_METHOD(...)``), right after the C++ method call, |
- | | | |but still inside the scope created by the overload for each |
- | | | |signature. |
- | +------+---------+--------------------------------------------------------------+
- | |shell |beginning|Used only for virtual functions. The code is injected when the|
- | | | |function does not has a Python implementation, then the code |
- | | | |is inserted before c++ call |
- | | +---------+--------------------------------------------------------------+
- | | |end |Same as above, but the code is inserted after c++ call |
- +---------------+------+---------+--------------------------------------------------------------+
- |typesystem |native|beginning|Write code to the beginning of the module ``.cpp`` file, right|
- | | | |after the ``#include`` clauses. This position has a similar |
- | | | |purpose as the ``native/beginning`` position on a wrapper |
- | | | |class ``.cpp`` file, namely write function prototypes, but not|
- | | | |restricted to this use. |
- | | +---------+--------------------------------------------------------------+
- | | |end |Write code to the end of the module ``.cpp`` file. Usually |
- | | | |implementations for function prototypes inserted at the |
- | | | |beginning of the file with a ``native/beginning`` code |
- | | | |injection. |
- | +------+---------+--------------------------------------------------------------+
- | |target|beginning|Insert code at the start of the module initialization function|
- | | | |(``initMODULENAME()``), before the calling ``Py_InitModule``. |
- | | +---------+--------------------------------------------------------------+
- | | |end |Insert code at the end of the module initialization function |
- | | | |(``initMODULENAME()``), but before the checking that emits a |
- | | | |fatal error in case of problems importing the module. |
- +---------------+------+---------+--------------------------------------------------------------+
+ +---------------+------+-----------+--------------------------------------------------------------+
+ |Parent Tag |Class |Position |Meaning |
+ +===============+======+===========+==============================================================+
+ |value-type, |native|beginning |Write to the beginning of a class wrapper ``.cpp`` file, right|
+ |object-type | | |after the ``#include`` clauses. A common use would be to write|
+ | | | |prototypes for custom functions whose definitions are put on a|
+ | | | |``native/end`` code injection. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |Write to the end of a class wrapper ``.cpp`` file. Could be |
+ | | | |used to write custom/helper functions definitions for |
+ | | | |prototypes declared on ``native/beginning``. |
+ | +------+-----------+--------------------------------------------------------------+
+ | |target|beginning |Put custom code on the beginning of the wrapper initializer |
+ | | | |function (``init_CLASS(PyObject *module)``). This could be |
+ | | | |used to manipulate the ``PyCLASS_Type`` structure before |
+ | | | |registering it on Python. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |Write the given custom code at the end of the class wrapper |
+ | | | |initializer function (``init_CLASS(PyObject *module)``). The |
+ | | | |code here will be executed after all the wrapped class |
+ | | | |components have been initialized. |
+ +---------------+------+-----------+--------------------------------------------------------------+
+ |modify-function|native|beginning |Code here is put on the virtual method override of a C++ |
+ | | | |wrapper class (the one responsible for passing C++ calls to a |
+ | | | |Python override, if there is any), right after the C++ |
+ | | | |arguments have been converted but before the Python call. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |This code injection is put in a virtual method override on the|
+ | | | |C++ wrapper class, after the call to Python and before |
+ | | | |dereferencing the Python method and tuple of arguments. |
+ | +------+-----------+--------------------------------------------------------------+
+ | |target|beginning |This code is injected on the Python method wrapper |
+ | | | |(``PyCLASS_METHOD(...)``), right after the decisor have found |
+ | | | |which signature to call and also after the conversion of the |
+ | | | |arguments to be used, but before the actual call. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |This code is injected on the Python method wrapper |
+ | | | |(``PyCLASS_METHOD(...)``), right after the C++ method call, |
+ | | | |but still inside the scope created by the overload for each |
+ | | | |signature. |
+ | +------+-----------+--------------------------------------------------------------+
+ | |shell |declaration|Used only for virtual functions. This code is injected at the |
+ | | | |top. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |beginning |Used only for virtual functions. The code is injected when the|
+ | | | |function does not has a Python implementation, then the code |
+ | | | |is inserted before c++ call |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |Same as above, but the code is inserted after c++ call |
+ +---------------+------+-----------+--------------------------------------------------------------+
+ |typesystem |native|beginning |Write code to the beginning of the module ``.cpp`` file, right|
+ | | | |after the ``#include`` clauses. This position has a similar |
+ | | | |purpose as the ``native/beginning`` position on a wrapper |
+ | | | |class ``.cpp`` file, namely write function prototypes, but not|
+ | | | |restricted to this use. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |Write code to the end of the module ``.cpp`` file. Usually |
+ | | | |implementations for function prototypes inserted at the |
+ | | | |beginning of the file with a ``native/beginning`` code |
+ | | | |injection. |
+ | +------+-----------+--------------------------------------------------------------+
+ | |target|beginning |Insert code at the start of the module initialization function|
+ | | | |(``initMODULENAME()``), before the calling ``Py_InitModule``. |
+ | | +-----------+--------------------------------------------------------------+
+ | | |end |Insert code at the end of the module initialization function |
+ | | | |(``initMODULENAME()``), but before the checking that emits a |
+ | | | |fatal error in case of problems importing the module. |
+ +---------------+------+-----------+--------------------------------------------------------------+
Anatomy of Code Injection
diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp
index 88ba1f04f..04658eff3 100644
--- a/sources/shiboken2/generator/generator.cpp
+++ b/sources/shiboken2/generator/generator.cpp
@@ -540,37 +540,12 @@ void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFuncti
QTextStream &formatCode(QTextStream &s, const QString &code, Indentor &indentor)
{
- // detect number of spaces before the first character
- const QStringList lst(code.split(QLatin1Char('\n')));
- static const QRegularExpression nonSpaceRegex(QStringLiteral("[^\\s]"));
- Q_ASSERT(nonSpaceRegex.isValid());
- int spacesToRemove = 0;
- for (const QString &line : lst) {
- if (!line.trimmed().isEmpty()) {
- spacesToRemove = line.indexOf(nonSpaceRegex);
- if (spacesToRemove == -1)
- spacesToRemove = 0;
- break;
- }
- }
-
- static const QRegularExpression emptyLine(QStringLiteral("^\\s*[\\r]?[\\n]?\\s*$"));
- Q_ASSERT(emptyLine.isValid());
-
- for (QString line : lst) {
- if (!line.isEmpty() && !emptyLine.match(line).hasMatch()) {
- while (line.constEnd()->isSpace())
- line.chop(1);
- int limit = 0;
- for(int i = 0; i < spacesToRemove; ++i) {
- if (!line[i].isSpace())
- break;
- limit++;
- }
-
- s << indentor << line.remove(0, limit);
- }
- s << Qt::endl;
+ const auto lines= code.splitRef(QLatin1Char('\n'));
+ for (const auto &line : lines) {
+ // Do not indent preprocessor lines
+ if (!line.isEmpty() && !line.startsWith(QLatin1Char('#')))
+ s << indentor;
+ s << line << '\n';
}
return s;
}
diff --git a/sources/shiboken2/generator/main.cpp b/sources/shiboken2/generator/main.cpp
index 476e176d3..3c9d13b48 100644
--- a/sources/shiboken2/generator/main.cpp
+++ b/sources/shiboken2/generator/main.cpp
@@ -42,7 +42,7 @@
#include "headergenerator.h"
#include "qtdocgenerator.h"
-#ifdef _WINDOWS
+#ifdef Q_OS_WIN
static const QChar pathSplitter = QLatin1Char(';');
#else
static const QChar pathSplitter = QLatin1Char(':');
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 4c637e701..0ad4fff3b 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -312,6 +312,7 @@ void CppGenerator::generateClass(QTextStream &s, const GeneratorContext &classCo
s << "#include <pysidesignal.h>\n"
<< "#include <pysideproperty.h>\n"
<< "#include <pyside.h>\n"
+ << "#include <pysideqenum.h>\n"
<< "#include <qapp_macro.h>\n\n"
<< "QT_WARNING_DISABLE_DEPRECATED\n\n";
}
@@ -784,6 +785,47 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio
+ typeEntry->qualifiedCppName() + QLatin1String(" >())->tp_name");
}
+// When writing an overridden method of a wrapper class, write the part
+// calling the C++ function in case no overload in Python exists.
+void CppGenerator::writeVirtualMethodCppCall(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const QString &funcName,
+ const CodeSnipList &snips,
+ const AbstractMetaArgument *lastArg,
+ const TypeEntry *retType,
+ const DefaultValue &defaultReturnExpr)
+{
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::ShellCode, func, lastArg);
+ }
+
+ if (func->isAbstract()) {
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"
+ << func->ownerClass()->name() << '.' << funcName
+ << "()' not implemented.\");\n";
+ s << INDENT << "return";
+ if (retType)
+ s << ' ' << defaultReturnExpr.returnValue();
+ s << ";\n";
+ return;
+ }
+
+ s << INDENT;
+ if (retType)
+ s << "return ";
+ s << "this->::" << func->implementingClass()->qualifiedCppName() << "::";
+ writeFunctionCall(s, func, Generator::VirtualCall);
+ s << ";\n";
+ if (retType)
+ return;
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::ShellCode, func, lastArg);
+ }
+ s << INDENT << "return;\n";
+}
+
void CppGenerator::writeVirtualMethodNative(QTextStream &s,
const AbstractMetaFunction *func,
int cacheIndex)
@@ -802,10 +844,10 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
Indentation indentation(INDENT);
+ const FunctionModificationList &functionModifications = func->modifications();
DefaultValue defaultReturnExpr;
if (retType) {
- const FunctionModificationList &mods = func->modifications();
- for (const FunctionModification &mod : mods) {
+ for (const FunctionModification &mod : functionModifications) {
for (const ArgumentModification &argMod : mod.argument_mods) {
if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) {
static const QRegularExpression regex(QStringLiteral("%(\\d+)"));
@@ -853,16 +895,17 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
return;
}
+ const CodeSnipList snips = func->hasInjectedCode()
+ ? func->injectedCodeSnips() : CodeSnipList();
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : func->arguments().constLast();
+
//Write declaration/native injected code
- if (func->hasInjectedCode()) {
- CodeSnipList snips = func->injectedCodeSnips();
- const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast();
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, func, lastArg);
- s << Qt::endl;
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::ShellCode, func, lastArg);
}
- // PYSIDE-803: Build a boolean cache for unused overrides.
- bool multi_line = retType == nullptr; // set to true when using instrumentation
if (wrapperDiagnostics()) {
s << INDENT << "std::cerr << ";
#ifndef Q_CC_MSVC // g++ outputs __FUNCTION__ unqualified
@@ -872,22 +915,13 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
<< cacheIndex << R"( << "]=" << m_PyMethodCache[)" << cacheIndex
<< R"(] << '\n';)" << '\n';
}
+ // PYSIDE-803: Build a boolean cache for unused overrides.
+ const bool multi_line = retType == nullptr || !snips.isEmpty() || func->isAbstract();
s << INDENT << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n");
{
Indentation indentation(INDENT);
- s << INDENT;
- if (retType)
- s << "return ";
- if (!func->isAbstract()) {
- s << "this->::" << func->implementingClass()->qualifiedCppName() << "::";
- writeFunctionCall(s, func, Generator::VirtualCall);
- } else {
- if (retType)
- s << ' ' << defaultReturnExpr.returnValue();
- }
- if (!retType)
- s << ";\n" << INDENT << "return";
- s << ";\n";
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ defaultReturnExpr);
}
if (multi_line)
s << INDENT << "}\n";
@@ -907,36 +941,12 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n";
{
Indentation indentation(INDENT);
- CodeSnipList snips;
- if (func->hasInjectedCode()) {
- snips = func->injectedCodeSnips();
- const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast();
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::ShellCode, func, lastArg);
- s << Qt::endl;
- }
-
- if (func->isAbstract()) {
- s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '";
- s << func->ownerClass()->name() << '.' << funcName;
- s << "()' not implemented.\");\n";
- s << INDENT << "return";
- if (retType)
- s << ' ' << defaultReturnExpr.returnValue();
- } else {
- s << INDENT << "gil.release();\n";
- if (useOverrideCaching(func->ownerClass())) {
- s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
- s << INDENT;
- }
- if (retType)
- s << "return ";
- s << "this->::" << func->implementingClass()->qualifiedCppName() << "::";
- writeFunctionCall(s, func, Generator::VirtualCall);
- if (!retType)
- s << ";\n" << INDENT << "return";
- }
+ s << INDENT << "gil.release();\n";
+ if (useOverrideCaching(func->ownerClass()))
+ s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ defaultReturnExpr);
}
- s << ";\n";
s << INDENT << "}\n\n"; //WS
writeConversionRule(s, func, TypeSystem::TargetLangCode);
@@ -970,8 +980,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
convert = !m_formatUnits.contains(argType->name());
}
- Indentation indentation(INDENT);
- ac << INDENT;
+ Indentor nested;
+ Indentation indentation(nested);
+ ac << nested;
if (!func->conversionRule(TypeSystem::TargetLangCode, arg->argumentIndex() + 1).isEmpty()) {
// Has conversion rule.
ac << arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX);
@@ -993,8 +1004,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
bool invalidateReturn = false;
QSet<int> invalidateArgs;
- const FunctionModificationList &mods = func->modifications();
- for (const FunctionModification &funcMod : mods) {
+ for (const FunctionModification &funcMod : functionModifications) {
for (const ArgumentModification &argMod : funcMod.argument_mods) {
if (argMod.resetAfterUse && !invalidateArgs.contains(argMod.index)) {
invalidateArgs.insert(argMod.index);
@@ -1007,16 +1017,12 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
}
s << Qt::endl;
- CodeSnipList snips;
- if (func->hasInjectedCode()) {
- snips = func->injectedCodeSnips();
-
+ if (!snips.isEmpty()) {
if (injectedCodeUsesPySelf(func))
s << INDENT << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n";
const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast();
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg);
- s << Qt::endl;
}
if (!injectedCodeCallsPythonOverride(func)) {
@@ -1102,8 +1108,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s,
}
- const FunctionModificationList &funcMods = func->modifications();
- for (const FunctionModification &funcMod : funcMods) {
+ for (const FunctionModification &funcMod : functionModifications) {
for (const ArgumentModification &argMod : funcMod.argument_mods) {
if (argMod.ownerships.contains(TypeSystem::NativeCode)
&& argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) {
@@ -1217,8 +1222,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *
}
QString code;
QTextStream c(&code);
- c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
- << INDENT << " ";
+ Indentor nested;
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " ";
if (enumType->isFlags())
c << cppTypeName << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))";
else
@@ -1231,9 +1237,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *
code.clear();
- c << INDENT << "const int castCppIn = int(*reinterpret_cast<const "
+ c << nested << "const int castCppIn = int(*reinterpret_cast<const "
<< cppTypeName << " *>(cppIn));\n";
- c << INDENT;
+ c << nested;
c << "return ";
if (enumType->isFlags()) {
c << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, "
@@ -1258,8 +1264,8 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *
code.clear();
cppTypeName = getFullTypeName(flags).trimmed();
- c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
- << INDENT << " " << cppTypeName
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " " << cppTypeName
<< "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n";
QString flagsTypeName = fixedCppTypeName(flags);
@@ -1267,9 +1273,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *
writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck);
code.clear();
- c << INDENT << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n";
- c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
- << INDENT << " " << cppTypeName
+ c << nested << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n";
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " " << cppTypeName
<< "(QFlag(int(PyLong_AsLong(pyLong.object()))));\n";
// PYSIDE-898: Include an additional condition to detect if the type of the
// enum corresponds to the object that is being evaluated.
@@ -1316,7 +1322,8 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla
QString targetTypeName = metaClass->name() + QLatin1String("_PTR");
QString code;
QTextStream c(&code);
- c << INDENT << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);";
+ Indentor nested;
+ c << nested << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);";
writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
// "Is convertible" function for the Python object to C++ pointer conversion.
@@ -1330,30 +1337,30 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla
code.clear();
if (usePySideExtensions() && metaClass->isQObject())
{
- c << INDENT << "return PySide::getWrapperForQObject(reinterpret_cast<"
+ c << nested << "return PySide::getWrapperForQObject(reinterpret_cast<"
<< typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
} else {
- c << INDENT << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n";
- c << INDENT << "if (pyOut) {\n";
+ c << nested << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n";
+ c << nested << "if (pyOut) {\n";
{
- Indentation indent(INDENT);
- c << INDENT << "Py_INCREF(pyOut);\n";
- c << INDENT << "return pyOut;\n";
- }
- c << INDENT << "}\n";
- c << INDENT << "bool changedTypeName = false;\n"
- << INDENT << "auto tCppIn = reinterpret_cast<const " << typeName << " *>(cppIn);\n"
- << INDENT << "const char *typeName = typeid(*tCppIn).name();\n"
- << INDENT << "auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName);\n"
- << INDENT << "if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {\n"
- << INDENT << " typeName = typeNameOf(tCppIn);\n"
- << INDENT << " changedTypeName = true;\n"
- << INDENT << " }\n"
- << INDENT << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
+ Indentation indent(nested);
+ c << nested << "Py_INCREF(pyOut);\n";
+ c << nested << "return pyOut;\n";
+ }
+ c << nested << "}\n";
+ c << nested << "bool changedTypeName = false;\n"
+ << nested << "auto tCppIn = reinterpret_cast<const " << typeName << " *>(cppIn);\n"
+ << nested << "const char *typeName = typeid(*tCppIn).name();\n"
+ << nested << "auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName);\n"
+ << nested << "if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {\n"
+ << nested << " typeName = typeNameOf(tCppIn);\n"
+ << nested << " changedTypeName = true;\n"
+ << nested << "}\n"
+ << nested << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
<< ", const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName);\n"
- << INDENT << "if (changedTypeName)\n"
- << INDENT << " delete [] typeName;\n"
- << INDENT << "return result;";
+ << nested << "if (changedTypeName)\n"
+ << nested << " delete [] typeName;\n"
+ << nested << "return result;";
}
std::swap(targetTypeName, sourceTypeName);
writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName);
@@ -1383,7 +1390,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla
computedWrapperName = classContext.smartPointerWrapperName();
}
- c << INDENT << "return Shiboken::Object::newObject(" << cpythonType
+ c << nested << "return Shiboken::Object::newObject(" << cpythonType
<< ", new ::" << computedWrapperName << "(*reinterpret_cast<const "
<< typeName << " *>(cppIn)), true, true);";
writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName);
@@ -1406,7 +1413,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla
else
wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable);
- c << INDENT << "*reinterpret_cast<" << typeName << " *>(cppOut) = *"
+ c << nested << "*reinterpret_cast<" << typeName << " *>(cppOut) = *"
<< wrappedCPtrExpression << ';';
writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
@@ -1475,7 +1482,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla
|| sourceType->typeEntry()->isEnum()
|| sourceType->typeEntry()->isFlags()) {
QTextStream pc(&toCppPreConv);
- pc << INDENT << getFullTypeNameWithoutModifiers(sourceType) << " cppIn";
+ pc << nested << getFullTypeNameWithoutModifiers(sourceType) << " cppIn";
writeMinimalConstructorExpression(pc, sourceType);
pc << ";\n";
writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn"));
@@ -1703,10 +1710,10 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over
}
if (usesNamedArguments && !rfunc->isCallOperator())
- s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);\n";
+ s << INDENT << "const Py_ssize_t numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);\n";
if (initPythonArguments) {
- s << INDENT << "int numArgs = ";
+ s << INDENT << "const Py_ssize_t numArgs = ";
if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData))
s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n";
else
@@ -2859,8 +2866,9 @@ void CppGenerator::writeCppToPythonFunction(QTextStream &s, const QString &code,
static void replaceCppToPythonVariables(QString &code, const QString &typeName)
{
- code.prepend(QLatin1String("auto &cppInRef = *reinterpret_cast<")
- + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));\n"));
+ const QString line = QLatin1String("auto &cppInRef = *reinterpret_cast<")
+ + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));");
+ CodeSnipAbstract::prependCode(&code, line);
code.replace(QLatin1String("%INTYPE"), typeName);
code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject *"));
code.replace(QLatin1String("%in"), QLatin1String("cppInRef"));
@@ -2947,12 +2955,13 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s,
// Python to C++ conversion function.
QString code;
QTextStream c(&code);
+ Indentor nested;
if (conversion.isEmpty())
conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType->typeEntry(), QLatin1String("pyIn"));
if (!preConversion.isEmpty())
- c << INDENT << preConversion << Qt::endl;
+ c << nested << preConversion << Qt::endl;
const QString fullTypeName = getFullTypeName(targetType->typeEntry());
- c << INDENT << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = "
+ c << nested << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = "
<< fullTypeName << '(' << conversion << ");";
QString sourceTypeName = fixedCppTypeName(sourceType);
QString targetTypeName = fixedCppTypeName(targetType);
@@ -3034,11 +3043,10 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, const Abs
}
// Python to C++ conversion function.
QString cppTypeName = getFullTypeNameWithoutModifiers(containerType);
- QString code;
- QTextStream c(&code);
- c << INDENT << "auto &cppOutRef = *reinterpret_cast<"
- << cppTypeName << " *>(cppOut);\n";
- code.append(toCppConversions.constFirst()->conversion());
+ QString code = toCppConversions.constFirst()->conversion();
+ const QString line = QLatin1String("auto &cppOutRef = *reinterpret_cast<")
+ + cppTypeName + QLatin1String(" *>(cppOut);");
+ CodeSnipAbstract::prependCode(&code, line);
for (int i = 0; i < containerType->instantiations().count(); ++i) {
const AbstractMetaType *type = containerType->instantiations().at(i);
QString typeName = getFullTypeName(type);
@@ -3232,7 +3240,6 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f
}
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, lastArg);
- s << Qt::endl;
}
writeConversionRule(s, func, TypeSystem::NativeCode);
@@ -3523,10 +3530,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f
}
}
- if (func->hasInjectedCode() && !func->isConstructor()) {
- s << Qt::endl;
+ if (func->hasInjectedCode() && !func->isConstructor())
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func, lastArg);
- }
bool hasReturnPolicy = false;
@@ -4983,8 +4988,13 @@ void CppGenerator::writeSignatureStrings(QTextStream &s,
s << "// Multiple signatures have their index \"n:\" in front.\n";
s << "static const char *" << arrayName << "_SignatureStrings[] = {\n";
QString line;
- while (signatureStream.readLineInto(&line))
- s << INDENT << "R\"CPP(" << line << ")CPP\",\n";
+ while (signatureStream.readLineInto(&line)) {
+ // must anything be escaped?
+ if (line.contains(QLatin1Char('"')) || line.contains(QLatin1Char('\\')))
+ s << INDENT << "R\"CPP(" << line << ")CPP\",\n";
+ else
+ s << INDENT << '"' << line << "\",\n";
+ }
s << INDENT << NULL_PTR << "}; // Sentinel\n\n";
}
@@ -5637,6 +5647,7 @@ bool CppGenerator::finishGeneration()
if (usePySideExtensions()) {
s << includeQDebug;
s << "#include <pyside.h>\n";
+ s << "#include <pysideqenum.h>\n";
s << "#include <qapp_macro.h>\n";
}
@@ -5680,10 +5691,8 @@ bool CppGenerator::finishGeneration()
const CodeSnipList snips = moduleEntry->codeSnips();
// module inject-code native/beginning
- if (!snips.isEmpty()) {
+ if (!snips.isEmpty())
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
- s << Qt::endl;
- }
// cleanup staticMetaObject attribute
if (usePySideExtensions()) {
@@ -5814,10 +5823,8 @@ bool CppGenerator::finishGeneration()
ErrorCode errorCode(QLatin1String("SBK_MODULE_INIT_ERROR"));
// module inject-code target/beginning
- if (!snips.isEmpty()) {
+ if (!snips.isEmpty())
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode);
- s << Qt::endl;
- }
for (const QString &requiredModule : requiredModules) {
s << INDENT << "{\n";
@@ -5919,16 +5926,12 @@ bool CppGenerator::finishGeneration()
s << INDENT << "}\n";
// module inject-code target/end
- if (!snips.isEmpty()) {
+ if (!snips.isEmpty())
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
- s << Qt::endl;
- }
// module inject-code native/end
- if (!snips.isEmpty()) {
+ if (!snips.isEmpty())
writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
- s << Qt::endl;
- }
if (usePySideExtensions()) {
for (AbstractMetaEnum *metaEnum : qAsConst(globalEnums))
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h
index 4e995d56f..16ee412c9 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.h
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h
@@ -60,7 +60,10 @@ private:
QString getVirtualFunctionReturnTypeName(const AbstractMetaFunction *func);
void writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction *func, int cacheIndex);
-
+ void writeVirtualMethodCppCall(QTextStream &s, const AbstractMetaFunction *func,
+ const QString &funcName, const CodeSnipList &snips,
+ const AbstractMetaArgument *lastArg, const TypeEntry *retType,
+ const DefaultValue &defaultReturnExpr);
void writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext);
void writeMetaCast(QTextStream &s, const GeneratorContext &classContext);
diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
index fe0d2765c..6abaef698 100644
--- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
@@ -1755,7 +1755,7 @@ void ShibokenGenerator::writeClassCodeSnips(QTextStream &s,
processClassCodeSnip(code, context);
s << INDENT << "// Begin code injection\n";
s << code;
- s << INDENT << "// End of code injection\n";
+ s << INDENT << "// End of code injection\n\n";
}
void ShibokenGenerator::writeCodeSnips(QTextStream &s,
@@ -1769,7 +1769,7 @@ void ShibokenGenerator::writeCodeSnips(QTextStream &s,
processCodeSnip(code);
s << INDENT << "// Begin code injection\n";
s << code;
- s << INDENT << "// End of code injection\n";
+ s << INDENT << "// End of code injection\n\n";
}
void ShibokenGenerator::writeCodeSnips(QTextStream &s,
@@ -1996,7 +1996,7 @@ void ShibokenGenerator::writeCodeSnips(QTextStream &s,
processCodeSnip(code);
s << INDENT << "// Begin code injection\n";
s << code;
- s << INDENT << "// End of code injection\n";
+ s << INDENT << "// End of code injection\n\n";
}
// Returns true if the string is an expression,
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp
index 443d25cdf..d7184569b 100644
--- a/sources/shiboken2/libshiboken/basewrapper.cpp
+++ b/sources/shiboken2/libshiboken/basewrapper.cpp
@@ -486,7 +486,7 @@ void SbkObjectTypeDealloc(PyObject *pyObj)
}
}
-PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
+static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
// Check if all bases are new style before calling type.tp_new
// Was causing gc assert errors in test_bug704.py when
@@ -513,7 +513,8 @@ PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *k
#ifndef IS_PY3K
if (PyClass_Check(baseType)) {
PyErr_Format(PyExc_TypeError, "Invalid base class used in type %s. "
- "PySide only support multiple inheritance from python new style class.", metatype->tp_name);
+ "PySide only supports multiple inheritance from Python new style classes.",
+ metatype->tp_name);
return 0;
}
#endif
@@ -579,7 +580,6 @@ PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *k
if (PepType_SOTP(base)->subtype_init)
PepType_SOTP(base)->subtype_init(newType, args, kwds);
}
-
return reinterpret_cast<PyObject *>(newType);
}
diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp
index f07cac613..5e0053e2e 100644
--- a/sources/shiboken2/libshiboken/pep384impl.cpp
+++ b/sources/shiboken2/libshiboken/pep384impl.cpp
@@ -668,6 +668,22 @@ PyImport_GetModule(PyObject *name)
}
#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API)
+
+/*****************************************************************************
+ *
+ * Python 2 incompatibilities
+ *
+ * This is incompatibly implemented as macro in Python 2.
+ */
+#if PY_VERSION_HEX < 0x03000000
+
+PyObject *PepMapping_Items(PyObject *o)
+{
+ return PyObject_CallMethod(o, const_cast<char *>("items"), NULL);
+}
+
+#endif
+
/*****************************************************************************
*
* Extra support for name mangling
diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h
index 541b0e775..2bfe52254 100644
--- a/sources/shiboken2/libshiboken/pep384impl.h
+++ b/sources/shiboken2/libshiboken/pep384impl.h
@@ -533,6 +533,18 @@ LIBSHIBOKEN_API PyObject *PyImport_GetModule(PyObject *name);
/*****************************************************************************
*
+ * Python 2 incompatibilities
+ *
+ * This is incompatibly implemented as macro in Python 2.
+ */
+#if PY_VERSION_HEX < 0x03000000
+extern LIBSHIBOKEN_API PyObject *PepMapping_Items(PyObject *o);
+#else
+#define PepMapping_Items PyMapping_Items
+#endif
+
+/*****************************************************************************
+ *
* Runtime support for Python 3.8 incompatibilities
*
*/
diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp
index f9a43845a..369b264e7 100644
--- a/sources/shiboken2/libshiboken/sbkenum.cpp
+++ b/sources/shiboken2/libshiboken/sbkenum.cpp
@@ -608,11 +608,16 @@ newItem(PyTypeObject *enumType, long itemValue, const char *itemName)
enumObj->ob_value = itemValue;
if (newValue) {
- PyObject *values = PyDict_GetItem(enumType->tp_dict, Shiboken::PyName::values());
- if (!values) {
- values = PyDict_New();
- PyDict_SetItem(enumType->tp_dict, Shiboken::PyName::values(), values);
- Py_DECREF(values); // ^ values still alive, because setitem increfs it
+ auto dict = enumType->tp_dict; // Note: 'values' is borrowed
+ PyObject *values = PyDict_GetItemWithError(dict, Shiboken::PyName::values());
+ if (values == nullptr) {
+ if (PyErr_Occurred())
+ return nullptr;
+ Shiboken::AutoDecRef new_values(values = PyDict_New());
+ if (values == nullptr)
+ return nullptr;
+ if (PyDict_SetItem(dict, Shiboken::PyName::values(), values) < 0)
+ return nullptr;
}
PyDict_SetItemString(values, itemName, reinterpret_cast<PyObject *>(enumObj));
}
diff --git a/sources/shiboken2/libshiboken/sbkpython.h b/sources/shiboken2/libshiboken/sbkpython.h
index 9dd1e712e..6755e945d 100644
--- a/sources/shiboken2/libshiboken/sbkpython.h
+++ b/sources/shiboken2/libshiboken/sbkpython.h
@@ -41,6 +41,7 @@
#define SBKPYTHON_H
#include "sbkversion.h"
+#define PyEnumMeta_Check(x) (strcmp(Py_TYPE(x)->tp_name, "EnumMeta") == 0)
// Qt's "slots" macro collides with the "slots" member variables
// used in some Python structs. For compilers that support push_macro,
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
index c19665176..541d74918 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
@@ -55,11 +55,15 @@ namespace PyName {
STATIC_STRING_IMPL(dumps, "dumps")
STATIC_STRING_IMPL(loads, "loads")
STATIC_STRING_IMPL(result, "result")
+STATIC_STRING_IMPL(value, "value")
STATIC_STRING_IMPL(values, "values")
// Internal:
STATIC_STRING_IMPL(classmethod, "classmethod")
+STATIC_STRING_IMPL(co_name, "co_name")
STATIC_STRING_IMPL(compile, "compile");
+STATIC_STRING_IMPL(f_code, "f_code")
+STATIC_STRING_IMPL(f_lineno, "f_lineno")
STATIC_STRING_IMPL(function, "function")
STATIC_STRING_IMPL(marshal, "marshal")
STATIC_STRING_IMPL(method, "method")
@@ -73,6 +77,7 @@ namespace PyMagicName {
STATIC_STRING_IMPL(class_, "__class__")
STATIC_STRING_IMPL(ecf, "__ecf__")
STATIC_STRING_IMPL(file, "__file__")
+STATIC_STRING_IMPL(members, "__members__")
STATIC_STRING_IMPL(module, "__module__")
STATIC_STRING_IMPL(name, "__name__")
STATIC_STRING_IMPL(qualname, "__qualname__")
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h
index 07d6cc60a..4078d163c 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.h
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h
@@ -48,9 +48,13 @@ namespace Shiboken
// Some often-used strings
namespace PyName
{
+LIBSHIBOKEN_API PyObject *co_name();
LIBSHIBOKEN_API PyObject *dumps();
+LIBSHIBOKEN_API PyObject *f_code();
+LIBSHIBOKEN_API PyObject *f_lineno();
LIBSHIBOKEN_API PyObject *loads();
LIBSHIBOKEN_API PyObject *result();
+LIBSHIBOKEN_API PyObject *value();
LIBSHIBOKEN_API PyObject *values();
} // namespace PyName
@@ -59,6 +63,7 @@ namespace PyMagicName
LIBSHIBOKEN_API PyObject *class_();
LIBSHIBOKEN_API PyObject *ecf();
LIBSHIBOKEN_API PyObject *file();
+LIBSHIBOKEN_API PyObject *members();
LIBSHIBOKEN_API PyObject *module();
LIBSHIBOKEN_API PyObject *name();
LIBSHIBOKEN_API PyObject *qualname();
diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
index 3cd318ceb..5754b3047 100644
--- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
@@ -44,15 +44,15 @@
<inject-code class="native" position="beginning">
static bool Check2TupleOfNumbers(PyObject* pyIn) {
- if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))
- return false;
- Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0));
- if (!SbkNumber_Check(pyReal))
- return false;
- Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));
- if (!SbkNumber_Check(pyImag))
- return false;
- return true;
+ if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))
+ return false;
+ Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0));
+ if (!SbkNumber_Check(pyReal))
+ return false;
+ Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));
+ if (!SbkNumber_Check(pyImag))
+ return false;
+ return true;
}
</inject-code>
<primitive-type name="Complex" target-lang-api-name="PyComplex">
@@ -70,8 +70,8 @@
%out = %OUTTYPE(real, imag);
</add-conversion>
<add-conversion type="PySequence" check="Check2TupleOfNumbers(%in)">
- Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0));
- Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1));
+ Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0));
+ Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1));
double real = %CONVERTTOCPP[double](pyReal);
double imag = %CONVERTTOCPP[double](pyImag);
%out = %OUTTYPE(real, imag);