PySide Binding Generation Tutorial: Module 5 Building the generator: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
(Change category "LanguageBindings::PySide::Shiboken::PySide Binding Generation Tutorial" -> "PySide")
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:LanguageBindings::PySide::Shiboken::PySide Binding Generation Tutorial]]<br />[toc align_right=&quot;yes&amp;quot; depth=&quot;3&amp;quot;]


'''English''' &quot;French&amp;quot;:http://qt-devnet.developpez.com/tutoriels/python/pyside/binding-shiboken/#LVII


* '''Note:''' this article is a member of the multipart &quot;PySide Binding Generation Tutorial&amp;quot;:http://developer.qt.nokia.com/wiki/Category:LanguageBindings::PySide::Shiboken::PySide_Binding_Generation_Tutorial
[[Category:PySide]]
 
 
'''English''' [http://qt-devnet.developpez.com/tutoriels/python/pyside/binding-shiboken/#LVII French]
 
* '''Note:''' this article is a member of the multipart [https://wiki.qt.io/PySide_Binding_Generation_Tutorial PySide Binding Generation Tutorial]


= PySide Binding Generation Tutorial =
= PySide Binding Generation Tutorial =
Line 21: Line 24:
=== Collect Information with pkg-config ===
=== Collect Information with pkg-config ===


The Qt bindings include compile and build information through the pkg-config mechanism. The pkg-config name for Qt Python bindings is &lt;code&amp;gt;pyside&amp;lt;/code&amp;gt; and a simple &lt;code&amp;gt;pkg-config pyside —cflags —libs&amp;lt;/code&amp;gt; will retrieve information required to build the new binding.
The Qt bindings include compile and build information through the pkg-config mechanism. The pkg-config name for Qt Python bindings is '''pyside''' and a simple '''pkg-config pyside —cflags —libs''' will retrieve information required to build the new binding.


The Qt bindings file &lt;code&amp;gt;pyside.pc&amp;lt;/code&amp;gt; for the use of pkg-config requires Qt's &lt;code&amp;gt;.pc&amp;lt;/code&amp;gt; files to be installed. If the library is in an unusual location, e.g. &lt;code&amp;gt;/opt/qt47&amp;lt;/code&amp;gt;, remember to export it to the &lt;code&amp;gt;PKG_CONFIG_PATH&amp;lt;/code&amp;gt; environment variable. For example: &lt;code&amp;gt;export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/qt47/lib/pkgconfig&amp;lt;/code&amp;gt;
The Qt bindings file '''pyside.pc''' for the use of pkg-config requires Qt's '''.pc''' files to be installed. If the library is in an unusual location, e.g. '''/opt/qt47''', remember to export it to the '''PKG_CONFIG_PATH''' environment variable. For example:  
<code>export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/qt47/lib/pkgconfig</code>


Information is also available through pkg-config: the &lt;code&amp;gt;typesystemdir&amp;lt;/code&amp;gt; variable. It is used like this: &lt;code&amp;gt;pkg-config pyside —variable=typesystemdir&amp;lt;/code&amp;gt; This provides information where to find the type system files used to create the Qt bindings. As mentioned before, the binding being created needs this to complement its own binding information for the generation proccess.
Information is also available through pkg-config: the '''typesystemdir''' variable. It is used like this: '''pkg-config pyside —variable=typesystemdir'''
This provides information where to find the type system files used to create the Qt bindings. As mentioned before, the binding being created needs this to complement its own binding information for the generation proccess.


Information from the '''Shiboken''' binding generator is also needed for the build, it's pkg-config name is &lt;code&amp;gt;shiboken&amp;lt;/code&amp;gt;. More details on this later.
Information from the '''Shiboken''' binding generator is also needed for the build, it's pkg-config name is '''shiboken'''. More details on this later.


=== Collect Information with CMake ===
=== Collect Information with CMake ===


When building your binding with CMake the relevant information can be included from your project's &lt;code&amp;gt;CMakeLists.txt&amp;lt;/code&amp;gt; using:
When building your binding with CMake the relevant information can be included from your project's <code>CMakeLists.txt</code> using:


<code><br />find_package(Shiboken REQUIRED)<br />find_package(PySide REQUIRED)<br /></code>
<code>
find_package(Shiboken REQUIRED)
find_package(PySide REQUIRED)
</code>


Requiring the '''Shiboken''' and '''PySide''' packages will set the values of a number of variables, according to <code>PySideConfig.cmake<code> file:
Requiring the '''Shiboken''' and '''PySide''' packages will set the values of a number of variables, according to '''PySideConfig.cmake''' file:


</code><br />PYSIDE_INCLUDE_DIR - Directories to include to use PySide<br />PYSIDE_LIBRARY - Files to link against to use PySide<br />PYSIDE_PYTHONPATH - Path to where the PySide Python module files could be found<br />PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide<br /><code>
<code>
PYSIDE_INCLUDE_DIR - Directories to include to use PySide
PYSIDE_LIBRARY - Files to link against to use PySide
PYSIDE_PYTHONPATH - Path to where the PySide Python module files could be found
PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide
</code>


Similarly &lt;code&amp;gt;ShibokenConfig.cmake&amp;lt;/code&amp;gt; provides needed information:
Similarly '''ShibokenConfig.cmake''' provides needed information:


</code><br />SHIBOKEN_INCLUDE_DIR - Directories to include to use SHIBOKEN<br />SHIBOKEN_LIBRARIES - Files to link against to use SHIBOKEN<br />SHIBOKEN_BUILD_TYPE - Tells if Shiboken was compiled in Release or Debug mode.<br />SHIBOKEN_PYTHON_INTERPRETER - Python interpreter (regular or debug) to be used with the bindings.<br />SHIBOKEN_PYTHON_LIBRARIES - Python libraries (regular or debug) Shiboken is linked against.<br /><code>
<code>
SHIBOKEN_INCLUDE_DIR - Directories to include to use SHIBOKEN
SHIBOKEN_LIBRARIES - Files to link against to use SHIBOKEN
SHIBOKEN_BUILD_TYPE - Tells if Shiboken was compiled in Release or Debug mode.
SHIBOKEN_PYTHON_INTERPRETER - Python interpreter (regular or debug) to be used with the bindings.
SHIBOKEN_PYTHON_LIBRARIES - Python libraries (regular or debug) Shiboken is linked against.
</code>


== Run the Generator ==
== Run the Generator ==
Line 47: Line 66:
The generator is called with the following parameters and options:
The generator is called with the following parameters and options:


</code><br />generatorrunner —generatorSet=shiboken  global_header.h  —include-paths=$(PATHS_TO_HEADERS))  —typesystem-paths=$(PATHS_TO_TYPESYSTEMS)  —output-directory=.  typesystem.xml<br /><code>
<code>
generatorrunner —generatorSet=shiboken  global_header.h  —include-paths=$(PATHS_TO_HEADERS))  —typesystem-paths=$(PATHS_TO_TYPESYSTEMS)   
—output-directory=.  typesystem.xml
</code>


Note that the variables for include and type system paths could be determined at build time with the pkg-config tool or with information provided by CMake configuration files.
Note that the variables for include and type system paths could be determined at build time with the pkg-config tool or with information provided by CMake configuration files.
Line 59: Line 81:
Below is a plain Makefile for the binding project.
Below is a plain Makefile for the binding project.


&lt;code&amp;gt;foobinding-makefile/Makefile&amp;lt;/code&amp;gt;:
<code>foobinding-makefile/Makefile</code>:


</code><br />LIBFOO_DIR = `pwd`/../libfoo<br />LIBS = `pkg-config pyside —libs`  -L$(LIBFOO_DIR) -lfoo<br />CXXFLAGS = -I/usr/share/qt4/mkspecs/linux-g++ -I.  -I$(LIBFOO_DIR)  -I`pwd`/foo  -I`pkg-config —variable=includedir pyside`/QtCore/  -I`pkg-config —variable=includedir QtCore`  -I`pkg-config —variable=includedir QtCore`/..  -I`pkg-config —variable=includedir QtGui`  `pkg-config pyside —cflags`
<code>
LIBFOO_DIR = `pwd`/../libfoo
LIBS = `pkg-config pyside —libs`  -L$(LIBFOO_DIR) -lfoo
CXXFLAGS = -I/usr/share/qt4/mkspecs/linux-g++ -I.  -I$(LIBFOO_DIR)  -I`pwd`/foo  -I`pkg-config —variable=includedir pyside`/QtCore/   
-I`pkg-config —variable=includedir QtCore`  -I`pkg-config —variable=includedir QtCore`/..  -I`pkg-config —variable=includedir QtGui`   
`pkg-config pyside —cflags`


QT4TYPESYSTEM_DIR = `pkg-config pyside —variable=typesystemdir`<br />QT4HEADER_DIRS = `pkg-config —variable=includedir QtCore`:`pkg-config —variable=includedir QtCore`/..<br />PYSIDE_PYTHONPATH = `pkg-config —variable=pythonpath PySide`<br />PYTHON_INTERPRETER = `pkg-config —variable=python_interpreter shiboken`
QT4TYPESYSTEM_DIR = `pkg-config pyside —variable=typesystemdir`
QT4HEADER_DIRS = `pkg-config —variable=includedir QtCore`:`pkg-config —variable=includedir QtCore`/..
PYSIDE_PYTHONPATH = `pkg-config —variable=pythonpath PySide`
PYTHON_INTERPRETER = `pkg-config —variable=python_interpreter shiboken`


all: generate compile link
all: generate compile link


generate:<br /> generatorrunner —generatorSet=shiboken  global.h  —include-paths=$(LIBFOO_DIR):$(QT4HEADER_DIRS):/usr/include  —typesystem-paths=.:$(QT4TYPESYSTEM_DIR)  —output-directory=.  typesystem_foo.xml
generate:
generatorrunner —generatorSet=shiboken  global.h  —include-paths=$(LIBFOO_DIR):$(QT4HEADER_DIRS):/usr/include   
—typesystem-paths=.:$(QT4TYPESYSTEM_DIR)  —output-directory=.  typesystem_foo.xml


compile:<br /> g++ foo/foo_module_wrapper.cpp foo/math_wrapper.cpp -Wall -fPIC $(CXXFLAGS) -c
compile:
g++ foo/foo_module_wrapper.cpp foo/math_wrapper.cpp -Wall -fPIC $(CXXFLAGS) -c


link:<br /> g++ foo_module_wrapper.o math_wrapper.o $(LIBS) -fPIC -shared -Wl,-soname,foo.so -o foo.so
link:
g++ foo_module_wrapper.o math_wrapper.o $(LIBS) -fPIC -shared -Wl,-soname,foo.so -o foo.so


test:<br /> LD_LIBRARY_PATH=$(LIBFOO_DIR):$(LD_LIBRARY_PATH) PYTHONPATH=$(PYSIDE_PYTHONPATH):$(PYTHONPATH) $(PYTHON_INTERPRETER) -c  &quot;import foo; m = foo.Math(); print '5 squared is d' m.squared(5)&quot;
test:
LD_LIBRARY_PATH=$(LIBFOO_DIR):$(LD_LIBRARY_PATH) PYTHONPATH=$(PYSIDE_PYTHONPATH):$(PYTHONPATH) $(PYTHON_INTERPRETER) -c   
"import foo; m = foo.Math(); print '5 squared is d' m.squared(5)"


clean:<br /> rm -rf '''.o'''.so '''.?pp'''.log '''.log foo/'''<br /><code>
clean:
rm -rf '''.o'''.so '''.?pp'''.log '''.log foo/'''
</code>


Keep in mind that the Makefile above expects the &lt;code&amp;gt;libfoo&amp;lt;/code&amp;gt; and &lt;code&amp;gt;foobinding-makefile&amp;lt;/code&amp;gt; directories to be in the same level in the directory hierarchy. Remember to change any path references accordingly if you elect to change things.
Keep in mind that the Makefile above expects the '''libfoo''' and '''foobinding-makefile''' directories to be in the same level in the directory hierarchy. Remember to change any path references accordingly if you elect to change things.


==== Build and Test ====
==== Build and Test ====
Line 83: Line 121:
Now generate, compile and link the binding with make:
Now generate, compile and link the binding with make:


</code><br />cd foobinding-makefile<br />make<br />make test<br /><code>
<code>
cd foobinding-makefile
make
make test
</code>


The &lt;code&amp;gt;make test&amp;lt;/code&amp;gt; causes the Python interpreter to run the line &lt;code&amp;gt;import foo; m = foo.Math(); print '5 squared is d' m.squared(5)&lt;/code&amp;gt;, which will import the binding module, instantiate the class from it, run a method and print its result (which should be 25).
The '''make test''' causes the Python interpreter to run the line  
<code>import foo; m = foo.Math(); print '5 squared is d' m.squared(5)</code>
, which will import the binding module, instantiate the class from it, run a method and print its result (which should be 25).


=== The CMake Version ===
=== The CMake Version ===


&lt;code&amp;gt;foobinding-cmake/CMakeLists.txt&amp;lt;/code&amp;gt;:
<code>foobinding-cmake/CMakeLists.txt</code>:


</code><br />project(foobinding)
<code>
project(foobinding)


cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 2.6)


find_package(PythonLibs REQUIRED)<br />find_package(Shiboken REQUIRED)<br />find_package(PySide REQUIRED)<br />find_package(Qt4 4.6.2 REQUIRED)
find_package(PythonLibs REQUIRED)
find_package(Shiboken REQUIRED)
find_package(PySide REQUIRED)
find_package(Qt4 4.6.2 REQUIRED)


set(LIBFOO_DIR ${CMAKE_SOURCE_DIR}/../libfoo)
set(LIBFOO_DIR ${CMAKE_SOURCE_DIR}/../libfoo)


find_program(GENERATOR generatorrunner REQUIRED)<br />if (NOT GENERATOR)<br /> message(FATAL_ERROR &quot;You need to specify GENERATOR variable (-DGENERATOR=value)&quot;)<br />endif()
find_program(GENERATOR generatorrunner REQUIRED)
if (NOT GENERATOR)
message(FATAL_ERROR "You need to specify GENERATOR variable (-DGENERATOR=value)")
endif()
 
if(CMAKE_HOST_UNIX)
option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library size and performance. Only available on Release Mode" 0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
if(ENABLE_GCC_OPTIMIZATION)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1")
if(NOT CMAKE_HOST_APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,—hash-style=gnu")
endif()
endif()
 
if(CMAKE_HOST_APPLE)
if (NOT QT_INCLUDE_DIR)
set(QT_INCLUDE_DIR "/Library/Frameworks")
endif()
endif()
endif()
 
include(${QT_USE_FILE})
 
enable_testing()
 
add_subdirectory(foo)
add_subdirectory(tests)
</code>
 
This is the main project's '''CMakeLists.txt''', it is a regular CMake file and general doubts can be checked in the [http://www.cmake.org/cmake/help/documentation.html CMake documentation] Notice the we're going to have tests in this project so we have to enable them with '''enable_testing()'''.
 
<code>foobinding-cmake/foo/CMakeLists.txt</code>:
 
<code>
project(foo)
 
set(foo_SRC
${CMAKE_CURRENT_BINARY_DIR}/foo/foo_module_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/foo/math_wrapper.cpp
)
 
set(foo_INCLUDE_DIRECTORIES
${SHIBOKEN_INCLUDE_DIR}
${PYTHON_INCLUDE_PATH}
${PYSIDE_INCLUDE_DIR}
${PYSIDE_INCLUDE_DIR}/QtCore
${QT_INCLUDE_DIR}
${QT_QTCORE_INCLUDE_DIR}
${LIBFOO_DIR}
)
 
set(foo_LINK_LIBRARIES
${QT_QTCORE_LIBRARY}
${SHIBOKEN_PYTHON_LIBRARIES}
${SHIBOKEN_LIBRARY}
${PYSIDE_LIBRARY}
${LIBFOO_DIR}/libfoo.so
)
 
include_directories(foo ${foo_INCLUDE_DIRECTORIES})
add_library(foo MODULE ${foo_SRC})
set_property(TARGET foo PROPERTY PREFIX "")
target_link_libraries(foo ${foo_LINK_LIBRARIES})
 
add_custom_command(OUTPUT ${foo_SRC}
COMMAND ${GENERATOR}
—generatorSet=shiboken —enable-parent-ctor-heuristic —enable-pyside-extensions —enable-return-value-heuristic
${CMAKE_SOURCE_DIR}/foo/global.h
—include-paths=${QT_INCLUDE_DIR}:${LIBFOO_DIR}
—typesystem-paths=${typesystem_path}:${PYSIDE_TYPESYSTEMS}
—output-directory=${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/typesystem_foo.xml
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running generator for libfoo…"
)
</code>
 
This is the '''CMakeLists.txt''' file for the binding directory proper, the '''add_custom_command''' statement is responsible for the calling of Shiboken generator with the proper parameters and variables. Notice that the command line options '''—enable-parent-ctor-heuristic —enable-pyside-extensions —enable-return-value-heuristic''' are directly related to Qt bindings idiosyncrasies, for a pure C++ binding none of those will be necessary.
 
<code>foobinding-cmake/tests/CMakeLists.txt</code>:
 
<code>
if(WIN32)
set(TEST_PYTHONPATH "${foo_BINARY_DIR};${PYSIDE_PYTHONPATH}")
set(TEST_LIBRARY_PATH "${LIBFOO_DIR};$ENV{PATH}")
set(LIBRARY_PATH_VAR "PATH")
string(REPLACE "" "/" TEST_PYTHONPATH "${TEST_PYTHONPATH}")
string(REPLACE "" "/" TEST_LIBRARY_PATH "${TEST_LIBRARY_PATH}")
 
string(REPLACE ";" ";" TEST_PYTHONPATH "${TEST_PYTHONPATH}")
string(REPLACE ";" ";" TEST_LIBRARY_PATH "${TEST_LIBRARY_PATH}")
else()
set(TEST_PYTHONPATH "${foo_BINARY_DIR}:${PYSIDE_PYTHONPATH}")
set(TEST_LIBRARY_PATH "${LIBFOO_DIR}:$ENV{LD_LIBRARY_PATH}")
set(LIBRARY_PATH_VAR "LD_LIBRARY_PATH")
endif()
 
add_test(math ${SHIBOKEN_PYTHON_INTERPRETER} ${CMAKE_CURRENT_SOURCE_DIR}/math_test.py)
set_tests_properties(math PROPERTIES ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH}")
</code>
 
This not very elaborate '''CMakeLists.txt''' informs CMake which tests should be executed, and with which variables.
 
==== Build and Test ====
The best thing to do when building with CMake is to create a build directory and run <code>cmake</code> from there.
 
<code>
cd foobinding-cmake
mkdir build
cd build
cmake ..
make
</code>
 
Ah, let's not forget the unit test. It's a very simple one.
 
<code>foobinding-cmake/tests/math.py</code>


if(CMAKE_HOST_UNIX)<br /> option(ENABLE_GCC_OPTIMIZATION &quot;Enable specific GCC flags to optimization library size and performance. Only available on Release Mode&amp;quot; 0)<br /> set(CMAKE_CXX_FLAGS &quot;${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing&amp;quot;)<br /> set(CMAKE_CXX_FLAGS_DEBUG &quot;-g&amp;quot;)<br /> if(ENABLE_GCC_OPTIMIZATION)<br /> set(CMAKE_BUILD_TYPE Release)<br /> set(CMAKE_CXX_FLAGS_RELEASE &quot;-DNDEBUG -Os -Wl,-O1&amp;quot;)<br /> if(NOT CMAKE_HOST_APPLE)<br /> set(CMAKE_CXX_FLAGS &quot;${CMAKE_CXX_FLAGS} <s>Wl,—hash-style=gnu&amp;quot;)<br /> endif()<br /> endif()
<code>
<br /> if(CMAKE_HOST_APPLE)<br /> if (NOT QT_INCLUDE_DIR)<br /> set(QT_INCLUDE_DIR &quot;/Library/Frameworks&amp;quot;)<br /> endif()<br /> endif()<br />endif()
#!/usr/bin/env python
<br />include(${QT_USE_FILE})
#-'''- coding: utf-8 -'''-
<br />enable_testing()
<br />add_subdirectory(foo)<br />add_subdirectory(tests)<br /><code>
<br />This is the main project's &lt;code&amp;gt;CMakeLists.txt&amp;lt;/code&amp;gt;, it is a regular CMake file and general doubts can be checked in the &quot;CMake documentation&amp;quot;:http://www.cmake.org/cmake/help/documentation.html Notice the we're going to have tests in this project so we have to enable them with &lt;code&amp;gt;enable_testing()&lt;/code&amp;gt;.
<br />&lt;code&amp;gt;foobinding-cmake/foo/CMakeLists.txt&amp;lt;/code&amp;gt;:
<br /></code><br />project(foo)
<br />set(foo_SRC<br /> ${CMAKE_CURRENT_BINARY_DIR}/foo/foo_module_wrapper.cpp<br /> ${CMAKE_CURRENT_BINARY_DIR}/foo/math_wrapper.cpp<br />)
<br />set(foo_INCLUDE_DIRECTORIES<br /> ${SHIBOKEN_INCLUDE_DIR}<br /> ${PYTHON_INCLUDE_PATH}<br /> ${PYSIDE_INCLUDE_DIR}<br /> ${PYSIDE_INCLUDE_DIR}/QtCore<br /> ${QT_INCLUDE_DIR}<br /> ${QT_QTCORE_INCLUDE_DIR}<br /> ${LIBFOO_DIR}<br />)
<br />set(foo_LINK_LIBRARIES<br /> ${QT_QTCORE_LIBRARY}<br /> ${SHIBOKEN_PYTHON_LIBRARIES}<br /> ${SHIBOKEN_LIBRARY}<br /> ${PYSIDE_LIBRARY}<br /> ${LIBFOO_DIR}/libfoo.so<br />)
<br />include_directories(foo ${foo_INCLUDE_DIRECTORIES})<br />add_library(foo MODULE ${foo_SRC})<br />set_property(TARGET foo PROPERTY PREFIX &quot;&quot;)<br />target_link_libraries(foo ${foo_LINK_LIBRARIES})
<br />add_custom_command(OUTPUT ${foo_SRC}<br /> COMMAND ${GENERATOR}<br /> —generatorSet=shiboken —enable-parent-ctor-heuristic —enable-pyside-extensions —enable-return-value-heuristic<br /> ${CMAKE_SOURCE_DIR}/foo/global.h<br /> —include-paths=${QT_INCLUDE_DIR}:${LIBFOO_DIR}<br /> —typesystem-paths=${typesystem_path}:${PYSIDE_TYPESYSTEMS}<br /> —output-directory=${CMAKE_CURRENT_BINARY_DIR}<br /> ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_foo.xml<br /> WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}<br /> COMMENT &quot;Running generator for libfoo…&quot;<br /> )<br /><code>
<br />This is the &lt;code&amp;gt;CMakeLists.txt&amp;lt;/code&amp;gt; file for the binding directory proper, the &lt;code&amp;gt;add_custom_command&amp;lt;/code&amp;gt; statement is responsible for the calling of Shiboken generator with the proper parameters and variables. Notice that the command line options &lt;code&amp;gt;—enable-parent-ctor-heuristic —enable-pyside-extensions —enable-return-value-heuristic&amp;lt;/code&amp;gt; are directly related to Qt bindings idiosyncrasies, for a pure C++ binding none of those will be necessary.
<br />&lt;code&amp;gt;foobinding-cmake/tests/CMakeLists.txt&amp;lt;/code&amp;gt;:
<br /></code><br />if(WIN32)<br /> set(TEST_PYTHONPATH &quot;${foo_BINARY_DIR};${PYSIDE_PYTHONPATH}&quot;)<br /> set(TEST_LIBRARY_PATH &quot;${LIBFOO_DIR};$ENV{PATH}&quot;)<br /> set(LIBRARY_PATH_VAR &quot;PATH&amp;quot;)<br /> string(REPLACE &quot;&quot; &quot;/&amp;quot; TEST_PYTHONPATH &quot;${TEST_PYTHONPATH}&quot;)<br /> string(REPLACE &quot;&quot; &quot;/&amp;quot; TEST_LIBRARY_PATH &quot;${TEST_LIBRARY_PATH}&quot;)
<br /> string(REPLACE &quot;;&quot; &quot;;&quot; TEST_PYTHONPATH &quot;${TEST_PYTHONPATH}&quot;)<br /> string(REPLACE &quot;;&quot; &quot;;&quot; TEST_LIBRARY_PATH &quot;${TEST_LIBRARY_PATH}&quot;)<br />else()<br /> set(TEST_PYTHONPATH &quot;${foo_BINARY_DIR}:${PYSIDE_PYTHONPATH}&quot;)<br /> set(TEST_LIBRARY_PATH &quot;${LIBFOO_DIR}:$ENV{LD_LIBRARY_PATH}&quot;)<br /> set(LIBRARY_PATH_VAR &quot;LD_LIBRARY_PATH&amp;quot;)<br />endif()
<br />add_test(math ${SHIBOKEN_PYTHON_INTERPRETER} ${CMAKE_CURRENT_SOURCE_DIR}/math_test.py)<br />set_tests_properties(math PROPERTIES ENVIRONMENT &quot;PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH}&quot;)<br /><code>
<br />This not very elaborate &lt;code&amp;gt;CMakeLists.txt&amp;lt;/code&amp;gt; informs CMake which tests should be executed, and with which variables.
<br />h4. Build and Test
<br />The best thing to do when building with CMake is to create a build directory and run &lt;code&amp;gt;cmake&amp;lt;/code&amp;gt; from there.
<br /></code><br />cd foobinding-cmake<br />mkdir build<br />cd build<br />cmake ..<br />make<br /><code>
<br />Ah, let's not forget the unit test. It's a very simple one.
<br />&lt;code&amp;gt;foobinding-cmake/tests/math.py&amp;lt;/code&amp;gt;
<br /></code><br />#!/usr/bin/env python<br />#</s>'''- coding: utf-8 -'''-


'''Test cases for foo bindings module.'''
'''Test cases for foo bindings module.'''


import unittest<br />import foo
import unittest
import foo


class MathTest(unittest.TestCase):
class MathTest(unittest.TestCase):


def testMath(self):<br /> '''Test case for Math class from foo module.'''<br /> val = 5<br /> math = foo.Math()<br /> self.assertEqual(math.squared(5), 5 * 5)
def testMath(self):
'''Test case for Math class from foo module.'''
val = 5
math = foo.Math()
self.assertEqual(math.squared(5), 5 * 5)


if ''name'' == '''main''':<br /> unittest.main()<br /><code>
if ''name'' == '''main''':
unittest.main()
</code>


To run the test:
To run the test:


</code><br />ctest<br /><code>
<code>
ctest
</code>


The output will be something like this:
The output will be something like this:


</code><br />Test project YOURPATH/binding-tutorial/foobinding-cmake/build<br /> Start 1: math<br />1/1 Test #1: math ……………………….. Passed 0.10 sec
<code>
Test project YOURPATH/binding-tutorial/foobinding-cmake/build
Start 1: math
1/1 Test #1: math ……………………….. Passed 0.10 sec


100% tests passed, 0 tests failed out of 1
100% tests passed, 0 tests failed out of 1


Total Test time (real) = 0.11 sec<br /><code>
Total Test time (real) = 0.11 sec
</code>


For a more verbose output use &lt;code&amp;gt;ctest -V&amp;lt;/code&amp;gt;
For a more verbose output use '''ctest -V'''


== Conclusion ==
== Conclusion ==

Latest revision as of 05:06, 5 June 2016


English French

PySide Binding Generation Tutorial

Three Steps to Build the Binding

As mentioned before, the build system used must perform the following tasks in the correct order:

  • Gather data about locations of headers and needed type systems from other projects.
  • Run the generator with the correct parameters.
  • Compile and link the binding.

Gather Information

There are two options to gather data about locations of headers and needed type systems:

Collect Information with pkg-config

The Qt bindings include compile and build information through the pkg-config mechanism. The pkg-config name for Qt Python bindings is pyside and a simple pkg-config pyside —cflags —libs will retrieve information required to build the new binding.

The Qt bindings file pyside.pc for the use of pkg-config requires Qt's .pc files to be installed. If the library is in an unusual location, e.g. /opt/qt47, remember to export it to the PKG_CONFIG_PATH environment variable. For example:

export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/qt47/lib/pkgconfig

Information is also available through pkg-config: the typesystemdir variable. It is used like this: pkg-config pyside —variable=typesystemdir This provides information where to find the type system files used to create the Qt bindings. As mentioned before, the binding being created needs this to complement its own binding information for the generation proccess.

Information from the Shiboken binding generator is also needed for the build, it's pkg-config name is shiboken. More details on this later.

Collect Information with CMake

When building your binding with CMake the relevant information can be included from your project's

CMakeLists.txt

using:

find_package(Shiboken REQUIRED)
find_package(PySide REQUIRED)

Requiring the Shiboken and PySide packages will set the values of a number of variables, according to PySideConfig.cmake file:

PYSIDE_INCLUDE_DIR - Directories to include to use PySide
PYSIDE_LIBRARY - Files to link against to use PySide
PYSIDE_PYTHONPATH - Path to where the PySide Python module files could be found
PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide

Similarly ShibokenConfig.cmake provides needed information:

SHIBOKEN_INCLUDE_DIR - Directories to include to use SHIBOKEN
SHIBOKEN_LIBRARIES - Files to link against to use SHIBOKEN
SHIBOKEN_BUILD_TYPE - Tells if Shiboken was compiled in Release or Debug mode.
SHIBOKEN_PYTHON_INTERPRETER - Python interpreter (regular or debug) to be used with the bindings.
SHIBOKEN_PYTHON_LIBRARIES - Python libraries (regular or debug) Shiboken is linked against.

Run the Generator

The generator is called with the following parameters and options:

generatorrunner generatorSet=shiboken  global_header.h  include-paths=$(PATHS_TO_HEADERS))  typesystem-paths=$(PATHS_TO_TYPESYSTEMS)  
output-directory=.  typesystem.xml

Note that the variables for include and type system paths could be determined at build time with the pkg-config tool or with information provided by CMake configuration files.

Build

This section will alternate in presenting the two build methods: Makefile and CMake.

The Makefile Version

Below is a plain Makefile for the binding project.

foobinding-makefile/Makefile

:

LIBFOO_DIR = `pwd`/../libfoo
LIBS = `pkg-config pyside libs`  -L$(LIBFOO_DIR) -lfoo
CXXFLAGS = -I/usr/share/qt4/mkspecs/linux-g++ -I.  -I$(LIBFOO_DIR)  -I`pwd`/foo  -I`pkg-config variable=includedir pyside`/QtCore/  
-I`pkg-config variable=includedir QtCore`  -I`pkg-config variable=includedir QtCore`/..  -I`pkg-config variable=includedir QtGui`  
`pkg-config pyside cflags`

QT4TYPESYSTEM_DIR = `pkg-config pyside variable=typesystemdir`
QT4HEADER_DIRS = `pkg-config variable=includedir QtCore`:`pkg-config variable=includedir QtCore`/..
PYSIDE_PYTHONPATH = `pkg-config variable=pythonpath PySide`
PYTHON_INTERPRETER = `pkg-config variable=python_interpreter shiboken`

all: generate compile link

generate:
 generatorrunner generatorSet=shiboken  global.h  include-paths=$(LIBFOO_DIR):$(QT4HEADER_DIRS):/usr/include  
typesystem-paths=.:$(QT4TYPESYSTEM_DIR)  output-directory=.  typesystem_foo.xml

compile:
 g++ foo/foo_module_wrapper.cpp foo/math_wrapper.cpp -Wall -fPIC $(CXXFLAGS) -c

link:
 g++ foo_module_wrapper.o math_wrapper.o $(LIBS) -fPIC -shared -Wl,-soname,foo.so -o foo.so

test:
 LD_LIBRARY_PATH=$(LIBFOO_DIR):$(LD_LIBRARY_PATH) PYTHONPATH=$(PYSIDE_PYTHONPATH):$(PYTHONPATH) $(PYTHON_INTERPRETER) -c  
"import foo; m = foo.Math(); print '5 squared is d' m.squared(5)"

clean:
 rm -rf '''.o'''.so '''.?pp'''.log '''.log foo/'''

Keep in mind that the Makefile above expects the libfoo and foobinding-makefile directories to be in the same level in the directory hierarchy. Remember to change any path references accordingly if you elect to change things.

Build and Test

Now generate, compile and link the binding with make:

cd foobinding-makefile
make
make test

The make test causes the Python interpreter to run the line

import foo; m = foo.Math(); print '5 squared is d' m.squared(5)

, which will import the binding module, instantiate the class from it, run a method and print its result (which should be 25).

The CMake Version

foobinding-cmake/CMakeLists.txt

:

project(foobinding)

cmake_minimum_required(VERSION 2.6)

find_package(PythonLibs REQUIRED)
find_package(Shiboken REQUIRED)
find_package(PySide REQUIRED)
find_package(Qt4 4.6.2 REQUIRED)

set(LIBFOO_DIR ${CMAKE_SOURCE_DIR}/../libfoo)

find_program(GENERATOR generatorrunner REQUIRED)
if (NOT GENERATOR)
 message(FATAL_ERROR "You need to specify GENERATOR variable (-DGENERATOR=value)")
endif()

if(CMAKE_HOST_UNIX)
 option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library size and performance. Only available on Release Mode" 0)
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing")
 set(CMAKE_CXX_FLAGS_DEBUG "-g")
 if(ENABLE_GCC_OPTIMIZATION)
 set(CMAKE_BUILD_TYPE Release)
 set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1")
 if(NOT CMAKE_HOST_APPLE)
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,—hash-style=gnu")
 endif()
 endif()

 if(CMAKE_HOST_APPLE)
 if (NOT QT_INCLUDE_DIR)
 set(QT_INCLUDE_DIR "/Library/Frameworks")
 endif()
 endif()
endif()

include(${QT_USE_FILE})

enable_testing()

add_subdirectory(foo)
add_subdirectory(tests)

This is the main project's CMakeLists.txt, it is a regular CMake file and general doubts can be checked in the CMake documentation Notice the we're going to have tests in this project so we have to enable them with enable_testing().

foobinding-cmake/foo/CMakeLists.txt

:

project(foo)

set(foo_SRC
 ${CMAKE_CURRENT_BINARY_DIR}/foo/foo_module_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/foo/math_wrapper.cpp
)

set(foo_INCLUDE_DIRECTORIES
 ${SHIBOKEN_INCLUDE_DIR}
 ${PYTHON_INCLUDE_PATH}
 ${PYSIDE_INCLUDE_DIR}
 ${PYSIDE_INCLUDE_DIR}/QtCore
 ${QT_INCLUDE_DIR}
 ${QT_QTCORE_INCLUDE_DIR}
 ${LIBFOO_DIR}
)

set(foo_LINK_LIBRARIES
 ${QT_QTCORE_LIBRARY}
 ${SHIBOKEN_PYTHON_LIBRARIES}
 ${SHIBOKEN_LIBRARY}
 ${PYSIDE_LIBRARY}
 ${LIBFOO_DIR}/libfoo.so
)

include_directories(foo ${foo_INCLUDE_DIRECTORIES})
add_library(foo MODULE ${foo_SRC})
set_property(TARGET foo PROPERTY PREFIX "")
target_link_libraries(foo ${foo_LINK_LIBRARIES})

add_custom_command(OUTPUT ${foo_SRC}
 COMMAND ${GENERATOR}
 generatorSet=shiboken enable-parent-ctor-heuristic enable-pyside-extensions enable-return-value-heuristic
 ${CMAKE_SOURCE_DIR}/foo/global.h
 include-paths=${QT_INCLUDE_DIR}:${LIBFOO_DIR}
 typesystem-paths=${typesystem_path}:${PYSIDE_TYPESYSTEMS}
 output-directory=${CMAKE_CURRENT_BINARY_DIR}
 ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_foo.xml
 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 COMMENT "Running generator for libfoo…"
 )

This is the CMakeLists.txt file for the binding directory proper, the add_custom_command statement is responsible for the calling of Shiboken generator with the proper parameters and variables. Notice that the command line options —enable-parent-ctor-heuristic —enable-pyside-extensions —enable-return-value-heuristic are directly related to Qt bindings idiosyncrasies, for a pure C++ binding none of those will be necessary.

foobinding-cmake/tests/CMakeLists.txt

:

if(WIN32)
 set(TEST_PYTHONPATH "${foo_BINARY_DIR};${PYSIDE_PYTHONPATH}")
 set(TEST_LIBRARY_PATH "${LIBFOO_DIR};$ENV{PATH}")
 set(LIBRARY_PATH_VAR "PATH")
 string(REPLACE "" "/" TEST_PYTHONPATH "${TEST_PYTHONPATH}")
 string(REPLACE "" "/" TEST_LIBRARY_PATH "${TEST_LIBRARY_PATH}")

 string(REPLACE ";" ";" TEST_PYTHONPATH "${TEST_PYTHONPATH}")
 string(REPLACE ";" ";" TEST_LIBRARY_PATH "${TEST_LIBRARY_PATH}")
else()
 set(TEST_PYTHONPATH "${foo_BINARY_DIR}:${PYSIDE_PYTHONPATH}")
 set(TEST_LIBRARY_PATH "${LIBFOO_DIR}:$ENV{LD_LIBRARY_PATH}")
 set(LIBRARY_PATH_VAR "LD_LIBRARY_PATH")
endif()

add_test(math ${SHIBOKEN_PYTHON_INTERPRETER} ${CMAKE_CURRENT_SOURCE_DIR}/math_test.py)
set_tests_properties(math PROPERTIES ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH}")

This not very elaborate CMakeLists.txt informs CMake which tests should be executed, and with which variables.

Build and Test

The best thing to do when building with CMake is to create a build directory and run

cmake

from there.

cd foobinding-cmake
mkdir build
cd build
cmake ..
make

Ah, let's not forget the unit test. It's a very simple one.

foobinding-cmake/tests/math.py
#!/usr/bin/env python
#-'''- coding: utf-8 -'''-

'''Test cases for foo bindings module.'''

import unittest
import foo

class MathTest(unittest.TestCase):

def testMath(self):
 '''Test case for Math class from foo module.'''
 val = 5
 math = foo.Math()
 self.assertEqual(math.squared(5), 5 * 5)

if ''name'' == '''main''':
 unittest.main()

To run the test:

ctest

The output will be something like this:

Test project YOURPATH/binding-tutorial/foobinding-cmake/build
 Start 1: math
1/1 Test #1: math ……………………….. Passed 0.10 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) = 0.11 sec

For a more verbose output use ctest -V

Conclusion