From f029a27152cf7c7a741ce03a6f91be71729093ca Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 7 Dec 2021 15:21:37 +0100 Subject: Document the scriptableapplication, samplebinding examples Add a dummy .pyproject file into the doc directory for the example gallery script to collect it. Convert the .md files to .rst files for the documentation. Pick-to: 6.2 Change-Id: I87ea5b980d3d2177a7851f71462ca0b0bd0eba7e Reviewed-by: Cristian Maureira-Fredes --- examples/samplebinding/README.md | 245 ------------------ examples/samplebinding/doc/samplebinding.pyproject | 10 + examples/samplebinding/doc/samplebinding.rst | 273 +++++++++++++++++++++ 3 files changed, 283 insertions(+), 245 deletions(-) delete mode 100644 examples/samplebinding/README.md create mode 100644 examples/samplebinding/doc/samplebinding.pyproject create mode 100644 examples/samplebinding/doc/samplebinding.rst (limited to 'examples/samplebinding') diff --git a/examples/samplebinding/README.md b/examples/samplebinding/README.md deleted file mode 100644 index 26ff2b383..000000000 --- a/examples/samplebinding/README.md +++ /dev/null @@ -1,245 +0,0 @@ -# Sample bindings example - -This example showcases how to generate Python bindings for a -non-Qt C++ library. - -The example defines a CMake project that builds two libraries: -* `libuniverse` - a sample library with two C++ classes. -* `Universe` - the generated Python extension module that contains - bindings to the library above. - -The project file is structured in such a way that a user can copy-paste -in into their own project, and be able to build it with a minimal amount -of modifications. - -## Description - -The libuniverse library declares two classes: `Icecream` and `Truck`. - -`Icecream` objects have a flavor, and an accessor for returning the -flavor. - -`Truck` instances store a vector of `Icecream` objects, and have various -methods for adding new flavors, printing available flavors, delivering -icecream, etc. - -From a C++ perspective, `Icecream` instances are treated as -**object types** (pointer semantics) because the class declares virtual -methods. - -In contrast `Truck` does not define virtual methods and is treated as -a **value type** (copy semantics). - -Because `Truck` is a value type and it stores a vector of `Icecream` -pointers, the rule of three has to be taken into account (implement the -copy constructor, assignment operator, destructor). - -And due to `Icecream` objects being copyable, the type has to define an -implementation of the *clone()* method, to avoid type slicing issues. - -Both of these types and their methods will be exposed to Python by -generating CPython code. The code is generated by **shiboken** and -placed in separate ".cpp" files named after each C++ type. The code is -then compiled and linked into a shared library. The shared library is a -CPython extension module, which is loaded by the Python interpreter. - -Beacuse the C++ language has different semantics to Python, shiboken -needs help in figuring out how to generate the bindings code. This is -done by specifying a special XML file called a typesystem file. - -In the typesystem file you specify things like: - * which C++ primitive types should have bindings (int, bool, float) - * which C++ classes should have bindings (Icecream) and what kind of - semantics (value / object) - * Ownership rules (who deletes the C++ objects, C++ or Python) - * Code injection (for various special cases that shiboken doesn't know - about) - * Package name (name of package as imported from Python) - -In this example we declare `bool` and `std::string` as primitive types, -`Icecream` as an object type, `Truck` as a value type, -and the `clone()` and `addIcecreamFlavor(Icecream*)` need additional -info about who owns the parameter objects when passing them across -language boundaries (in this case C++ will delete the objects). - -The `Truck` has getters and setters for the string `arrivalMessage`. -In the type system file, we declare this to be a property in Python: - -``` - -``` - -It can then be used in a more pythonic way: - -``` -special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" -``` - -After shiboken generates the C++ code and CMake makes an extension -module from the code, the types can be accessed in Python simply by -importing them using the original C++ names. - -``` -from Universe import Icecream, Truck -``` - -Constructing C++ wrapped objects is the same as in Python -``` -icecream = Icecream("vanilla") -truck = Truck() -``` - - -And actual C++ constructors are mapped to the Python `__init__` method. -``` -class VanillaChocolateIcecream(Icecream): - def __init__(self, flavor=""): - super().__init__(flavor) -``` - - -C++ methods can be accessed as regular Python methods using the C++ -names -``` -truck.addIcecreamFlavor(icecream) -``` - - -Inheritance works as with regular Python classes, and virtual C++ -methods can be overridden simply by definining a method with the same -name as in the C++ class. -``` -class VanillaChocolateIcecream(Icecream): - # ... - def getFlavor(self): - return "vanilla sprinked with chocolate" - -``` - - -The `main.py` script demonstrates usages of these types. - -The CMake project file contains many comments explaining all the build -rules for those interested in the build process. - -## Building the project - -This example can only be built using **CMake**. -The following requirements need to be met: - -* A PySide package is installed into the current active Python - environment (system or virtualenv) -* A new enough version of CMake (**3.1+**). -* ninja - -For Windows you will also need: -* a Visual Studio environment to be active in your terminal -* Correct visual studio architecture chosen (32 vs 64 bit) -* Make sure that your Python intepreter and bindings project build - configuration is the same (all Release, which is more likely, - or all Debug). - -The build uses the `pyside_config.py` file to configure the project -using the current PySide/Shiboken installation. - -### Using CMake - -You can build and run this example by executing the following commands -(slightly adapted to your file system layout) in a terminal: - -macOS/Linux: -```bash -cd ~/pyside-setup/examples/samplebinding -``` - -On Windows: -```bash -cd C:\pyside-setup\examples\samplebinding -``` - -```bash -mkdir build -cd build -mkdir build -cd build -cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -ninja -ninja install -cd .. -``` - -The final example can then be run by: -```bash -python main.py -``` - -#### Windows troubleshooting - -It is possible that **CMake** can pick up the wrong compiler -for a different architecture, but it can be addressed explicitly -by setting the **CC** environment variable: - -```bash -set CC=cl -``` - -or by using the -G option: - -```bash -cmake -H.. -B. -G "Visual Studio 14 Win64" -``` - -If the `-G "Visual Studio 14 Win64"` option is used, a `sln` file -will be generated, and can be used with `MSBuild` -instead of `nmake/jom`. -The easiest way to both build and install in this case, is to use -the cmake executable: - -```bash -cmake --build . --target install --config Release -``` - -Note that using the "NMake Makefiles JOM" generator is preferred to -the MSBuild one, because the MSBuild one generates configs for both -Debug and Release, and this might lead to building errors if you -accidentally build the wrong config at least once. - -## Virtualenv Support - -If the python application is started from a terminal with an activated -python virtual environment, that environment's packages will be used for -the python module import process. -In this case, make sure that the bindings were built while the -`virtualenv` was active, so that the build system picks up the correct -python shared library and PySide6 / shiboken package. - -## Linux Shared Libraries Notes - -For this example's purpose, we link against the absolute path of the -dependent shared library `libshiboken` because the -installation of the library is done via a wheel, and there is -no clean solution to include symbolic links in a wheel package -(so that passing -lshiboken to the linker would work). - -## Windows Notes - -The build config of the bindings (Debug or Release) should match -the PySide build config, otherwise the application will not properly -work. - -In practice this means the only supported configurations are: - -1. release config build of the bindings + - PySide `setup.py` without `--debug` flag + `python.exe` for the - PySide build process + `python36.dll` for the linked in shared - library. -2. debug config build of the application + - PySide `setup.py` **with** `--debug` flag + `python_d.exe` for the - PySide build process + `python36_d.dll` for the linked in shared - library. - -This is necessary because all the shared libraries in question have to -link to the same C++ runtime library (`msvcrt.dll` or `msvcrtd.dll`). -To make the example as self-contained as possible, the shared libraries -in use (`pyside6.dll`, `shiboken6.dll`) are hard-linked into the build -folder of the application. diff --git a/examples/samplebinding/doc/samplebinding.pyproject b/examples/samplebinding/doc/samplebinding.pyproject new file mode 100644 index 000000000..82c485a09 --- /dev/null +++ b/examples/samplebinding/doc/samplebinding.pyproject @@ -0,0 +1,10 @@ +{ + "files": ["../bindings.h", + "../icecream.cpp", + "../icecream.h", + "../macros.h", + "../main.py", + "../truck.cpp", + "../truck.h", + "../CMakeLists.txt"] +} diff --git a/examples/samplebinding/doc/samplebinding.rst b/examples/samplebinding/doc/samplebinding.rst new file mode 100644 index 000000000..af2e917dc --- /dev/null +++ b/examples/samplebinding/doc/samplebinding.rst @@ -0,0 +1,273 @@ +Sample Bindings Example +======================= + +This example showcases how to generate Python bindings for a +non-Qt C++ library. + +The example defines a CMake project that builds two libraries: + +* ``libuniverse`` - a sample library with two C++ classes. + +* ``Universe`` - the generated Python extension module that contains + bindings to the library above. + +The project file is structured in such a way that a user can copy-paste +in into their own project, and be able to build it with a minimal amount +of modifications. + +Description ++++++++++++ + +The libuniverse library declares two classes: ``Icecream`` and ``Truck``. + +``Icecream`` objects have a flavor, and an accessor for returning the +flavor. + +``Truck`` instances store a vector of ``Icecream`` objects, and have various +methods for adding new flavors, printing available flavors, delivering +icecream, etc. + +From a C++ perspective, ``Icecream`` instances are treated as +*object types* (pointer semantics) because the class declares virtual +methods. + +In contrast ``Truck`` does not define virtual methods and is treated as +a *value type* (copy semantics). + +Because ``Truck`` is a value type and it stores a vector of ``Icecream`` +pointers, the rule of three has to be taken into account (implement the +copy constructor, assignment operator, destructor). + +And due to ``Icecream`` objects being copyable, the type has to define an +implementation of the ``clone()`` method, to avoid type slicing issues. + +Both of these types and their methods will be exposed to Python by +generating CPython code. The code is generated by ``shiboken`` and +placed in separate ``.cpp`` files named after each C++ type. The code is +then compiled and linked into a shared library. The shared library is a +CPython extension module, which is loaded by the Python interpreter. + +Beacuse the C++ language has different semantics to Python, shiboken +needs help in figuring out how to generate the bindings code. This is +done by specifying a special XML file called a typesystem file. + +In the typesystem file you specify things like: + + * which C++ primitive types should have bindings (int, bool, float) + + * which C++ classes should have bindings (Icecream) and what kind of + semantics (value / object) + + * Ownership rules (who deletes the C++ objects, C++ or Python) + + * Code injection (for various special cases that shiboken doesn't know + about) + + * Package name (name of package as imported from Python) + +In this example we declare ``bool`` and ``std::string`` as primitive types, +``Icecream`` as an object type, ``Truck`` as a value type, +and the ``clone()`` and ``addIcecreamFlavor(Icecream*)`` need additional +info about who owns the parameter objects when passing them across +language boundaries (in this case C++ will delete the objects). + +The ``Truck`` has getters and setters for the string ``arrivalMessage``. +In the type system file, we declare this to be a property in Python: + +.. code-block:: xml + + + + +It can then be used in a more pythonic way: + +.. code-block:: python + + special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" + +After shiboken generates the C++ code and CMake makes an extension +module from the code, the types can be accessed in Python simply by +importing them using the original C++ names. + +.. code-block:: python + + from Universe import Icecream, Truck + + +Constructing C++ wrapped objects is the same as in Python + +.. code-block:: python + + icecream = Icecream("vanilla") + truck = Truck() + + +And actual C++ constructors are mapped to the Python `__init__` method. + +.. code-block:: python + + class VanillaChocolateIcecream(Icecream): + def __init__(self, flavor=""): + super().__init__(flavor) + + +C++ methods can be accessed as regular Python methods using the C++ +names + +.. code-block:: python + + truck.addIcecreamFlavor(icecream) + +Inheritance works as with regular Python classes, and virtual C++ +methods can be overridden simply by definining a method with the same +name as in the C++ class. + +.. code-block:: python + + class VanillaChocolateIcecream(Icecream): + # ... + def getFlavor(self): + return "vanilla sprinked with chocolate" + + +The ``main.py`` script demonstrates usages of these types. + +The CMake project file contains many comments explaining all the build +rules for those interested in the build process. + +Building the project +++++++++++++++++++++ + +This example can only be built using ``CMake``. +The following requirements need to be met: + +* A PySide package is installed into the current active Python + environment (system or virtualenv) + +* A new enough version of CMake (3.1+). + +* ninja + +For Windows you will also need: + +* a Visual Studio environment to be active in your terminal + +* Correct visual studio architecture chosen (32 vs 64 bit) + +* Make sure that your Python intepreter and bindings project build + configuration is the same (all Release, which is more likely, + or all Debug). + +The build uses the ``pyside_config.py`` file to configure the project +using the current PySide/Shiboken installation. + +Using CMake +=========== + +You can build and run this example by executing the following commands +(slightly adapted to your file system layout) in a terminal: + +macOS/Linux: + +.. code-block:: bash + + cd ~/pyside-setup/examples/samplebinding + +On Windows: + +.. code-block:: bash + + cd C:\pyside-setup\examples\samplebinding + +.. code-block:: bash + + mkdir build + cd build + mkdir build + cd build + cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release + ninja + ninja install + cd .. + +The final example can then be run by: + +.. code-block:: bash + + python main.py + +Windows troubleshooting ++++++++++++++++++++++++ + +It is possible that ``CMake`` can pick up the wrong compiler +for a different architecture, but it can be addressed explicitly +by setting the ``CC`` environment variable: + +.. code-block:: bash + + set CC=cl + +or by using the -G option: + +.. code-block:: bash + + cmake -H.. -B. -G "Visual Studio 14 Win64" + +If the ``-G "Visual Studio 14 Win64"`` option is used, a ``sln`` file +will be generated, and can be used with ``MSBuild`` +instead of ``nmake/jom``. +The easiest way to both build and install in this case, is to use +the cmake executable: + +.. code-block:: bash + + cmake --build . --target install --config Release + +Note that using the ``"NMake Makefiles JOM"`` generator is preferred to +the MSBuild one, because the MSBuild one generates configs for both +Debug and Release, and this might lead to building errors if you +accidentally build the wrong config at least once. + +Virtualenv Support +++++++++++++++++++ + +If the python application is started from a terminal with an activated +python virtual environment, that environment's packages will be used for +the python module import process. +In this case, make sure that the bindings were built while the +``virtualenv`` was active, so that the build system picks up the correct +python shared library and PySide6 / shiboken package. + +Linux Shared Libraries Notes +++++++++++++++++++++++++++++ + +For this example's purpose, we link against the absolute path of the +dependent shared library ``libshiboken`` because the +installation of the library is done via a wheel, and there is +no clean solution to include symbolic links in a wheel package +(so that passing -lshiboken to the linker would work). + +Windows Notes ++++++++++++++ + +The build config of the bindings (Debug or Release) should match +the PySide build config, otherwise the application will not properly +work. + +In practice this means the only supported configurations are: + +#. release config build of the bindings + + PySide ``setup.py`` without ``--debug`` flag + ``python.exe`` for the + PySide build process + ``python36.dll`` for the linked in shared + library. + +#. debug config build of the application + + PySide ``setup.py`` *with* ``--debug`` flag + ``python_d.exe`` for the + PySide build process + ``python36_d.dll`` for the linked in shared + library. + +This is necessary because all the shared libraries in question have to +link to the same C++ runtime library (``msvcrt.dll`` or ``msvcrtd.dll``). +To make the example as self-contained as possible, the shared libraries +in use (``pyside6.dll``, ``shiboken6.dll``) are hard-linked into the build +folder of the application. -- cgit v1.2.3