Skip to content

Commit dd5590c

Browse files
committedFeb 20, 2019
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

+38-16
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

+36-9
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

+59-19
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

-18
This file was deleted.

‎add/src/maths.cpp

+14
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

+25-7
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

+1-1
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
}
+15-5
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

+10-2
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
~~~

‎eval-file-pyqt5-set-value/input-number.py

-20
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import sys
2+
3+
def get_the_value(locals):
4+
from PyQt5.QtWidgets import QInputDialog
5+
num,ok = QInputDialog.getInt(None, "The number to be set", "enter a number")
6+
if ok:
7+
locals["set_the_answer"](num)
8+
set_the_answer(num)
9+
10+
if __name__ == '__main__':
11+
from PyQt5.QtWidgets import QApplication
12+
# app = QApplication(sys.argv)
13+
app = QApplication(['myname'])
14+
get_the_value(locals())

‎eval-file-pyqt5-set-value/src/main.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@ class Dog
1414

1515
int main()
1616
{
17-
// http://pybind11.readthedocs.io/en/latest/advanced/embedding.html#getting-started
18-
py::scoped_interpreter guard{}; // start the interpreter and keep it alive
17+
py::scoped_interpreter guard{};
1918

2019
auto local = py::dict();
21-
local["y"] = py::int_(43);
2220

2321
int val_out;
2422
local["set_the_answer"] = py::cpp_function([&](int value) { val_out = value; });
2523

26-
py::eval_file("../input-number.py", py::globals(), local);
24+
py::eval_file("input-number.py", py::globals(), local);
2725

2826
std::cout << "value set by the python script: " << val_out << std::endl;
2927
}

‎eval-file-pyqt5/README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
The C++ code runs an external Python script that shows a PyQt5 alert.
44

5+
Warning: we need to create a `QApplication` before being able to use a `QWidget`
6+
57
~~~.sh
68
$ mkdir build
79
$ cd build
8-
$ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11
10+
$ cmake ..
911
$ make
1012
$ ./scripting
1113
~~~
14+
15+
Todo:
16+
17+
- [ ] in the Python script use a function `ask_question()` instead of the class.

‎eval-file-pyqt5/src/main.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ namespace py = pybind11;
44

