diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-12-07 15:21:37 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-12-08 12:12:16 +0100 |
commit | f029a27152cf7c7a741ce03a6f91be71729093ca (patch) | |
tree | 06fab915acc36e5598f3b6469706a03d16453d0a /examples/samplebinding/doc | |
parent | f3972822d254ad20b8e9582f89fe8e30ce931f5c (diff) |
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 <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'examples/samplebinding/doc')
-rw-r--r-- | examples/samplebinding/doc/samplebinding.pyproject | 10 | ||||
-rw-r--r-- | examples/samplebinding/doc/samplebinding.rst | 273 |
2 files changed, 283 insertions, 0 deletions
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 + + <property type="std::string" name="arrivalMessage" get="getArrivalMessage" set="setArrivalMessage"/> + + +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. |