]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
[bos #35148][EDF] (2023-T1) Evaluation of PySide. Added an ability to build SalomePyQ... kleontev/35148_evaluation_of_pyside 16/head
authorKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Wed, 29 Nov 2023 11:38:32 +0000 (11:38 +0000)
committerKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Wed, 13 Dec 2023 11:03:06 +0000 (11:03 +0000)
CMakeLists.txt
src/SALOME_PYQT/SalomePyQt/CMakeLists.txt
src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.h [new file with mode: 0644]
src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.xml [new file with mode: 0644]
src/SALOME_PYQT/SalomePyQt/pyside2_config.py [new file with mode: 0644]
src/SalomeApp/salome_pluginsmanager.py

index a278e3ef47f1217436dbd529bdb0ba59b9900897..3f81650dd78cd46db470d33a1470a9f71f9c6e3e 100644 (file)
@@ -84,6 +84,8 @@ OPTION(SALOME_USE_PYCONSOLE "Enable Python GUI interface (Mandatory in classic c
 OPTION(SALOME_USE_QXGRAPHVIEWER "Enable QX graph visualization (Mandatory in classic configurations)" ON)
 OPTION(SALOME_USE_PVVIEWER "Enable ParaView visualization (Mandatory in classic configurations)" ON)
 
+OPTION(SALOME_USE_PYSIDE "Use PySide2 to create Python bindings for Qt" ON)
+
 CMAKE_DEPENDENT_OPTION(SALOME_USE_SALOMEOBJECT "Enable Salome Object (Mandatory in classic configurations)" ON
                        "SALOME_LIGHT_ONLY" ON)
 
index 35603b9d5b4b42c1163567f54975b7fbfa0fd4bd..8c77ebfae699f117aa8323faf88a517d72c6b3d6 100644 (file)
@@ -100,31 +100,210 @@ SET(_moc_HEADERS SalomePyQt.h)
 
 # --- resources ---
 
-# sip files / to be processed by sip
-SET(_sip_files SalomePyQt.sip)
-IF(SALOME_USE_PLOT2DVIEWER)
-  LIST(APPEND _sip_files SalomePyQt_Plot2d.sip)
-ENDIF(SALOME_USE_PLOT2DVIEWER)
-
-# --- sources ---
-
 # sources / moc wrappings
 QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS})
 
-# sources / sip wrappings
-PYQT_WRAP_SIP(_sip_SOURCES ${_sip_files})
-
 # sources / static
 SET(_other_SOURCES SalomePyQt.cxx)
 