55
int main()
66
{
7-
// http://pybind11.readthedocs.io/en/latest/advanced/embedding.html#getting-started
8-
py::scoped_interpreter guard{}; // start the interpreter and keep it alive
7+
py::scoped_interpreter guard{};
98

109
py::object scope = py::module::import("__main__").attr("__dict__");
1110
py::eval_file("pyqt5.py", scope);

‎eval-file/README.md

+55-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,69 @@
11
# Eval a python script that modifies a C++ object
22

3-
From the C++ code we run a Python script that modifies the state of a C++ variable by calling a C++ function.
3+
This example shows how to create a C++ program that _runs_ a Python script that has access to variables and functions defined in the C++ program itself.
44

5-
In the C++ program we define a `local` list that contains the variable and the function passed to the Python file when evaluating it.
5+
The first step is to include the `pybind11`'s `embed.h` library:
6+
7+
```cpp
8+
#include <pybind11/embed.h>
9+
```
10+
11+
We then need to load the Python interpreter:
12+
13+
```cpp
14+
py::scoped_interpreter guard{};
15+
```
16+
17+
We do not directly use it and it will be automatically destroyed when it goes out of scope.
18+
19+
In the _local_ environment variable, we create a variable and a function that will be injected in the the Python script's environment:
20+
21+
```cpp
22+
auto local = py::dict();
23+
local["y"] = ...;
24+
local["set_the_answer"] = ...;
25+
```
26+
27+
The `scope` variable _contains_ the scope of the main module of our interpreter:
28+
29+
```cpp
30+
py::object scope = py::module::import("__main__").attr("__dict__");
31+
```
32+
33+
We can finally _evaluate_ our Python script:
34+
35+
```cpp
36+
py::eval_file("set-the-y.py", scope, local);
37+
```
38+
39+
What does the script do?
40+
41+
```py
42+
set_the_answer(y - 1)
43+
```
44+
45+
It simply calls the `set_the_answer()` function we have defined by passing the _injected_ variable `y` decremented by one.
46+
47+
The final effect is that the `val_out` variable we have defined in our C++ code will now have a value of 42 (`y - 1`) instead of the original `7`.
48+
49+
You can try it with:
650

751
~~~.sh
852
$ mkdir build
953
$ cd build
10-
$ cmake -Dpybind11_DIR=~/bin/pybind11/share/cmake/pybind11 ..
54+
$ cmake
1155
$ make
1256
$ ./scripting
1357
~~~
1458

59+
With the `configure_file` command, `CMakeLists.txt` copies the Python script into the build directory.
60+
61+
You can modify the Python Script in your build directory and see that the value printed by the `scripting` program will change accordingly.
62+
63+
## Todo
64+
65+
- What does it mean that we cannot have two concurrent interpreters (as mentioned in the pybind11 documentation)?
66+
1567
## References
1668

1769
- http://pybind11.readthedocs.io/en/latest/advanced/embedding.html

‎eval-file/src/main.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ namespace py = pybind11;
55

66
int main()
77
{
8-
// http://pybind11.readthedocs.io/en/latest/advanced/embedding.html#getting-started
8+
// http://pybind11.readthedocs.io/en/latest/advanced/embedding.html#getting-started
99
py::scoped_interpreter guard{}; // start the interpreter and keep it alive
1010

1111
// inject the y variable into the python script
1212
auto local = py::dict();
1313
local["y"] = py::int_(43);
1414

15-
// inject the set_the_answer() funciton into the python script.
16-
// set_the_answer() modifies the c++ variable val_out
15+
// inject the set_the_answer() lambda into the python script.
1716
int val_out{7};
1817
local["set_the_answer"] = py::cpp_function([&](int value) { val_out = value; });
1918

‎eval-set-object-module/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
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

66
find_package(pybind11 CONFIG)
77

88
add_executable(scripting
9+
src/foo.cpp
910
src/main.cpp
1011
)
1112
target_link_libraries(scripting

‎eval-set-object-module/README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
# C++ launches a Python script that loads a shared library module and sets a c++ variable
1+
# C++ creates and loads a module and runs a Python script that uses the module to modify its own state
22

3-
- Use the C++ foo.[h|cpp] files to create a `.so` shared object (module)
4-
- Load the module from the C++ main program Python.
5-
- Call the Python script from the C++ code and pass one variable by value and the other by reference.
6-
- The Python script modifies the variable.
7-
- The c++ code checks that the values has been modified
3+
The C++ `foo.h` and `foo.cpp` provide the `fooapi` Python module (a `.so` shared object defining the `Foo` class with a `bar` member variable (an integer initialized to `1`).
4+
5+
The `main.cpp` program includes the `foo.h` library and imports the `fooapi` into the Python interpreter.
6+
7+
It calls the usual Python script with two variables, one by copy and one by reference.
88

99
~~~.sh
1010
$ mkdir build
1111
$ cd build
12-
$ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 ..
12+
$ cmake ..
1313
$ make
1414
$ ./scripting
1515
~~~

‎eval-set-object-module/src/main.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ using namespace pybind11::literals;
1010

1111
int main()
1212
{
13-
py::scoped_interpreter guard{}; // start the interpreter and keep it alive
13+
py::scoped_interpreter guard{};
1414

1515
Foo foo1, foo2;
1616

1717
auto module = py::module::import("fooapi");
1818

19-
auto locals = py::dict("foo_copy"_a=foo1, "foo_ref"_a=py::cast(foo2, py::return_value_policy::reference)); // foo1 by value, foo2 by reference
19+
auto locals = py::dict(
20+
"foo_copy"_a = foo1,
21+
"foo_ref"_a = py::cast(foo2,
22+
py::return_value_policy::reference));
2023

2124
py::eval_file("set-bar.py", py::globals(), locals);
2225

2326
assert(foo1.bar == 1);
2427
assert(foo2.bar == 5);
25-
2628
}

‎eval-set-object/CMakeLists.txt

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ project(scripting)
33

44
set(PYBIND11_PYTHON_VERSION 3.4)
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(
9+
scripting src/main.cpp
10+
)
11+
12+
target_link_libraries(scripting pybind11::embed)

‎eval-set-object/README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
# A python script running a PyQt5 input dialog and setting a c++ variable with the value
1+
# Evaluate a Python script that modifies a C++ object
2+
3+
Create two C++ objects `foo_1` and `foo_2` of type `Foo` and pass them to a Python script (defined as a string in the C++ code):
4+
5+
- the first object is passed by value,
6+
- the second one by reference.
7+
8+
The Python script modifies both values and the C++ code then checks that the first object has not changed but the second has.
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
~~~
917

18+
The output from the Python script shows that the bar in `foo_copy` initially has the value `1`, but then gets the value `5`.
19+
20+
Outside of the script, back in the C++ code, the two asserts confirm that the bar in `foo_1` (passted by copy) still has the value `1` and the one in `foo_2` is now `5`.
21+
22+
## References
23+
1024
see http://pybind11.readthedocs.io/en/master/advanced/embedding.html

‎eval-set-object/src/main.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,25 @@ PYBIND11_EMBEDDED_MODULE(foo_module, m) {
1717

1818
int main()
1919
{
20-
py::scoped_interpreter guard{}; // start the interpreter and keep it alive
20+
py::scoped_interpreter guard{};
2121

22-
Foo foo1, foo2;
22+
Foo foo_1, foo_2;
2323

2424
auto module = py::module::import("foo_module");
2525

26-
auto locals = py::dict("foo_copy"_a=foo1, "foo_ref"_a=py::cast(foo2, py::return_value_policy::reference), **module.attr("__dict__")); // foo1 by value, foo2 by reference
26+
auto locals = py::dict(
27+
"foo_copy"_a=foo_1, // by value
28+
"foo_ref"_a=py::cast(foo_2, // by reference
29+
py::return_value_policy::reference),
30+
**module.attr("__dict__"));
2731
py::exec(R"(
2832
print(foo_copy.bar);
2933
foo_copy.bar = 5;
3034
print(foo_copy.bar);
3135
foo_ref.bar = 5;
3236
)", py::globals(), locals);
3337

34-
assert(foo1.bar == 1);
35-
assert(foo2.bar == 5);
38+
assert(foo_1.bar == 1);
39+
assert(foo_2.bar == 5);
3640

3741
}

‎qt5-pyqt5/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
~~~.sh
44
$ mkdir build
5-
$ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 -DCMAKE_BUILD_TYPE=Debug ..
5+
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
66
$ make
77
$ ./sample
88
~~~

‎qt5-pyqt5/python/set-bar.py

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22

33
print(Sample.bar)
44
Sample.bar = 5
5-

‎scripter-api/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ target_link_libraries(scripting
1616
pybind11::embed
1717
)
1818

19-
PYBIND11_ADD_MODULE(scripterapi
19+
pybind11_add_module(scripterapi
2020
src/scripterAPI/scripterAPI.cpp
2121
src/scripterAPI/document.cpp
2222
src/scripterAPI/margin.cpp

‎scripter-api/python/set-bar.py

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
print("python page 1 margin.top "+str(document.getPage(1).margin.top))
1313
Sample.document.getPage(1).margin.top = 42
1414
print("python page 1 margin.top "+str(document.getPage(1).margin.top))
15+
# Sample.document.addPage()

‎scripter-api/src/scripterAPI/document.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ void init_DocumentAPI(py::module &m) {
88
py::class_<ScripterAPI::Document>(m, "Document")
99
.def(py::init<>())
1010
.def("getPage", &ScripterAPI::Document::getPage)
11+
.def("addPage", &ScripterAPI::Document::addPage)
1112
.def_property_readonly("margin", &ScripterAPI::Document::getMargin)
1213
;
1314
}

‎scripter-api/src/scripterAPI/document.h

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define SCRIPTERAPIDOCUMENT_H
33

44
#include "sample/document.h"
5+
#include "sample/page.h"
56
#include "scripterAPI/margin.h"
67
#include "scripterAPI/page.h"
78

@@ -13,6 +14,10 @@ namespace ScripterAPI {
1314
}
1415
Margin getMargin() { return Margin(document->margin); }
1516
Page getPage(int i) { return Page(document, i); }
17+
void addPage() {
18+
Sample::Page page;
19+
document->page.push_back(page);
20+
}
1621
private:
1722
Sample::Document* document;
1823
};

0 commit comments

Comments
 (0)
Please sign in to comment.