aboutsummaryrefslogtreecommitdiffstats
path: root/examples/samplebinding/doc/samplebinding.rst
diff options
context:
space:
mode:
Diffstat (limited to 'examples/samplebinding/doc/samplebinding.rst')
-rw-r--r--examples/samplebinding/doc/samplebinding.rst288
1 files changed, 288 insertions, 0 deletions
diff --git a/examples/samplebinding/doc/samplebinding.rst b/examples/samplebinding/doc/samplebinding.rst
new file mode 100644
index 000000000..defb55d6b
--- /dev/null
+++ b/examples/samplebinding/doc/samplebinding.rst
@@ -0,0 +1,288 @@
+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 five has to be taken into account (implement the
+copy constructor, assignment operator, move constructor, move assignment
+operator and 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++ classes should have bindings (Icecream, Truck) and with 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 ``Icecream`` as an object type and ``Truck``
+as a value type. 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.16+).
+
+* 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
+ cmake -S.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe
+ ninja
+ ninja install
+ cd ..
+
+Use the Python module
++++++++++++++++++++++
+
+The final example can then be run by:
+
+.. code-block:: bash
+
+ python main.py
+
+In the ``main.py`` script, two types are derived from :code:`Icecream` for
+different “flavors” after importing the classes from the :code:`Universe`
+module. Then, a :code:`truck` is created to deliver some regular flavored
+Icecreams and two special ones.
+
+If the delivery fails, a new :code:`truck` is created with the old flavors
+copied over, and a new *magical* flavor that will surely satisfy all customers.
+
+Try running it to see if the ice creams are delivered.
+
+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
+
+passing the compiler on the command line:
+
+.. code-block:: bash
+
+ cmake -S.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
+
+or by using the -G option:
+
+.. code-block:: bash
+
+ cmake -S.. -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 ``ninja``.
+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 ``"Ninja"`` 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 + ``python39.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 + ``python39_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.