-# sources / to compile
-SET(SalomePyQt_SOURCES ${_other_SOURCES} ${_moc_SOURCES} ${_sip_SOURCES})
+# --- PyQt5/PySide switch ---
+
+IF(SALOME_USE_PYSIDE)
+
+  # Most of the code below is a modified version of SampleBinding Example from PySide2 repo.
+  MESSAGE(STATUS "SALOME_USE_PYSIDE is defined. Start compilation with PySide2.")
+
+  # ======================== General Configuration ==============================================
+
+  # The names sample_library and bindings_library below are from Shiboken SampleBinding Example.
+  # They used here for easy comparing with example during testing period.
+  SET(lib_base_name "SalomePyQt")
+
+  # The SalomePyQt library for which we will create bindings.
+  SET(sample_library "lib${lib_base_name}")
+
+  # The name of the generated bindings module (as imported by Python).
+  SET(bindings_library "${lib_base_name}")
+
+  # The header file with all the types and functions for which bindings will be generated.
+  SET(wrapped_header ${CMAKE_CURRENT_SOURCE_DIR}/${lib_base_name}_bindings.h)
+
+  # The typesystem xml file which defines the relationships between the C++ types / functions
+  # and the corresponding Python equivalents.
+  SET(typesystem_file ${CMAKE_CURRENT_SOURCE_DIR}/${lib_base_name}_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.
+  # All generated files go out in lower case, so we need to lower them here to prevent
+  # file not found error in case of using just original names.
+  STRING(TOLOWER ${bindings_library} bindings_library_lowercase)
+  SET(generated_sources
+    ${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/${bindings_library_lowercase}_wrapper.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/${bindings_library}/${bindings_library_lowercase}_module_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_CURRENT_SOURCE_DIR}/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(--python-include-path python_include_dir)
+  pyside2_config(--shiboken2-generator-include-path shiboken2_include_dir 1)
+  pyside2_config(--shiboken2-module-shared-libraries-cmake shiboken2_shared_libraries 0)
+  pyside2_config(--python-link-flags-cmake python_linking_data 0)
+  pyside2_config(--pyside2-path pyside2-path)
+
+  MESSAGE(STATUS "shiboken2_module_path: ${shiboken2_module_path}")
+  MESSAGE(STATUS "shiboken2_generator_path: ${shiboken2_generator_path}")
+  MESSAGE(STATUS "python_include_dir: ${python_include_dir}")
+  MESSAGE(STATUS "shiboken2_include_dir: ${shiboken2_include_dir}")
+  MESSAGE(STATUS "shiboken2_shared_libraries: ${shiboken2_shared_libraries}")
+  MESSAGE(STATUS "python_linking_data: ${python_linking_data}")
+  MESSAGE(STATUS "pyside2-path: ${pyside2-path}")
+  
+  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 ==============================================
+
+  # 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_BINARY_DIR})
+  SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+
+  # ========================== CMake Target - SalomePyQt ====================================
+
+  # sources / to compile
+  SET(SalomePyQt_SOURCES ${_other_SOURCES} ${_moc_SOURCES})
+
+  ADD_LIBRARY(${sample_library} SHARED ${SalomePyQt_SOURCES})
+  TARGET_LINK_LIBRARIES(${sample_library} ${_link_LIBRARIES})
+  SET_PROPERTY(TARGET ${sample_library} PROPERTY PREFIX "")
 
-# --- rules ---
+  # Needed mostly on Windows to export symbols, and create a .lib file, otherwise the binding
+  # library can't link to the sample library.
+  TARGET_COMPILE_DEFINITIONS(${sample_library} PRIVATE BINDINGS_BUILD)
 
-ADD_LIBRARY(SalomePyQt MODULE ${SalomePyQt_SOURCES})
-TARGET_LINK_LIBRARIES(SalomePyQt ${_link_LIBRARIES})
-INSTALL(TARGETS SalomePyQt DESTINATION ${SALOME_INSTALL_LIBS})
+  # ========================== Shiboken Target for generating binding C++ files =================
+
+  # Get includes and make a colon-separated string to pass to shiboken options
+  GET_DIRECTORY_PROPERTY(include_directories_list INCLUDE_DIRECTORIES)
+  LIST(JOIN include_directories_list ":" include_directories_string)
+
+  # Set up the options to pass to shiboken.
+  SET(shiboken_options --generator-set=shiboken --enable-parent-ctor-heuristic
+    --enable-return-value-heuristic --use-isnull-as-nb_nonzero
+    --avoid-protected-hack
+    --include-paths=${include_directories_string}
+    -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} MODULE ${${bindings_library}_sources})
+
+  # Prepare pyside2 includes
+  FILE(GLOB_RECURSE pyside2_include_dirs LIST_DIRECTORIES true ${pyside2-path}/include/*)
+  LIST(FILTER pyside2_include_dirs EXCLUDE REGEX \.h$)
+  LIST(APPEND pyside2_include_dirs ${pyside2-path}/include)
+  MESSAGE(STATUS "pyside2_include_dirs: ${pyside2_include_dirs}")
+
+  # Apply relevant include and link flags.
+  TARGET_INCLUDE_DIRECTORIES(${bindings_library} PRIVATE
+    ${python_include_dir}
+    ${shiboken2_include_dir}
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${pyside2_include_dirs}
+  )
+
+  TARGET_LINK_LIBRARIES(${bindings_library} PRIVATE
+    ${shiboken2_shared_libraries}
+    ${sample_library}
+    ${_link_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}")
+
+  # 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} ${sample_library}
+          LIBRARY DESTINATION ${SALOME_INSTALL_LIBS}
+          RUNTIME DESTINATION ${SALOME_INSTALL_LIBS}
+  )
+
+ELSE()
+
+  # sip files / to be processed by sip
+  SET(_sip_files SalomePyQt.sip)
+  IF(SALOME_USE_PLOT2DVIEWER)
+    LIST(APPEND _sip_files SalomePyQt_Plot2d.sip)
+  ENDIF(SALOME_USE_PLOT2DVIEWER)
+
+  # sources / sip wrappings
+  PYQT_WRAP_SIP(_sip_SOURCES ${_sip_files})
+
+  # --- sources ---
+
+  # sources / to compile
+  SET(SalomePyQt_SOURCES ${_other_SOURCES} ${_moc_SOURCES} ${_sip_SOURCES})
+
+  # --- rules ---
+
+  ADD_LIBRARY(SalomePyQt MODULE ${SalomePyQt_SOURCES})
+  TARGET_LINK_LIBRARIES(SalomePyQt ${_link_LIBRARIES})
+  INSTALL(TARGETS SalomePyQt DESTINATION ${SALOME_INSTALL_LIBS})
+
+ENDIF()
 
 IF(WIN32)
   SET_TARGET_PROPERTIES(SalomePyQt PROPERTIES SUFFIX ".pyd" DEBUG_OUTPUT_NAME SalomePyQt_d RELEASE_OUTPUT_NAME SalomePyQt)
diff --git a/src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.h b/src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.h
new file mode 100644 (file)
index 0000000..e2c99f8
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+// File   : SalomePyQt_bindings.h
+// Author : Konstantin LEONTEV
+//
+
+#ifndef SALOME_PYQT_BINDINGS_H
+#define SALOME_PYQT_BINDINGS_H
+
+#include "SalomePyQt.h"
+
+#endif // SALOME_PYQT_BINDINGS_H
diff --git a/src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.xml b/src/SALOME_PYQT/SalomePyQt/SalomePyQt_bindings.xml
new file mode 100644 (file)
index 0000000..31e2521
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
+**
+** Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+** CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License as published by the Free Software Foundation; either
+** version 2.1 of the License, or (at your option) any later version.
+**
+** This library is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public
+** License along with this library; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+**
+** See http:**www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+**
+** File   : SalomePyQt_bindings.h
+** Author : Konstantin LEONTEV
+**
+**
+** It's an experimental file for testing migration from PyQt5 to PySide2.
+**
+****************************************************************************/
+-->
+
+<typesystem package="SalomePyQt">
+
+    <primitive-type name="bool"/>
+
+    <load-typesystem name="typesystem_widgets.xml" generate="no"/>
+    <load-typesystem name="typesystem_core.xml" generate="no"/>
+
+    <!-- The attribute identified-by-value helps to specify anonymous enums using the name
+    of one of their values, which is unique for the anonymous enum scope. -->
+    <enum-type identified-by-value="PT_Auto"/>
+
+    <!-- Helper function to check if SalomePyQt module was built with PySide2 -->
+    <add-function signature="use_pyside()" return-type="bool">
+        <inject-code class="target" position="beginning">
+            Py_RETURN_TRUE;
+        </inject-code>
+    </add-function>
+
+    <!-- The optional attribute generate-functions="..." was introduced only in PySide6,
+    so we don't have a choice here, but generate all the methods by default except explicitly rejected ones. -->
+    <object-type name="SalomePyQt">
+        <!-- By default the ownership of an object created in Python is tied
+        to the Python name pointing to it. In order for the underlying
+        C++ object not to get deleted when the Python name goes out of
+        scope, we have to transfer ownership to C++.
+        -->
+        <modify-function signature="getMainFrame()">
+            <modify-argument index="0">
+                <define-ownership owner="c++"/>
+            </modify-argument>
+        </modify-function>
+
+        <!-- Fix for enum argument. Generator exports PT_Auto argument as SalomePyQt::PT_Auto
+        that leads to ‘PT_Auto’ is not a member of ‘SalomePyQt’ compilation error. -->
+        <!-- <modify-function signature="addPreference"> -->
+        <modify-function signature="addPreference(QString,int,int,QString,QString)">
+            <modify-argument index="3">
+                <!-- Replacement below doesn't work because it generates default value as SalomePyQt::PT_Auto anyway. -->
+                <!-- <replace-default-expression with="PT_Auto"/> -->
+                <replace-default-expression with="0"/>
+            </modify-argument>
+        </modify-function>
+
+        <!-- Prevents error: expected primary-expression before ‘,’ token in line:
+        pyResult = Shiboken::Conversions::copyToPython(, &cppResult); -->
+        <modify-function signature="getActivePythonModule()">
+            <modify-argument index="return">
+                <conversion-rule class="target">
+                    %PYARG_0 = %0;
+                </conversion-rule>
+            </modify-argument>
+        </modify-function>
+
+    </object-type>
+
+</typesystem>
diff --git a/src/SALOME_PYQT/SalomePyQt/pyside2_config.py b/src/SALOME_PYQT/SalomePyQt/pyside2_config.py
new file mode 100644 (file)
index 0000000..b4b7fe7
--- /dev/null
@@ -0,0 +1,378 @@
+#############################################################################
+##
+## Copyright (C) 2019 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 os, glob, re, sys
+from distutils import sysconfig
+
+generic_error = (' Did you forget to activate your virtualenv? Or perhaps'
+                 ' you forgot to build / install PySide2 into your currently active Python'
+                 ' environment?')
+pyside2_error = 'Unable to locate PySide2.' + generic_error
+shiboken2_module_error = 'Unable to locate shiboken2-module.' + generic_error
+shiboken2_generator_error = 'Unable to locate shiboken2-generator.' + generic_error
+pyside2_libs_error = 'Unable to locate the PySide2 shared libraries.' + generic_error
+python_link_error = 'Unable to locate the Python library for linking.'
+python_include_error = 'Unable to locate the Python include headers directory.'
+
+options = []
+
+# option, function, error, description
+options.append(("--shiboken2-module-path",
+                lambda: find_shiboken2_module(),
+                shiboken2_module_error,
+                "Print shiboken2 module location"))
+options.append(("--shiboken2-generator-path",
+                lambda: find_shiboken2_generator(),
+                shiboken2_generator_error,
+                "Print shiboken2 generator location"))
+options.append(("--pyside2-path", lambda: find_pyside2(), pyside2_error,
+                "Print PySide2 location"))
+
+options.append(("--python-include-path",
+                lambda: get_python_include_path(),
+                python_include_error,
+                "Print Python include path"))
+options.append(("--shiboken2-generator-include-path",
+                lambda: get_package_include_path(Package.shiboken2_generator),
+                pyside2_error,
+                "Print shiboken2 generator include paths"))
+options.append(("--pyside2-include-path",
+                lambda: get_package_include_path(Package.pyside2),
+                pyside2_error,
+                "Print PySide2 include paths"))
+
+options.append(("--python-link-flags-qmake", lambda: python_link_flags_qmake(), python_link_error,
+                "Print python link flags for qmake"))
+options.append(("--python-link-flags-cmake", lambda: python_link_flags_cmake(), python_link_error,
+                "Print python link flags for cmake"))
+
+options.append(("--shiboken2-module-qmake-lflags",
+                lambda: get_package_qmake_lflags(Package.shiboken2_module), pyside2_error,
+                "Print shiboken2 shared library link flags for qmake"))
+options.append(("--pyside2-qmake-lflags",
+                lambda: get_package_qmake_lflags(Package.pyside2), pyside2_error,
+                "Print PySide2 shared library link flags for qmake"))
+
+options.append(("--shiboken2-module-shared-libraries-qmake",
+                lambda: get_shared_libraries_qmake(Package.shiboken2_module), pyside2_libs_error,
+                "Print paths of shiboken2 shared libraries (.so's, .dylib's, .dll's) for qmake"))
+options.append(("--shiboken2-module-shared-libraries-cmake",
+                lambda: get_shared_libraries_cmake(Package.shiboken2_module), pyside2_libs_error,
+                "Print paths of shiboken2 shared libraries (.so's, .dylib's, .dll's) for cmake"))
+
+options.append(("--pyside2-shared-libraries-qmake",
+                lambda: get_shared_libraries_qmake(Package.pyside2), pyside2_libs_error,
+                "Print paths of PySide2 shared libraries (.so's, .dylib's, .dll's) for qmake"))
+options.append(("--pyside2-shared-libraries-cmake",
+                lambda: get_shared_libraries_cmake(Package.pyside2), pyside2_libs_error,
+                "Print paths of PySide2 shared libraries (.so's, .dylib's, .dll's) for cmake"))
+
+options_usage = ''
+for i, (flag, _, _, description) in enumerate(options):
+    options_usage += '    {:<45} {}'.format(flag, description)
+    if i < len(options) - 1:
+        options_usage += '\n'
+
+usage = """
+Utility to determine include/link options of shiboken2/PySide2 and Python for qmake/CMake projects
+that would like to embed or build custom shiboken2/PySide2 bindings.
+
+Usage: pyside2_config.py [option]
+Options:
+{}
+    -a                                            Print all options and their values
+    --help/-h                                     Print this help
+""".format(options_usage)
+
+option = sys.argv[1] if len(sys.argv) == 2 else '-a'
+if option == '-h' or option == '--help':
+    print(usage)
+    sys.exit(0)
+
+
+class Package(object):
+    shiboken2_module = 1
+    shiboken2_generator = 2
+    pyside2 = 3
+
+
+def clean_path(path):
+    return path if sys.platform != 'win32' else path.replace('\\', '/')
+
+
+def shared_library_suffix():
+    if sys.platform == 'win32':
+        return 'lib'
+    elif sys.platform == 'darwin':
+        return 'dylib'
+    # Linux
+    else:
+        return 'so.*'
+
+
+def import_suffixes():
+    if (sys.version_info >= (3, 4)):
+        import importlib.machinery
+        return importlib.machinery.EXTENSION_SUFFIXES
+    else:
+        import imp
+        result = []
+        for t in imp.get_suffixes():
+            result.append(t[0])
+        return result
+
+
+def is_debug():
+    debug_suffix = '_d.pyd' if sys.platform == 'win32' else '_d.so'
+    return any([s.endswith(debug_suffix) for s in import_suffixes()])
+
+
+def shared_library_glob_pattern():
+    glob = '*.' + shared_library_suffix()
+    return glob if sys.platform == 'win32' else 'lib' + glob
+
+
+def filter_shared_libraries(libs_list):
+    def predicate(lib_name):
+        basename = os.path.basename(lib_name)
+        if 'shiboken' in basename or 'pyside2' in basename:
+            return True
+        return False
+    result = [lib for lib in libs_list if predicate(lib)]
+    return result
+
+
+# Return qmake link option for a library file name
+def link_option(lib):
+    # On Linux:
+    # Since we cannot include symlinks with wheel packages
+    # we are using an absolute path for the libpyside and libshiboken
+    # libraries when compiling the project
+    baseName = os.path.basename(lib)
+    link = ' -l'
+    if sys.platform in ['linux', 'linux2']: # Linux: 'libfoo.so' -> '/absolute/path/libfoo.so'
+        link = lib
+    elif sys.platform in ['darwin']: # Darwin: 'libfoo.so' -> '-lfoo'
+        link += os.path.splitext(baseName[3:])[0]
+    else: # Windows: 'libfoo.dll' -> 'libfoo.dll'
+        link += os.path.splitext(baseName)[0]
+    return link
+
+
+# Locate PySide2 via sys.path package path.
+def find_pyside2():
+    return find_package_path("PySide2")
+
+
+def find_shiboken2_module():
+    return find_package_path("shiboken2")
+
+
+def find_shiboken2_generator():
+    return find_package_path("shiboken2_generator")
+
+
+def find_package(which_package):
+    if which_package == Package.shiboken2_module:
+        return find_shiboken2_module()
+    if which_package == Package.shiboken2_generator:
+        return find_shiboken2_generator()
+    if which_package == Package.pyside2:
+        return find_pyside2()
+    return None
+
+
+def find_package_path(dir_name):
+    for p in sys.path:
+        # Salome integration:
+        # Changed because on Debian 11 shiboken being installed in 
+        # /usr/local/lib/python3.9/dist-packages
+        # instead of
+        # /home/user/.local/lib/python3.9/site-packages
+        #if 'site-' in p:
+        if 'site-' in p or 'dist-' in p:
+            package = os.path.join(p, dir_name)
+            if os.path.exists(package):
+                return clean_path(os.path.realpath(package))
+    return None
+
+
+# Return version as "3.5"
+def python_version():
+    return str(sys.version_info[0]) + '.' + str(sys.version_info[1])
+
+
+def get_python_include_path():
+    return sysconfig.get_python_inc()
+
+
+def python_link_flags_qmake():
+    flags = python_link_data()
+    if sys.platform == 'win32':
+        libdir = flags['libdir']
+        # This will add the "~1" shortcut for directories that
+        # contain white spaces
+        # e.g.: "Program Files" to "Progra~1"
+        for d in libdir.split("\\"):
+            if " " in d:
+                libdir = libdir.replace(d, d.split(" ")[0][:-1]+"~1")
+        return '-L{} -l{}'.format(libdir, flags['lib'])
+    elif sys.platform == 'darwin':
+        return '-L{} -l{}'.format(flags['libdir'], flags['lib'])
+
+    else:
+        # Linux and anything else
+        return '-L{} -l{}'.format(flags['libdir'], flags['lib'])
+
+
+def python_link_flags_cmake():
+    flags = python_link_data()
+    libdir = flags['libdir']
+    lib = re.sub(r'.dll$', '.lib', flags['lib'])
+    return '{};{}'.format(libdir, lib)
+
+
+def python_link_data():
+    # @TODO Fix to work with static builds of Python
+    libdir = sysconfig.get_config_var('LIBDIR')
+    if libdir is None:
+        libdir = os.path.abspath(os.path.join(
+            sysconfig.get_config_var('LIBDEST'), "..", "libs"))
+    version = python_version()
+    version_no_dots = version.replace('.', '')
+
+    flags = {}
+    flags['libdir'] = libdir
+    if sys.platform == 'win32':
+        suffix = '_d' if is_debug() else ''
+        flags['lib'] = 'python{}{}'.format(version_no_dots, suffix)
+
+    elif sys.platform == 'darwin':
+        flags['lib'] = 'python{}'.format(version)
+
+    # Linux and anything else
+    else:
+        if sys.version_info[0] < 3:
+            suffix = '_d' if is_debug() else ''
+            flags['lib'] = 'python{}{}'.format(version, suffix)
+        else:
+            flags['lib'] = 'python{}{}'.format(version, sys.abiflags)
+
+    return flags
+
+
+def get_package_include_path(which_package):
+    package_path = find_package(which_package)
+    if package_path is None:
+        return None
+
+    includes = "{0}/include".format(package_path)
+
+    return includes
+
+
+def get_package_qmake_lflags(which_package):
+    package_path = find_package(which_package)
+    if package_path is None:
+        return None
+
+    link = "-L{}".format(package_path)
+    glob_result = glob.glob(os.path.join(package_path, shared_library_glob_pattern()))
+    for lib in filter_shared_libraries(glob_result):
+        link += ' '
+        link += link_option(lib)
+    return link
+
+
+def get_shared_libraries_data(which_package):
+    package_path = find_package(which_package)
+    if package_path is None:
+        return None
+
+    glob_result = glob.glob(os.path.join(package_path, shared_library_glob_pattern()))
+    filtered_libs = filter_shared_libraries(glob_result)
+    libs = []
+    if sys.platform == 'win32':
+        for lib in filtered_libs:
+            libs.append(os.path.realpath(lib))
+    else:
+        for lib in filtered_libs:
+            libs.append(lib)
+    return libs
+
+
+def get_shared_libraries_qmake(which_package):
+    libs = get_shared_libraries_data(which_package)
+    if libs is None:
+        return None
+
+    if sys.platform == 'win32':
+        if not libs:
+            return ''
+        dlls = ''
+        for lib in libs:
+            dll = os.path.splitext(lib)[0] + '.dll'
+            dlls += dll + ' '
+
+        return dlls
+    else:
+        libs_string = ''
+        for lib in libs:
+            libs_string += lib + ' '
+        return libs_string
+
+
+def get_shared_libraries_cmake(which_package):
+    libs = get_shared_libraries_data(which_package)
+    result = ';'.join(libs)
+    return result
+
+
+print_all = option == "-a"
+for argument, handler, error, _ in options:
+    if option == argument or print_all:
+        handler_result = handler()
+        if handler_result is None:
+            sys.exit(error)
+
+        line = handler_result
+        if print_all:
+            line = "{:<40}: ".format(argument) + line
+        print(line)
index fb27befd0309fa6a3af9ca59cb7a9ec1ef2a793b..7a5f9b33aa2b4a767d1b9f39a14f6596f5911ca8 100644 (file)
@@ -82,16 +82,30 @@ context attributes:
 """
 
 import os,sys,traceback
-from qtsalome import *
 
 import salome
+import SalomePyQt
+
+from salome.kernel.logger import Logger
+#from salome.kernel.termcolor import GREEN
+logger=Logger("PluginsManager") #,color=GREEN)
+# VSR 21/11/2011 : do not show infos in the debug mode
+#logger.showDebug()
+
+# Check if SalomePyQt uses PySide2
+if hasattr(SalomePyQt, 'use_pyside'):
+  logger.info('SalomePyQt implemented with PySide2')
+  from PySide2.QtWidgets import QMenu, QApplication, QMessageBox
+else:
+  logger.info('SalomePyQt implemented with PyQt5')
+  from qtsalome import *
+
 
 SEP=":"
 if sys.platform == "win32":
   SEP = ";"
 
 # Get SALOME PyQt interface
-import SalomePyQt
 sgPyQt = SalomePyQt.SalomePyQt()
 
 # Get SALOME Swig interface
@@ -138,11 +152,6 @@ def findMenu(lmenu,menu):
 PLUGIN_PATH_PATTERN="share/salome/plugins"
 MATCH_ENDING_PATTERN="_plugins.py"
 from salome.kernel.syshelper import walktree
-from salome.kernel.logger import Logger
-#from salome.kernel.termcolor import GREEN
-logger=Logger("PluginsManager") #,color=GREEN)
-# VSR 21/11/2011 : do not show infos in the debug mode
-#logger.showDebug()
 
 class PluginsManager:
     def __init__(self,module,name,basemenuname,menuname):