Skip to content

Commit dd5590c

Browse files
committed
lot of cleaning up
1 parent 57d1c07 commit dd5590c

File tree

28 files changed

+333
-135
lines changed

28 files changed

+333
-135
lines changed

README.md

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
# pybind11
1+
# pybind11 as a scripting engine
2+
3+
This tutorial explains in 14 steps how to use `pybind11` for creating a Python scripting engine for a Qt application.
4+
5+
It first explains how to create a module that can be imported in Python, continues by prosenting different aspects of running inside of a C++ (Qt) application a Python script that can access (and modify) the status of the main application.
6+
7+
Each step contains code that can be individually compiled, run, and – of course – modified.
8+
9+
You should probably go through each step in the order presented below, since the commands are (briefly) introduced only once.
210

311
## Getting pybind11
412

5-
By default, the samples in this repositories use a pre-compiled version of pybind11.
13+
There are three ways for getting pybind11:
614

7-
You will have to first get pybind11, compile it, and install it:
15+
- _Embedding_ it in your project as a Git submodule.
16+
- Installing pybind11 through your package manager (or downloading binaries that are installed in the system path).
17+
- Compiling pybind11 in a separate directory.
18+
19+
20+
The samples in this repositories use a pre-compiled version of pybind11. If you're not getting pybind11 from your distribution repositories:
821

922
- get the code from github
1023
- compile it:
@@ -21,43 +34,52 @@ You need:
2134
- a development environment for C++ (g++ or clang; cmake, ...)
2235
- the Python bindings (`libpython3-dev`, `python-cxx-dev`, ...)
2336

24-
Our [first example](add/) directly uses the compiler, all other examples will need a modern version of `cmake` being installed (pybind11 should have cmake 3.4)
37+
Our [first example](add/) directly uses the compiler, all other examples will need a modern version of `cmake` being installed (pybind11 needs cmake 3.4)
2538

2639
## Sample projects
2740

28-
- [`add/`](add/): using a c++ module to add two numbers in Python
29-
- [`add-cmake`](add-cmake/): the first example plus cmake
30-
- [`classes/`](classes/): let Python use C++ classes
41+
- [`add/`](add/): create a module providing a C++ `add` function that can be add two numbers.
42+
- [`add-cmake`](add-cmake/): the first example with CMake (all further examples use CMake).
43+
- [`classes/`](classes/): let Python use C++ classes.
3144
- [`eval-file/`](eval-file/): C++ evaluates a Python script in an external files that modifies the state of a C++ variable.
32-
- [`eval-set-object/`](eval-set-object/): Create two objects of type `Foo` and pass them to a Python script (defined as a string in the C++ code)
33-
- [`eval-set-object-module/`](eval-set-object-module/): Create a `.so` module for the `Foo` type, load it get the Python script to access it.
45+
- [`eval-set-object/`](eval-set-object/): Create two objects of type `Foo` and pass them by value or by reference to a Python script (defined as a string in the C++ code)
46+
- [`eval-set-object-module/`](eval-set-object-module/): Create a `.so` module for the `Foo` type, load it in the Python interpreter and let the Python script access it.
3447
- [`eval-file-pyqt5/`](eval-file-pyqt5/): The C++ code runs an external Python script that shows a PyQt5 alert.
48+
- [`eval-file-pyqt5-set-value/`](eval-file-pyqt5-set-value/): Setting a C++ value from a PyQt5 input dialog.
3549
- ...
3650

3751
## Links
3852

39-
- and HN thread: <https://news.ycombinator.com/item?id=15095757>
53+
- an HN thread: <https://news.ycombinator.com/item?id=15095757>
4054
- how to use cmake with pybind11 <https://github.com/pybind/pybind11/pull/1098>
4155

4256
## Notes
4357

4458
- pybind11 runs python code as if it was run in Python's `exec()` call. The visibility of the global and local variables works in the same way: <https://gist.github.com/dean0x7d/df5ce97e4a1a05be4d56d1378726ff92>
59+
- read https://github.com/GooFit/GooFit/blob/master/python/goofit/Variable.cpp as an example for...
4560

4661
## alternatives
4762

4863
- [cppy](https://pypi.python.org/pypi/cppyy) is an automatic Python-C++ bindings generator designed for large scale programs in high performance computing that use modern C++.
4964

5065
## todo
5166

52-
document all directories:
67+
- [ ] adapt all `CMakeLists.txt` files to require Python 3.7. (`eval-set-object-module` does somehow require it)
68+
- [ ] use pyside2 on top of pyqt5
69+
- [ ] can python use the c++ qt widgets (add an entry to the menus, add keyboard shortcuts); can we have callbacks back to python code from the c++ app?
70+
- [ ] in the qt5-pyqt example there is a "visibility" warning.
71+
- [ ] Try to add a menu entry from pyqt5
72+
- [ ] Try with PySide2
73+
74+
Document all directories:
5375

5476
- [x] add
5577
- [x] add-cmake
56-
- [ ] classes
57-
- [ ] eval-file
58-
- [ ] eval-set-object
59-
- [ ] eval-set-object-module
60-
- [ ] eval-file-pyqt5
78+
- [x] classes
79+
- [x] eval-file
80+
- [x] eval-set-object
81+
- [x] eval-set-object-module
82+
- [x] eval-file-pyqt5
6183
- [ ] eval-file-pyqt5-set-value
6284
- [ ] scripter-api
6385
- [ ] scripter-class

add-cmake/README.md

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,49 @@
11
# pybind11 and cmake
22

3-
This second example shows how to use cmake to simplify the compilation of the [add example](../add).
3+
This second example shows how to use cmake to compile the [add example](../add).
44

5-
Using `cmake` avoid us the use of the complex `g++` call in the [add example](../add).
6-
7-
Like all examples in this repository we're not binding `pybind11` as a git submodule but compile it separately in `~/bin`
5+
All further examples, will use cmake.
86

97
The `pybind11_DIR` passed to the cmake commands tells the compiler where to find the library.
108

11-
There is an official demo project that fetches pybind11 as a git submodule: <https://github.com/pybind/cmake_example>.
9+
This sample uses a "pre-compiled" version of pybind11. If you prefer using git submodules, please refer to the official demo project that fetches pybind11 as a git submodule: <https://github.com/pybind/cmake_example>.
10+
11+
## The `CMakeLists.txt` file
12+
13+
```cmake
14+
cmake_minimum_required(VERSION 3.4)
15+
project(maths)
16+
17+
set(PYBIND11_PYTHON_VERSION 3.4)
18+
19+
find_package(pybind11 CONFIG)
20+
21+
pybind11_add_module(maths
22+
src/maths.cpp
23+
)
24+
```
25+
26+
## Compiling and running
1227

13-
~~~.sh
28+
```.sh
1429
$ mkdir build
1530
$ cd build
16-
$ cmake -Dpybind11_DIR=~/bin/pybind11/share/cmake/pybind11 ..
31+
$ cmake ..
1732
$ make
33+
```
34+
35+
If cmake is not installed in a standard path, you'll need to tell cmake where it is installed by providing `pybind11_DIR` :
36+
37+
```.sh
38+
$ cmake -Dpybind11_DIR=~/bin/pybind11/share/cmake/pybind11 ..
39+
```
40+
41+
You will now have a file with a name similar to `maths.cpython-37m-x86_64-linux-gnu.so` which you can import in python3:
42+
43+
44+
```sh
1845
$ python3
1946
>>> import maths
20-
>>> maths.add(1,2)
47+
>>> maths.add(1, 2)
2148
3
22-
~~~
49+
```

add/README.md

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
11
# Add: the first example
22

3-
This first example shows how to create a python module providing the `add()` function that returns the sum of the two integers given as a parameter.
3+
This first example shows how to program in C++ a python module providing an `add()` function.
44

5-
This is the compile command that works for me:
5+
When imported in Pyhton, `add(a, b)` returns the sum of the two integers given as a parameter.
66

7-
```.sh
8-
c++ -O3 -shared -fPIC -std=c++11 -I ~/src/pybind11/include -I /usr/include/python3.5 -L /usr/lib/python3 `python-config --cflags --ldflags` maths.cpp -o maths.so
7+
A very simple C++ library provides a function that returns the sum of two integer numbers:
8+
9+
10+
```cpp
11+
#include <pybind11/pybind11.h>
12+
13+
namespace py = pybind11;
14+
15+
int add(int i, int j)
16+
{
17+
return i + j;
18+
}
19+
20+
PYBIND11_MODULE(maths, m) {
21+
m.doc() = "pybind11 example plugin"; // optional module docstring
22+
23+
m.def("add", &add, "A function which adds two numbers");
24+
}
925
```
1026
11-
As a result ou will get the python library `maths.so`.
27+
The `PYBIND11_MODULE` macro creates a `maths` module:
28+
29+
- it first sets the documentation string for the module, then
30+
- defines:
31+
- a Python function called `add`,
32+
- that will call the C++ `add` function passed as a reference,
33+
- and finally defines a documentation string for the `add` function.
1234
13-
You can now import the "maths" module in Python3. Start Python3 in the directory where the file is located and:
35+
If everything is setup _correctly_, you can compile the `maths.cpp` file with:
36+
37+
```sh
38+
c++ -O3 -shared -fPIC `python-config --cflags --ldflags` src/maths.cpp -o maths.so
39+
```
40+
41+
- If you're compiler does not default to (at least) C++ 11, you will need `-std=c++11`
42+
- If you have a _local_ version of `pybind11` you will need `-fPIC -std=c++11 -I ~/src/pybind11/include`
43+
- If your system does not defaul to Python 3, `-I /usr/include/python3.7 -L /usr/lib/python3`
44+
45+
As a result you will get the python library `maths.so`.
46+
47+
You can now import the "maths" module in Python3. Start Python3 in the directory where the `.so` file is located and:
1448

1549
```.py
1650
>>> import maths
@@ -19,27 +53,33 @@ You can now import the "maths" module in Python3. Start Python3 in the directory
1953
>>>
2054
```
2155

22-
# Notes
56+
You can also see the documentation with
2357

24-
the pybind11 documentation is suggesting the following:
58+
```.py
59+
>>> import maths
60+
>>> help(maths)
61+
Help on module maths:
2562

26-
```.sh
27-
c++ -O3 -shared -std=c++11 -I <path-to-pybind11>/include `python-config --cflags --ldflags` maths.cpp -o maths.so
28-
```
63+
NAME
64+
maths - pybind11 example plugin
2965

30-
but it fails for me.
66+
FUNCTIONS
67+
add(...) method of builtins.PyCapsule instance
68+
add(arg0: int, arg1: int) -> int
3169

32-
First I need to tell the compiler to use Python3 (since Debian and many other Linux distributions still default to Python2):
70+
A function which adds two numbers
3371

34-
```.sh
35-
c++ -O3 -shared -fPIC -std=c++11 -I ~/src/pybind11/include -I /usr/include/python3.5 -L /usr/lib/python3 `python-config --cflags --ldflags` maths.cpp -o maths.so
36-
```
72+
FILE
73+
/home/ale/src/cpp-pybind11-playground/add/maths.so
74+
```
3775

38-
(<https://docs.python.org/3/extending/embedding.html#compiling-and-linking-under-unix-like-systems>)
76+
## Notes
3977

40-
Then I need to add a `main()` function in the `.cpp` file.
78+
My full g++ command is:
4179

42-
Question: (how) can I compile a pure library without any main?
80+
```
81+
g++ -O3 -shared -fPIC -std=c++11 -I ~/src/pybind11/include -I /usr/include/python3.7 -L /usr/lib/python3 `python-config --cflags --ldflags` src/maths.cpp -o maths.so
82+
```
4383

4484
## Further steps
4585

add/maths.cpp

Lines changed: 0 additions & 18 deletions
This file was deleted.

add/src/maths.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <pybind11/pybind11.h>
2+
3+
namespace py = pybind11;
4+
5+
int add(int i, int j)
6+
{
7+
return i + j;
8+
}
9+
10+
PYBIND11_MODULE(maths, m) {
11+
m.doc() = "pybind11 example plugin"; // optional module docstring
12+
13+
m.def("add", &add, "A function which adds two numbers");
14+
}

classes/README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,34 @@
22

33
The `pet` module provides the `Dog` and `Cat` classes.
44

5-
Both classes have the `getName()` and `setName()` class functions.
5+
Both C++ classes have a `getName()` and a `setName()` class functions.
66

7-
`Dog` is added to the Python module with a getter and a setter.
7+
The `Dog` and `Cat` classes are exposed in a different way to the Python code:
88

9-
`Cat` is more _pythonic_ and can access the C++ accessor as a property.
9+
- `Dog` is added to the Python module with a getter and a setter.
10+
- `Cat` is more _pythonic_ and provides access to the C++ accessors as a property.
1011

11-
~~~.sh
12+
In C++, both classes are defined in the exact same way, but:
13+
14+
- the `get` and `set` function of the `Dog` class are bound to functions of the same name in Python
15+
- for the `Cat` the `name` class property on the Python side is _using_ the C++ getter and setter to read and write the `name` value.
16+
17+
```py
18+
py::class_<Dog>(m, "Dog")
19+
.def(py::init<const std::string &>())
20+
.def("setName", &Dog::setName, "Setting the dog's name")
21+
.def("getName", &Dog::getName), "Getting the dog's name";
22+
23+
py::class_<Cat>(m, "Cat")
24+
.def(py::init<const std::string &>())
25+
.def_property("name", &Cat::getName, &Cat::setName, "The cat name");
26+
```
27+
28+
You can compile and import the `pet` module, containing a `Dog` and a `Cat` classes:
29+
30+
```.sh
1231
$ mkdir build
13-
$ cmake -Dpybind11_DIR=~/bin/pybind11/share/cmake/pybind11 ..
32+
$ cmake ..
1433
$ make
1534
$ python3
1635
>>> import pet
@@ -28,5 +47,4 @@ $ python3
2847
>>> b.name = 'Fix'
2948
>>> b.name
3049
'Fix'
31-
~~~
32-
50+
```

classes/src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ PYBIND11_MODULE(pet, m) {
3535

3636
py::class_<Cat>(m, "Cat")
3737
.def(py::init<const std::string &>())
38-
.def_property("name", &Cat::getName, &Cat::setName);
38+
.def_property("name", &Cat::getName, &Cat::setName, "The cat name");
3939
}
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
cmake_minimum_required(VERSION 3.4)
22
project(scripting)
33

4-
set(PYBIND11_PYTHON_VERSION 3.4)
4+
set(PYBIND11_PYTHON_VERSION 3.7)
55

6-
FIND_PACKAGE(pybind11 CONFIG)
6+
find_package(pybind11 CONFIG)
77

8-
ADD_EXECUTABLE(scripting src/main.cpp)
9-
TARGET_LINK_LIBRARIES(scripting ${PYTHON_LIBRARIES})
10-
TARGET_LINK_LIBRARIES(scripting pybind11::embed)
8+
add_executable(scripting
9+
src/main.cpp
10+
)
11+
12+
target_link_libraries(scripting
13+
pybind11::embed
14+
)
15+
16+
configure_file(
17+
${CMAKE_CURRENT_SOURCE_DIR}/python/input-number.py
18+
${CMAKE_CURRENT_BINARY_DIR}
19+
COPYONLY
20+
)

eval-file-pyqt5-set-value/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
# A python script running a PyQt5 input dialog and setting a c++ variable with the value
1+
# Setting a C++ value from a PyQt5 input dialog
2+
3+
The C++ program defines a `set_the_answer` lambda that can receive the new value and passes it as `local` to the `input-number.py` script.
4+
5+
The Python script creates an (PyQt) application that is showing an input dialog. If the _Ok_ button is pressed, the `set_the_answer()` function passes the value to the C++ code.
6+
7+
It is to be noted that `pybind11` is passing the `set_the_answer()` as a local to the _eval_ (exec, in reality) Python context: we need to explicitely pass the function to the `get_the_value()` to make it visible inside of it.
8+
(This is becaue Python executes the code as if it was in a class environment... I've been told.)
29

310
~~~.sh
411
$ mkdir build
5-
$ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 ..
12+
$ cd build
13+
$ cmake ..
614
$ make
715
$ ./scripting
816
~~~

0 commit comments

Comments
 (0)