├── eval-file ├── setDogName.py ├── python │ └── set-the-y.py ├── CMakeLists.txt ├── src │ └── main.cpp └── README.md ├── scripter-class ├── src │ ├── foo.h │ ├── foo.cpp │ ├── scripter.h │ ├── main.cpp │ └── scripter.cpp ├── python │ └── set-bar.py ├── CMakeLists.txt └── README.md ├── eval-set-object-module ├── src │ ├── foo.h │ ├── foo.cpp │ └── main.cpp ├── python │ └── set-bar.py ├── CMakeLists.txt └── README.md ├── qt5-pyqt5 ├── python │ └── set-bar.py ├── src │ ├── scripterAPI.h │ ├── sample.h │ ├── sample.cpp │ ├── main.cpp │ ├── mainwindow.h │ ├── scripterAPI.cpp │ ├── scripter.cpp │ ├── scripter.h │ └── mainwindow.cpp ├── README.md └── CMakeLists.txt ├── scripter-class-in-class ├── python │ └── set-bar.py ├── src │ ├── main.cpp │ ├── bar.cpp │ ├── bar.h │ ├── foo.h │ ├── scripter.h │ ├── foo.cpp │ └── scripter.cpp ├── CMakeLists.txt └── README.md ├── add-cmake ├── CMakeLists.txt ├── src │ └── maths.cpp └── README.md ├── classes ├── CMakeLists.txt ├── src │ └── main.cpp └── README.md ├── eval-file-pyqt5 ├── src │ └── main.cpp ├── CMakeLists.txt ├── README.md └── python │ └── pyqt5.py ├── eval-file-pyside6 ├── src │ └── main.cpp ├── CMakeLists.txt └── python │ └── message_box.py ├── eval-set-object ├── CMakeLists.txt ├── README.md └── src │ └── main.cpp ├── eval-pyside6-twice ├── CMakeLists.txt ├── README.md └── src │ └── main.cpp ├── scripting ├── README.md ├── src │ ├── pet.h │ ├── api-pet.cpp │ └── main.cpp ├── CMakeLists.txt └── cmake │ └── FindPybind11.cmake ├── add ├── src │ └── maths.cpp └── README.md ├── scripter-api ├── src │ ├── scripterAPI │ │ ├── page.cpp │ │ ├── scripterAPI.h │ │ ├── document.cpp │ │ ├── page.h │ │ ├── margin.cpp │ │ ├── document.h │ │ ├── scripterAPI.cpp │ │ └── margin.h │ ├── sample │ │ ├── page.h │ │ ├── document.h │ │ └── margin.h │ ├── scripter.h │ ├── main.cpp │ └── scripter.cpp ├── CMakeLists.txt ├── python │ └── set-bar.py └── README.md ├── eval-file-pyqt5-set-value ├── CMakeLists.txt ├── src │ └── main.cpp ├── python │ └── input-number.py └── README.md ├── qt5-signals └── README.md └── README.md /eval-file/setDogName.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | print("tim") 3 | -------------------------------------------------------------------------------- /scripter-class/src/foo.h: -------------------------------------------------------------------------------- 1 | struct Foo { 2 | int bar = 1; 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /eval-set-object-module/src/foo.h: -------------------------------------------------------------------------------- 1 | struct Foo { 2 | int bar = 1; 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /qt5-pyqt5/python/set-bar.py: -------------------------------------------------------------------------------- 1 | import sampleapi 2 | 3 | print(Sample.bar) 4 | Sample.bar = 5 5 | -------------------------------------------------------------------------------- /scripter-class-in-class/python/set-bar.py: -------------------------------------------------------------------------------- 1 | import fooapi 2 | 3 | print(foo.bar.value) 4 | bar = foo.bar 5 | bar.value = 5 6 | -------------------------------------------------------------------------------- /eval-set-object-module/python/set-bar.py: -------------------------------------------------------------------------------- 1 | print(foo_copy.bar) 2 | foo_copy.bar = 5 3 | print(foo_copy.bar) 4 | foo_ref.bar = 5 5 | 6 | -------------------------------------------------------------------------------- /scripter-class/python/set-bar.py: -------------------------------------------------------------------------------- 1 | import fooapi 2 | 3 | print(foo_copy.bar) 4 | foo_copy.bar = 5 5 | print(foo_copy.bar) 6 | foo_ref.bar = 5 7 | 8 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/scripterAPI.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTERAPI_H 2 | #define SCRIPTERAPI_H 3 | class ScripterAPI 4 | { 5 | public: 6 | int bar = 1; 7 | }; 8 | #endif 9 | -------------------------------------------------------------------------------- /eval-file/python/set-the-y.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print(sys.version) 3 | # the set_the_answer() function and the y variable are injected from c++ 4 | set_the_answer(y - 1) 5 | -------------------------------------------------------------------------------- /add-cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(maths) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | pybind11_add_module(maths 9 | src/maths.cpp 10 | ) 11 | -------------------------------------------------------------------------------- /classes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(classes) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | pybind11_add_module(pet 9 | src/main.cpp 10 | ) 11 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/sample.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLE_H 2 | #define SAMPLE_H 3 | 4 | #include 5 | 6 | class Sample : public QApplication 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit Sample(int &argc, char *argv[]); 11 | 12 | }; 13 | #endif 14 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/sample.cpp: -------------------------------------------------------------------------------- 1 | #include "sample.h" 2 | 3 | #include 4 | 5 | Sample::Sample(int &argc, char *argv[]) : 6 | QApplication(argc, argv) 7 | { 8 | setApplicationName("Application Sample"); 9 | setApplicationVersion("0.1"); 10 | } 11 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "scripter.h" 6 | 7 | int main() { 8 | Scripter scripter; 9 | scripter.runFile("../python/set-bar.py"); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sample.h" 4 | #include "mainwindow.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | Sample app(argc, argv); 9 | 10 | MainWindow mainWin; 11 | mainWin.show(); 12 | 13 | return app.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /eval-file-pyqt5/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | int main() 6 | { 7 | py::scoped_interpreter guard{}; 8 | 9 | py::object scope = py::module::import("__main__").attr("__dict__"); 10 | py::eval_file("pyqt5.py", scope); 11 | } 12 | -------------------------------------------------------------------------------- /eval-file-pyside6/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | int main() 6 | { 7 | py::scoped_interpreter guard{}; 8 | 9 | py::object scope = py::module::import("__main__").attr("__dict__"); 10 | py::eval_file("message_box.py", scope); 11 | } 12 | -------------------------------------------------------------------------------- /eval-set-object/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable( 9 | scripting src/main.cpp 10 | ) 11 | 12 | target_link_libraries(scripting pybind11::embed) 13 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/bar.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "bar.h" 3 | 4 | namespace py = pybind11; 5 | 6 | PYBIND11_MODULE(barapi, m) { 7 | py::class_(m, "Bar") 8 | .def(py::init<>()) 9 | .def_readwrite("value", &Bar::value) 10 | ; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /eval-pyside6-twice/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.11) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable(scripting 9 | src/main.cpp 10 | ) 11 | target_link_libraries(scripting 12 | pybind11::embed 13 | ) 14 | -------------------------------------------------------------------------------- /scripting/README.md: -------------------------------------------------------------------------------- 1 | # Calling pybind11 from c++ 2 | 3 | A small c++ program with an API, that calls python that consumes the API. 4 | 5 | ~~~.sh 6 | $ mkdir build 7 | $ cmake -DPYBIND11_DIR=/home/ale/src/pybind11 -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 .. 8 | $ make 9 | $ ./scripting 10 | ~~~ 11 | 12 | -------------------------------------------------------------------------------- /scripter-class/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "foo.h" 3 | 4 | namespace py = pybind11; 5 | 6 | PYBIND11_MODULE(fooapi, m) { 7 | m.doc() = "pybind11 foo api"; 8 | 9 | py::class_ foo(m, "Foo"); 10 | foo 11 | .def(py::init<>()) 12 | .def_readwrite("bar", &Foo::bar); 13 | } 14 | -------------------------------------------------------------------------------- /add/src/maths.cpp: -------------------------------------------------------------------------------- 1 | #include 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 | } 15 | -------------------------------------------------------------------------------- /eval-pyside6-twice/README.md: -------------------------------------------------------------------------------- 1 | # Call twice PySide6 from a Python script running inside of c++ code 2 | 3 | As reported in , there was an issue when importing multiple times PyQt5 from Python scripts running in a c++ application. 4 | 5 | This seems to be solved with PySide6 and the latest pybind11 6 | -------------------------------------------------------------------------------- /eval-set-object-module/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "foo.h" 3 | 4 | namespace py = pybind11; 5 | 6 | PYBIND11_MODULE(fooapi, m) { 7 | m.doc() = "pybind11 foo api"; 8 | 9 | py::class_ foo(m, "Foo"); 10 | foo 11 | .def(py::init<>()) 12 | .def_readwrite("bar", &Foo::bar); 13 | } 14 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | class MainWindow : public QMainWindow 7 | { 8 | Q_OBJECT 9 | public: 10 | MainWindow(); 11 | private slots: 12 | void scriptRun(); 13 | void about(); 14 | private: 15 | void createActions(); 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/bar.h: -------------------------------------------------------------------------------- 1 | #ifndef BAR_H 2 | #define BAR_H 3 | #include 4 | struct Bar { 5 | int value = 1; 6 | 7 | Bar() {std::cout << "Making a bar!" << std::endl;} 8 | Bar(const Bar& a) {value = a.value; std::cout << "Copying a new bar" << std::endl;} 9 | ~Bar() {std::cout << "Bye-bye bar!" << std::endl;} 10 | }; 11 | #endif 12 | -------------------------------------------------------------------------------- /qt5-pyqt5/README.md: -------------------------------------------------------------------------------- 1 | # Encapsulate the scripter in a class 2 | 3 | ~~~.sh 4 | $ mkdir build 5 | $ cmake -DCMAKE_BUILD_TYPE=Debug .. 6 | $ make 7 | $ ./sample 8 | ~~~ 9 | 10 | ## Notes 11 | 12 | - running twice `run()`, produces a crash. 13 | - If you prefer you can symlink pybind11 directory and replace the `find_package` with `add_subdirectory` in `CMakeLists.txt` 14 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/page.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scripterAPI/page.h" 4 | 5 | namespace py = pybind11; 6 | 7 | void init_PageAPI(py::module &m) { 8 | py::class_(m, "Page") 9 | // .def(py::init<>()) 10 | .def_property_readonly("margin", &ScripterAPI::Page::getMargin) 11 | ; 12 | } 13 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/scripterAPI.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "scripterAPI.h" 3 | 4 | namespace py = pybind11; 5 | 6 | PYBIND11_MODULE(sampleapi, m) { 7 | m.doc() = "pybind11 scripter api"; 8 | 9 | py::class_ foo(m, "Sample"); 10 | foo 11 | .def(py::init<>()) 12 | .def_readwrite("bar", &ScripterAPI::bar); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/foo.h: -------------------------------------------------------------------------------- 1 | #ifndef FOO_H 2 | #define FOO_H 3 | #include 4 | #include "bar.h" 5 | struct Foo { 6 | Bar bar{}; 7 | 8 | Foo() {std::cout << "Making a foo!" << std::endl;} 9 | Foo(const Foo& a) {bar = a.bar; std::cout << "Copying a new foo" << std::endl;} 10 | ~Foo() {std::cout << "Bye-bye foo!" << std::endl;} 11 | }; 12 | #endif 13 | -------------------------------------------------------------------------------- /scripter-class/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable(scripting 9 | src/main.cpp 10 | src/scripter.cpp 11 | ) 12 | target_link_libraries(scripting 13 | pybind11::embed 14 | ) 15 | 16 | pybind11_add_module(fooapi src/foo.cpp) 17 | -------------------------------------------------------------------------------- /scripter-class/src/scripter.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTER_H 2 | #define SCRIPTER_H 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace py = pybind11; 9 | 10 | class Scripter 11 | { 12 | public: 13 | void runFile(std::string fileName); 14 | private: 15 | py::scoped_interpreter guard{}; 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /add-cmake/src/maths.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int add(int i, int j) 4 | { 5 | return i + j; 6 | } 7 | 8 | int main() 9 | { 10 | } 11 | 12 | namespace py = pybind11; 13 | 14 | PYBIND11_MODULE(maths, m) { 15 | m.doc() = "pybind11 example plugin"; // optional module docstring 16 | 17 | m.def("add", &add, "A function which adds two numbers"); 18 | } 19 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/scripter.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTER_H 2 | #define SCRIPTER_H 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace py = pybind11; 9 | 10 | class Scripter 11 | { 12 | public: 13 | void runFile(std::string fileName); 14 | private: 15 | py::scoped_interpreter guard{}; 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /scripter-api/src/sample/page.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLEPAGE_H 2 | #define SAMPLEPAGE_H 3 | #include "margin.h" 4 | namespace Sample 5 | { 6 | class Page 7 | { 8 | public: 9 | Page() { 10 | margin = new Margin(); 11 | } 12 | ~Page() { 13 | delete margin; 14 | } 15 | Margin* margin; 16 | }; 17 | 18 | } 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "foo.h" 3 | 4 | namespace py = pybind11; 5 | 6 | PYBIND11_MODULE(fooapi, m) { 7 | py::class_(m, "Bar") 8 | .def(py::init<>()) 9 | .def_readwrite("value", &Bar::value) 10 | ; 11 | py::class_(m, "Foo") 12 | .def(py::init<>()) 13 | .def_readwrite("bar", &Foo::bar) 14 | ; 15 | } 16 | -------------------------------------------------------------------------------- /scripter-class/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "scripter.h" 6 | #include "foo.h" 7 | 8 | namespace py = pybind11; 9 | 10 | using namespace pybind11::literals; 11 | 12 | void run() 13 | { 14 | Scripter scripter1; 15 | scripter1.runFile("../python/set-bar.py"); 16 | } 17 | 18 | int main() 19 | { 20 | run(); 21 | run(); 22 | } 23 | -------------------------------------------------------------------------------- /eval-file-pyqt5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable(scripting 9 | src/main.cpp 10 | ) 11 | target_link_libraries(scripting 12 | pybind11::embed 13 | ) 14 | 15 | configure_file( 16 | ${CMAKE_CURRENT_SOURCE_DIR}/python/pyqt5.py 17 | ${CMAKE_CURRENT_BINARY_DIR} 18 | COPYONLY 19 | ) 20 | -------------------------------------------------------------------------------- /eval-file/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.4) 5 | 6 | find_package(pybind11 CONFIG) 7 | 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/set-the-y.py 18 | ${CMAKE_CURRENT_BINARY_DIR} 19 | COPYONLY 20 | ) 21 | -------------------------------------------------------------------------------- /eval-file-pyside6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.11) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable(scripting 9 | src/main.cpp 10 | ) 11 | target_link_libraries(scripting 12 | pybind11::embed 13 | ) 14 | 15 | configure_file( 16 | ${CMAKE_CURRENT_SOURCE_DIR}/python/message_box.py 17 | ${CMAKE_CURRENT_BINARY_DIR} 18 | COPYONLY 19 | ) 20 | -------------------------------------------------------------------------------- /eval-file-pyqt5-set-value/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3) 5 | 6 | find_package(pybind11 CONFIG) 7 | 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 | ) 21 | -------------------------------------------------------------------------------- /eval-file-pyqt5/README.md: -------------------------------------------------------------------------------- 1 | # Eval a python script that launches a PyQt5 dialog 2 | 3 | The C++ code runs an external Python script that shows a PyQt5 alert. 4 | 5 | Warning: we need to create a `QApplication` before being able to use a `QWidget` 6 | 7 | ~~~.sh 8 | $ mkdir build 9 | $ cd build 10 | $ cmake .. 11 | $ make 12 | $ ./scripting 13 | ~~~ 14 | 15 | Todo: 16 | 17 | - [ ] in the Python script use a function `ask_question()` instead of the class. 18 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/scripter.cpp: -------------------------------------------------------------------------------- 1 | #include "scripter.h" 2 | #include "scripterAPI.h" 3 | 4 | using namespace pybind11::literals; // for the ""_a 5 | 6 | void Scripter::runFile(std::string fileName) 7 | { 8 | ScripterAPI api; 9 | 10 | auto module = py::module::import("sampleapi"); 11 | 12 | auto locals = py::dict("Sample"_a=py::cast(api, py::return_value_policy::reference)); 13 | 14 | py::eval_file(fileName, py::globals(), locals); 15 | 16 | assert(api.bar == 5); 17 | } 18 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/scripterAPI.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTERAPI_H 2 | #define SCRIPTERAPI_H 3 | #include 4 | 5 | #include "sample/document.h" 6 | #include "scripterAPI/document.h" 7 | 8 | namespace ScripterAPI { 9 | class ScripterAPI { 10 | public: 11 | ScripterAPI() {} 12 | int a = 1; 13 | void setDocument(Sample::Document* d) { documentAPI.setDocument(d);} 14 | Document documentAPI; 15 | }; 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /scripter-class-in-class/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting VERSION 1.0 LANGUAGES CXX) 3 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 4 | 5 | set(PYBIND11_PYTHON_VERSION 3.4) 6 | 7 | find_package(pybind11 CONFIG) 8 | 9 | add_executable(scripting 10 | src/main.cpp 11 | src/scripter.cpp 12 | ) 13 | target_link_libraries(scripting 14 | pybind11::embed 15 | ) 16 | 17 | pybind11_add_module(fooapi 18 | src/bar.cpp 19 | src/foo.cpp 20 | ) 21 | -------------------------------------------------------------------------------- /eval-file-pyqt5-set-value/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = pybind11; 5 | 6 | int main() 7 | { 8 | py::scoped_interpreter guard{}; 9 | 10 | auto local = py::dict(); 11 | 12 | int val_out; 13 | local["set_the_answer"] = py::cpp_function([&](int value) { val_out = value; }); 14 | 15 | py::eval_file("input-number.py", py::globals(), local); 16 | 17 | std::cout << "value set by the python script: " << val_out << std::endl; 18 | } 19 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/document.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scripterAPI/document.h" 4 | 5 | namespace py = pybind11; 6 | 7 | void init_DocumentAPI(py::module &m) { 8 | py::class_(m, "Document") 9 | .def(py::init<>()) 10 | .def("getPage", &ScripterAPI::Document::getPage) 11 | .def("addPage", &ScripterAPI::Document::addPage) 12 | .def_property_readonly("margin", &ScripterAPI::Document::getMargin) 13 | ; 14 | } 15 | -------------------------------------------------------------------------------- /scripter-class-in-class/README.md: -------------------------------------------------------------------------------- 1 | # adding multiple classes to a python module 2 | 3 | the python code can set `foo.bar.value` 4 | 5 | ~~~.sh 6 | $ mkdir build 7 | $ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 .. 8 | $ make 9 | $ ./scripting 10 | ~~~ 11 | 12 | # Notes 13 | 14 | Pybind11 tries to set the visibilty for all symbols as hidden. In the `CMakeLists.txt` we need to set 15 | 16 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 17 | 18 | because of the guard in the `script.h` file 19 | -------------------------------------------------------------------------------- /scripter-api/src/sample/document.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLEDOCUMENT_H 2 | #define SAMPLEDOCUMENT_H 3 | #include 4 | #include "margin.h" 5 | #include "page.h" 6 | namespace Sample 7 | { 8 | class Document 9 | { 10 | public: 11 | Document() { 12 | margin = new Margin(); 13 | } 14 | ~Document() { 15 | delete margin; 16 | } 17 | Margin* margin; 18 | std::vector page; 19 | }; 20 | 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /eval-set-object-module/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting) 3 | 4 | set(PYBIND11_PYTHON_VERSION 3.7) 5 | 6 | find_package(pybind11 CONFIG) 7 | 8 | add_executable(scripting 9 | src/foo.cpp 10 | src/main.cpp 11 | ) 12 | target_link_libraries(scripting 13 | pybind11::embed 14 | ) 15 | 16 | pybind11_add_module(fooapi 17 | src/foo.cpp 18 | ) 19 | 20 | configure_file( 21 | ${CMAKE_CURRENT_SOURCE_DIR}/python/set-bar.py 22 | ${CMAKE_CURRENT_BINARY_DIR} 23 | COPYONLY 24 | ) 25 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/scripter.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTER_H 2 | #define SCRIPTER_H 3 | #include 4 | #include 5 | 6 | #undef slots // avoid conflicts between Python.h and Qt slots 7 | #include 8 | #include 9 | #define slots Q_SLOTS 10 | 11 | namespace py = pybind11; 12 | 13 | class Scripter 14 | { 15 | public: 16 | void runFile(std::string fileName); 17 | private: 18 | py::scoped_interpreter guard{}; // start the interpreter and keep it alive 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /scripter-api/src/sample/margin.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLEDMARGIN_H 2 | #define SAMPLEDMARGIN_H 3 | 4 | namespace Sample 5 | { 6 | class Margin 7 | { 8 | public: 9 | double top{0}; 10 | double bottom{0}; 11 | double left{0}; 12 | double right{0}; 13 | void set(double t, double r, double b, double l) { 14 | top = t; 15 | right = r; 16 | bottom = b; 17 | left = l; 18 | } 19 | }; 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /scripting/src/pet.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Dog { 4 | public: 5 | Dog(const std::string &name) : name(name) {} 6 | void setName(const std::string &n) {name = n;} 7 | const std::string getName() const {return name;} 8 | private: 9 | std::string name; 10 | }; 11 | 12 | class Cat { 13 | public: 14 | Cat(const std::string &name) : name(name) {} 15 | void setName(const std::string &n) {name = n;} 16 | const std::string getName() const {return name;} 17 | private: 18 | std::string name; 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /scripter-api/src/scripter.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTER_H 2 | #define SCRIPTER_H 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "sample/document.h" 9 | 10 | namespace py = pybind11; 11 | 12 | class Scripter 13 | { 14 | public: 15 | Scripter(Sample::Document* document) : 16 | document{document} {}; 17 | void runFile(std::string fileName); 18 | private: 19 | Sample::Document* document; 20 | 21 | py::scoped_interpreter guard{}; 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /scripter-class/src/scripter.cpp: -------------------------------------------------------------------------------- 1 | #include "scripter.h" 2 | #include "foo.h" 3 | 4 | using namespace pybind11::literals; // for the ""_a 5 | 6 | void Scripter::runFile(std::string fileName) 7 | { 8 | Foo foo1, foo2; 9 | 10 | auto module = py::module::import("fooapi"); 11 | 12 | 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 13 | 14 | py::eval_file(fileName, py::globals(), locals); 15 | 16 | assert(foo1.bar == 1); 17 | assert(foo2.bar == 5); 18 | } 19 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/page.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTERAPIPAGE_H 2 | #define SCRIPTERAPIPAGE_H 3 | 4 | #include "sample/document.h" 5 | #include "sample/margin.h" 6 | #include "scripterAPI/margin.h" 7 | 8 | namespace ScripterAPI { 9 | class Page { 10 | public: 11 | Page(Sample::Document* d, int pageNumber): pageNumber{pageNumber}, document{d} {} 12 | Margin getMargin() { return Margin(document->page.at(pageNumber -1).margin); } 13 | private: 14 | int pageNumber; 15 | Sample::Document* document; 16 | }; 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /eval-file-pyqt5/python/pyqt5.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox 3 | 4 | class App(): 5 | 6 | def __init__(self): 7 | buttonReply = QMessageBox.question(None, 'PyQt5 message', "Do you like PyQt5?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 8 | if buttonReply == QMessageBox.Yes: 9 | print('Yes clicked.') 10 | else: 11 | print('No clicked.') 12 | 13 | if __name__ == '__main__': 14 | # app = QApplication(sys.argv) 15 | app = QApplication([]) 16 | ex = App() 17 | 18 | 19 | -------------------------------------------------------------------------------- /scripter-class/README.md: -------------------------------------------------------------------------------- 1 | # c++ launches a Python script that loads a shared library module and sets a c++ variable 2 | 3 | - Create a `.so` module. 4 | - Call the Python script from the c++ code and pass a variable per reference. 5 | - The Python script loads the module and modify the variable. 6 | - The c++ code checks that the values has been modified 7 | 8 | ~~~.sh 9 | $ mkdir build 10 | $ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 .. 11 | $ make 12 | $ ./scripting 13 | ~~~ 14 | 15 | For now, you have to copy the generated `fooapi.cpython-35m-x86_64-linux-gnu.so` file next to the python script. 16 | -------------------------------------------------------------------------------- /scripter-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(scripting VERSION 1.0 LANGUAGES CXX) 3 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 4 | 5 | set(PYBIND11_PYTHON_VERSION 3.4) 6 | 7 | find_package(pybind11 CONFIG) 8 | 9 | include_directories(src) 10 | 11 | add_executable(scripting 12 | src/main.cpp 13 | src/scripter.cpp 14 | ) 15 | target_link_libraries(scripting 16 | pybind11::embed 17 | ) 18 | 19 | pybind11_add_module(scripterapi 20 | src/scripterAPI/scripterAPI.cpp 21 | src/scripterAPI/document.cpp 22 | src/scripterAPI/margin.cpp 23 | src/scripterAPI/page.cpp 24 | ) 25 | -------------------------------------------------------------------------------- /scripter-api/python/set-bar.py: -------------------------------------------------------------------------------- 1 | import scripterapi 2 | 3 | # print(Sample.margin.top) 4 | # Sample.margin.top = 150 5 | # print(Sample.margin.top) 6 | document = Sample.document 7 | 8 | print("python document.margin.top "+str(document.margin.top)) 9 | Sample.document.margin.top = 21 10 | print("python document.margin.top "+str(document.margin.top)) 11 | Sample.document.getPage(1).margin.top = 21 12 | print("python page 1 margin.top "+str(document.getPage(1).margin.top)) 13 | Sample.document.getPage(1).margin.top = 42 14 | print("python page 1 margin.top "+str(document.getPage(1).margin.top)) 15 | # Sample.document.addPage() 16 | -------------------------------------------------------------------------------- /eval-file-pyqt5-set-value/python/input-number.py: -------------------------------------------------------------------------------- 1 | def get_the_value(outer_locals): 2 | from PyQt5.QtWidgets import QInputDialog 3 | # locals().update(outer_locals) # a way to make the locals difference explicit 4 | num, ok = QInputDialog.getInt(None, 'The number to be set', 'enter a number') 5 | if ok: 6 | outer_locals['set_the_answer'](num) 7 | # locals()['set_the_answer'](num) 8 | # set_the_answer(num) # this cannot work (see README) 9 | 10 | if __name__ == '__main__': 11 | from PyQt5.QtWidgets import QApplication 12 | app = QApplication(['myname']) 13 | get_the_value(locals()) 14 | -------------------------------------------------------------------------------- /eval-set-object-module/README.md: -------------------------------------------------------------------------------- 1 | # C++ creates and loads a module and runs a Python script that uses the module to modify its own state 2 | 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. 8 | 9 | ~~~.sh 10 | $ mkdir build 11 | $ cd build 12 | $ cmake .. 13 | $ make 14 | $ ./scripting 15 | ~~~ 16 | -------------------------------------------------------------------------------- /scripter-class-in-class/src/scripter.cpp: -------------------------------------------------------------------------------- 1 | #include "scripter.h" 2 | #include "foo.h" 3 | #include "bar.h" 4 | 5 | using namespace pybind11::literals; // for the ""_a 6 | 7 | void Scripter::runFile(std::string fileName) 8 | { 9 | Foo foo; 10 | 11 | std::cout << foo.bar.value << std::endl; 12 | 13 | auto module = py::module::import("fooapi"); 14 | 15 | // auto locals = py::dict("foo"_a=foo); // foo by value 16 | auto locals = py::dict("foo"_a=py::cast(foo, py::return_value_policy::reference)); // foo by reference 17 | 18 | py::eval_file(fileName, py::globals(), locals); 19 | 20 | std::cout << foo.bar.value << std::endl; 21 | } 22 | -------------------------------------------------------------------------------- /eval-pyside6-twice/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | int main() 6 | { 7 | { 8 | py::scoped_interpreter guard{}; 9 | auto scope = py::globals(); 10 | 11 | py::exec(R"( 12 | from PySide6.QtWidgets import QApplication 13 | print("first") 14 | )", scope 15 | ); 16 | } 17 | { 18 | py::scoped_interpreter guard{}; 19 | auto scope = py::globals(); 20 | 21 | py::exec(R"( 22 | from PySide6.QtWidgets import QApplication 23 | print("second") 24 | )", scope 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripting/src/api-pet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pet.h" 5 | 6 | int main() 7 | { 8 | } 9 | 10 | namespace py = pybind11; 11 | 12 | PYBIND11_MODULE(petapi, m) { 13 | m.doc() = "pybind11 class example plugin"; // optional module docstring 14 | 15 | py::class_(m, "Dog") 16 | .def(py::init()) 17 | .def("setName", &Dog::setName, "Setting the dog's name") 18 | .def("getName", &Dog::getName), "Getting the dog's name"; 19 | 20 | py::class_(m, "Cat") 21 | .def(py::init()) 22 | .def_property("name", &Cat::getName, &Cat::setName); 23 | } 24 | -------------------------------------------------------------------------------- /eval-set-object-module/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "foo.h" 6 | 7 | namespace py = pybind11; 8 | 9 | using namespace pybind11::literals; 10 | 11 | int main() 12 | { 13 | py::scoped_interpreter guard{}; 14 | 15 | Foo foo1, foo2; 16 | 17 | auto module = py::module::import("fooapi"); 18 | 19 | auto locals = py::dict( 20 | "foo_copy"_a = foo1, 21 | "foo_ref"_a = py::cast(foo2, 22 | py::return_value_policy::reference)); 23 | 24 | py::eval_file("set-bar.py", py::globals(), locals); 25 | 26 | assert(foo1.bar == 1); 27 | assert(foo2.bar == 5); 28 | } 29 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/margin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scripterAPI/margin.h" 4 | 5 | namespace py = pybind11; 6 | 7 | void init_MarginAPI(py::module &m) { 8 | py::class_(m, "Margin") 9 | // .def(py::init<>()) 10 | .def_property("top", &ScripterAPI::Margin::getTop, &ScripterAPI::Margin::setTop) 11 | .def_property("right", &ScripterAPI::Margin::getRight, &ScripterAPI::Margin::setRight) 12 | .def_property("bottom", &ScripterAPI::Margin::getBottom, &ScripterAPI::Margin::setBottom) 13 | .def_property("left", &ScripterAPI::Margin::getLeft, &ScripterAPI::Margin::setLeft) 14 | ; 15 | } 16 | -------------------------------------------------------------------------------- /eval-file-pyside6/python/message_box.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # sys.path.append('/home/ale/src/cpp-pybind11-playground/venv/lib/python3.11/site-packages/') 3 | from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox 4 | 5 | class App(): 6 | 7 | def __init__(self): 8 | buttonReply = QMessageBox.question(None, 'PyQt5 message', "Do you like PyQt5?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 9 | if buttonReply == QMessageBox.Yes: 10 | print('Yes clicked.') 11 | else: 12 | print('No clicked.') 13 | 14 | if __name__ == '__main__': 15 | # app = QApplication(sys.argv) 16 | app = QApplication([]) 17 | ex = App() 18 | -------------------------------------------------------------------------------- /qt5-pyqt5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.4) 2 | 3 | PROJECT(sample) 4 | 5 | SET(CMAKE_CXX_STANDARD 14) 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 8 | 9 | set(CMAKE_AUTOMOC ON) 10 | 11 | find_package(Qt5Widgets) 12 | 13 | set(PYBIND11_PYTHON_VERSION 3.4) 14 | 15 | FIND_PACKAGE(pybind11 CONFIG) 16 | 17 | ADD_DEFINITIONS(-DSCRIPTS_DIRECTORY="${CMAKE_SOURCE_DIR}") 18 | 19 | ADD_EXECUTABLE(sample 20 | src/main.cpp 21 | src/sample.cpp 22 | src/mainwindow.cpp 23 | src/scripter.cpp 24 | ) 25 | 26 | target_link_libraries(sample 27 | Qt5::Widgets 28 | ${PYTHON_LIBRARIES} 29 | pybind11::embed 30 | ) 31 | 32 | project(sampleapi) 33 | pybind11_add_module(sampleapi src/scripterAPI.cpp) 34 | -------------------------------------------------------------------------------- /scripting/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pet.h" 3 | 4 | int main() 5 | { 6 | setenv("PYTHONPATH", ".", 1); 7 | 8 | wchar_t *programName = Py_DecodeLocale("test-script", NULL); 9 | Py_SetProgramName(programName); 10 | Py_Initialize(); 11 | 12 | PyRun_SimpleString("from time import time,ctime\n" 13 | "print('Today is', ctime(time()))\n" 14 | "import petapi\n" 15 | "a = petapi.Cat('Fitz')\n" 16 | "print(a.name)\n"); 17 | Py_Finalize(); 18 | /* 19 | if (Py_FinalizeEx() < 0) { // new in 3.6 20 | exit(120); 21 | } 22 | */ 23 | PyMem_RawFree(programName); 24 | } 25 | -------------------------------------------------------------------------------- /scripter-api/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "sample/document.h" 6 | #include "sample/page.h" 7 | #include "sample/margin.h" 8 | 9 | #include "scripter.h" 10 | 11 | namespace py = pybind11; 12 | 13 | int main() 14 | { 15 | Sample::Document document; 16 | Sample::Page page; 17 | document.page.push_back(page); 18 | 19 | Scripter scripter{&document}; 20 | std::cout << "c++ document top " << document.margin->top << std::endl; 21 | scripter.runFile("../python/set-bar.py"); 22 | std::cout << "c++ document top " << document.margin->top << std::endl; 23 | std::cout << "c++ document page 1 top " << document.page.at(0).margin->top << std::endl; 24 | } 25 | -------------------------------------------------------------------------------- /scripting/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(scripting) 3 | 4 | FIND_PACKAGE(PythonInterp 3 REQUIRED) 5 | FIND_PACKAGE(PythonLibs 3 REQUIRED) 6 | 7 | INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS}) 8 | 9 | ADD_EXECUTABLE(scripting src/main.cpp) 10 | TARGET_LINK_LIBRARIES(scripting ${PYTHON_LIBRARIES}) 11 | 12 | IF( NOT EXISTS "${PYBIND11_DIR}" ) 13 | SET(PYBIND11_DIR ${PROJECT_SOURCE_DIR}"/pybind11") 14 | ENDIF() 15 | 16 | SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) 17 | 18 | FIND_PACKAGE(Pybind11 REQUIRED) 19 | 20 | SET(CMAKE_MODULE_PATH ${PYBIND11_DIR}/tools ${CMAKE_MODULE_PATH}) 21 | INCLUDE(${PYBIND11_DIR}/tools/pybind11Tools.cmake) 22 | 23 | project(petapi) 24 | pybind11_add_module(petapi src/api-pet.cpp) 25 | -------------------------------------------------------------------------------- /scripter-api/src/scripter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "scripter.h" 3 | #include "scripterAPI/scripterAPI.h" 4 | #include "sample/document.h" 5 | 6 | using namespace pybind11::literals; // for the ""_a 7 | 8 | void Scripter::runFile(std::string fileName) 9 | { 10 | // Sample::Document document; 11 | //Sample::Margin saMargin; 12 | 13 | ScripterAPI::ScripterAPI scripterAPI; 14 | scripterAPI.setDocument(document); 15 | 16 | 17 | auto module = py::module::import("scripterapi"); 18 | 19 | auto locals = py::dict("Sample"_a=py::cast(scripterAPI, py::return_value_policy::reference)); 20 | 21 | py::eval_file(fileName, py::globals(), locals); 22 | 23 | // document = scripterAPI.getDocument(); 24 | 25 | // assert(scripter.bar == 5); 26 | } 27 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/document.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTERAPIDOCUMENT_H 2 | #define SCRIPTERAPIDOCUMENT_H 3 | 4 | #include "sample/document.h" 5 | #include "sample/page.h" 6 | #include "scripterAPI/margin.h" 7 | #include "scripterAPI/page.h" 8 | 9 | namespace ScripterAPI { 10 | class Document { 11 | public: 12 | void setDocument(Sample::Document* d) { 13 | document = d; 14 | } 15 | Margin getMargin() { return Margin(document->margin); } 16 | Page getPage(int i) { return Page(document, i); } 17 | void addPage() { 18 | Sample::Page page; 19 | document->page.push_back(page); 20 | } 21 | private: 22 | Sample::Document* document; 23 | }; 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/scripterAPI.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scripterAPI/scripterAPI.h" 4 | 5 | namespace py = pybind11; 6 | 7 | void init_ScripterAPI(py::module &m) { 8 | py::class_(m, "Sample") 9 | .def(py::init<>()) 10 | .def_readwrite("a", &ScripterAPI::ScripterAPI::a) 11 | .def_readwrite("document", &ScripterAPI::ScripterAPI::documentAPI) 12 | ; 13 | } 14 | 15 | void init_DocumentAPI(py::module &); 16 | void init_PageAPI(py::module &); 17 | void init_MarginAPI(py::module &); 18 | 19 | PYBIND11_MODULE(scripterapi, m) { 20 | m.doc() = "Python interface the Sample application"; 21 | init_ScripterAPI(m); 22 | init_DocumentAPI(m); 23 | init_PageAPI(m); 24 | init_MarginAPI(m); 25 | } 26 | -------------------------------------------------------------------------------- /scripter-api/src/scripterAPI/margin.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTERAPIMARGIN_H 2 | #define SCRIPTERAPIMARGIN_H 3 | 4 | #include "sample/document.h" 5 | #include "sample/margin.h" 6 | 7 | namespace ScripterAPI { 8 | class Margin { 9 | public: 10 | Margin(Sample::Margin* m): margin{m} {} 11 | double getTop() { return margin->top; } 12 | void setTop(double t) { margin->top = t; } 13 | double getRight() { return margin->right; } 14 | void setRight(double r) { margin->right = r; } 15 | double getBottom() { return margin->bottom; } 16 | void setBottom(double b) { margin->bottom = b; } 17 | double getLeft() { return margin->left; } 18 | void setLeft(double l) { margin->left = l; } 19 | private: 20 | Sample::Margin* margin; 21 | }; 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /qt5-signals/README.md: -------------------------------------------------------------------------------- 1 | - https://github.com/cpp11nullptr/lsignal: lsignal (or lightweight signal) is a very little and fast C++ thread-safe implementation of signal and slot system which is based on modern C++11 code. 2 | - https://github.com/dgovil/PySignal: A purely Python implementation of the Qt signal system with no QObject dependencies 3 | 4 | how scribus manages the menus: 5 | 6 | - `scribus/menumanager.cpp`: seems to be managing the menus through strings 7 | - `ActionManager::createDefaultMenuNames()` fills defMenuNames 8 | - `static QVector< QPair > defMenuNames;` 9 | - `static QVector< QPair > defMenus;` 10 | - `scrActions` seems to contain the real actions 11 | - `scrActions` is a vector filled by the functions called in `ActionManager::createActions()` 12 | - `scribus/scribus.cpp` connects the `scrActions` with real actions 13 | -------------------------------------------------------------------------------- /eval-file-pyqt5-set-value/README.md: -------------------------------------------------------------------------------- 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 | One big warning: the `set_the_answer()` function is not visisble inside of `get_the_value()`: `pybind11` is passing the `set_the_answer()` as a local to the _eval_ (exec, in reality) Python context: we need to explicitely pass the outer local context to the `get_the_value()`, and visible inside of it. 8 | (I've been told that this is becaue Python executes the code as if it was in a class environment...) 9 | 10 | ~~~.sh 11 | $ mkdir build 12 | $ cd build 13 | $ cmake .. 14 | $ make 15 | $ ./scripting 16 | ~~~ 17 | -------------------------------------------------------------------------------- /eval-set-object/README.md: -------------------------------------------------------------------------------- 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. 9 | 10 | ~~~.sh 11 | $ mkdir build 12 | $ cd build 13 | $ cmake .. 14 | $ make 15 | $ ./scripting 16 | ~~~ 17 | 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 | 24 | see http://pybind11.readthedocs.io/en/master/advanced/embedding.html 25 | -------------------------------------------------------------------------------- /eval-file/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = pybind11; 5 | 6 | int main() 7 | { 8 | // http://pybind11.readthedocs.io/en/latest/advanced/embedding.html#getting-started 9 | py::scoped_interpreter guard{}; // start the interpreter and keep it alive 10 | 11 | // inject the y variable into the python script 12 | auto local = py::dict(); 13 | local["y"] = py::int_(43); 14 | 15 | // inject the set_the_answer() lambda into the python script. 16 | int val_out{7}; 17 | local["set_the_answer"] = py::cpp_function([&](int value) { val_out = value; }); 18 | 19 | std::cout << "the c++ value value before calling the python script: " << val_out << std::endl; 20 | 21 | py::object scope = py::module::import("__main__").attr("__dict__"); 22 | py::eval_file("set-the-y.py", scope, local); 23 | 24 | std::cout << "the c++ value after calling the python script: " << val_out << std::endl; 25 | } 26 | -------------------------------------------------------------------------------- /eval-set-object/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace py = pybind11; 6 | 7 | // for the _a string litteral 8 | // http://pybind11.readthedocs.io/en/stable/basics.html#keyword-arguments 9 | using namespace pybind11::literals; 10 | 11 | struct Foo { int bar = 1; }; 12 | 13 | 14 | PYBIND11_EMBEDDED_MODULE(foo_module, m) { 15 | py::class_(m, "Foo").def_readwrite("bar", &Foo::bar); 16 | } 17 | 18 | int main() 19 | { 20 | py::scoped_interpreter guard{}; 21 | 22 | Foo foo_1, foo_2; 23 | 24 | auto module = py::module::import("foo_module"); 25 | 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__")); 31 | py::exec(R"( 32 | print(foo_copy.bar); 33 | foo_copy.bar = 5; 34 | print(foo_copy.bar); 35 | foo_ref.bar = 5; 36 | )", py::globals(), locals); 37 | 38 | assert(foo_1.bar == 1); 39 | assert(foo_2.bar == 5); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /classes/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class Dog { 5 | public: 6 | Dog(const std::string &name) : name(name) {} 7 | void setName(const std::string &n) {name = n;} 8 | const std::string getName() const {return name;} 9 | private: 10 | std::string name; 11 | }; 12 | 13 | class Cat { 14 | public: 15 | Cat(const std::string &name) : name(name) {} 16 | void setName(const std::string &n) {name = n;} 17 | const std::string getName() const {return name;} 18 | private: 19 | std::string name; 20 | }; 21 | 22 | int main() 23 | { 24 | } 25 | 26 | namespace py = pybind11; 27 | 28 | PYBIND11_MODULE(pet, m) { 29 | m.doc() = "pybind11 class example plugin"; // optional module docstring 30 | 31 | py::class_(m, "Dog") 32 | .def(py::init()) 33 | .def("setName", &Dog::setName, "Setting the dog's name") 34 | .def("getName", &Dog::getName), "Getting the dog's name"; 35 | 36 | py::class_(m, "Cat") 37 | .def(py::init()) 38 | .def_property("name", &Cat::getName, &Cat::setName, "The cat name"); 39 | } 40 | -------------------------------------------------------------------------------- /add-cmake/README.md: -------------------------------------------------------------------------------- 1 | # pybind11 and cmake 2 | 3 | This second example shows how to use cmake to compile the [add example](../add). 4 | 5 | All further examples, will use cmake. 6 | 7 | The `pybind11_DIR` passed to the cmake commands tells the compiler where to find the library. 8 | 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: . 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 27 | 28 | ```.sh 29 | $ mkdir build 30 | $ cd build 31 | $ cmake .. 32 | $ make 33 | ``` 34 | 35 | If pybind11 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 45 | $ python3 46 | >>> import maths 47 | >>> maths.add(1, 2) 48 | 3 49 | ``` 50 | -------------------------------------------------------------------------------- /qt5-pyqt5/src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | #include "scripter.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | MainWindow::MainWindow() 10 | { 11 | createActions(); 12 | } 13 | 14 | void MainWindow::createActions() 15 | { 16 | QMenu *fileMenu = menuBar()->addMenu(tr("&File")); 17 | QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close); 18 | exitAct->setShortcuts(QKeySequence::Quit); 19 | 20 | QMenu *scriptMenu = menuBar()->addMenu(tr("&Script")); 21 | QAction *runAct = scriptMenu->addAction(tr("&Run"), this, &MainWindow::scriptRun); 22 | 23 | QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); 24 | QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about); 25 | // add an help entry that responds to F1 26 | } 27 | 28 | void MainWindow::scriptRun() 29 | { 30 | QString fileName = QFileDialog::getOpenFileName(this, 31 | // tr("Open Image"), QStandardPaths::standardLocations(QStandardPaths::HomeLocation).last(), tr("Image Files (*.py *.png *.jpg *.bmp)")); 32 | tr("Open Script"), SCRIPTS_DIRECTORY, tr("Python Files (*.py)")); 33 | qDebug() << "fileName" << fileName; 34 | std::cout << "filename: " << fileName.toStdString() << std::endl; 35 | 36 | Scripter scripter; 37 | scripter.runFile(fileName.toStdString()); 38 | } 39 | 40 | void MainWindow::about() 41 | { 42 | QMessageBox::about(this, tr("About this sample"), 43 | tr("This Sample creates a window with a menu bar.")); 44 | } 45 | -------------------------------------------------------------------------------- /classes/README.md: -------------------------------------------------------------------------------- 1 | # Modules with classes 2 | 3 | The `pet` module provides the `Dog` and `Cat` classes. 4 | 5 | Both C++ classes have a `getName()` and a `setName()` class functions. 6 | 7 | The `Dog` and `Cat` classes are exposed in a different way to the Python code: 8 | 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. 11 | 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_(m, "Dog") 19 | .def(py::init()) 20 | .def("setName", &Dog::setName, "Setting the dog's name") 21 | .def("getName", &Dog::getName), "Getting the dog's name"; 22 | 23 | py::class_(m, "Cat") 24 | .def(py::init()) 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 31 | $ mkdir build 32 | $ cmake .. 33 | $ make 34 | $ python3 35 | >>> import pet 36 | >>> a = pet.Dog('Flip') 37 | >>> print(a) 38 | 39 | >>> a.getName() 40 | 'Flip' 41 | >>> a.setName('Flop') 42 | >>> a.getName() 43 | 'Flop' 44 | >>> b = pet.Cat('Fox') 45 | >>> b.name 46 | 'Fox' 47 | >>> b.name = 'Fix' 48 | >>> b.name 49 | 'Fix' 50 | ``` 51 | -------------------------------------------------------------------------------- /scripting/cmake/FindPybind11.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindPybind11 3 | # -------- 4 | # 5 | # Find the Pybind11 includes. 6 | # Copied from https://github.com/ndarray/ndarray/blob/master/cmake/FindPybind11.cmake 7 | # 8 | # Result Variables 9 | # ^^^^^^^^^^^^^^^^ 10 | # 11 | # This module will set the following variables in your project:: 12 | # 13 | # PYBIND11_FOUND - True if Pybind11 found on the local system 14 | # PYBIND11_INCLUDE_DIR - Location of Pybind11 header files. 15 | # 16 | # Hints 17 | # ^^^^^ 18 | # 19 | # Set ``PYBIND11_DIR`` to a directory that contains a Pybind11 installation. 20 | # 21 | # This script expects to find headers at ``$PYBIND11_DIR/include``. 22 | # 23 | 24 | # Include these modules to handle the QUIETLY and REQUIRED arguments. 25 | include(FindPackageHandleStandardArgs) 26 | 27 | #============================================================================= 28 | # If the user has provided ``PYBIND11_DIR``, use it! Choose items found 29 | # at this location over system locations. 30 | if( EXISTS "$ENV{PYBIND11_DIR}" ) 31 | file( TO_CMAKE_PATH "$ENV{PYBIND11_DIR}" PYBIND11_DIR ) 32 | set( PYBIND11_DIR "${PYBIND11_DIR}" CACHE PATH "Prefix for Pybind11 installation." ) 33 | endif() 34 | 35 | #============================================================================= 36 | # Set PYBIND11_INCLUDE_DIRS. 37 | # Try to find pybind11 at $PYBIND11_DIR (if provided) or in standard 38 | # system locations. 39 | find_path(PYBIND11_INCLUDE_DIR 40 | NAMES pybind11/pybind11.h 41 | HINTS ${PYBIND11_DIR}/include 42 | ) 43 | 44 | find_package_handle_standard_args(Pybind11 DEFAULT_MSG PYBIND11_INCLUDE_DIR) 45 | -------------------------------------------------------------------------------- /scripter-api/README.md: -------------------------------------------------------------------------------- 1 | # Providing an API for manipuling a document 2 | 3 | A Scripter engine providing Python scripting to a C++ Sample application. The the scripting main goal are modifications to the current document in the C++ application. 4 | 5 | - `src/sample/` contains the data structures for the document used in the C++ application. 6 | 7 | ``` 8 | sample 9 | .document 10 | .margin 11 | .top 12 | .right 13 | .bottom 14 | .left 15 | .page[] 16 | .margin 17 | ... 18 | ``` 19 | - `src/scripterAPI/` contains the interface for the Python module. the "Sample" document is not directly exposed to the Python code. 20 | - the `scripterapi` Python module is created by `src/scripterAPI/scripterAPI.cpp` by collecting the module definitions for each `scripterAPI` class. 21 | - the memory management for the document respects the following constraints: 22 | - the c++ application might or not have an open document. 23 | - if there is an open document, it's shared with the API. 24 | - the API can modify the document. 25 | - while the script is running, the C++ application is not allowed to modify the document(s). 26 | - (eventually, trough the API it should be possible to change the current document to any other open document) 27 | - through the API, it should be possible to open documents / create new documents. 28 | - (eventually, the document will be left open when the script is finished) 29 | 30 | ~~~.sh 31 | $ mkdir build 32 | $ cmake -Dpybind11_DIR=/home/ale/bin/pybind11/share/cmake/pybind11 .. 33 | $ make 34 | $ ./scripting 35 | ~~~ 36 | 37 | ## notes 38 | 39 | - if we ever want to check how it works with `unique_ptr`s, this is a good article on how to add a `unique_ptr` as a private member in a class: 40 | -------------------------------------------------------------------------------- /eval-file/README.md: -------------------------------------------------------------------------------- 1 | # Eval a python script that modifies a C++ object 2 | 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. 4 | 5 | The first step is to include the `pybind11`'s `embed.h` library: 6 | 7 | ```cpp 8 | #include 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: 50 | 51 | ~~~.sh 52 | $ mkdir build 53 | $ cd build 54 | $ cmake 55 | $ make 56 | $ ./scripting 57 | ~~~ 58 | 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 | 67 | ## References 68 | 69 | - http://pybind11.readthedocs.io/en/latest/advanced/embedding.html 70 | - https://github.com/pybind/pybind11/blob/master/tests/test_eval.cpp#L63 71 | -------------------------------------------------------------------------------- /add/README.md: -------------------------------------------------------------------------------- 1 | # Add: the first example 2 | 3 | This first example shows how to program in C++ a python module providing an `add()` function. 4 | 5 | When imported in Pyhton, `add(a, b)` returns the sum of the two integers given as a parameter. 6 | 7 | A very simple C++ library provides a function that returns the sum of two integer numbers: 8 | 9 | 10 | ```cpp 11 | #include 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 | } 25 | ``` 26 | 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. 34 | 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 -I ~/src/pybind11/include` 43 | - If your system does not default to Python 3, `-I /usr/include/python3.7 -L /usr/lib/python3` 44 | 45 | In 2023, with Debian Testing, the command for compiling is: 46 | 47 | ```sh 48 | g++ -O3 -shared -fPIC -std=c++11 -I /usr/include/python3.11 -L /usr/lib/python3 src/maths.cpp -o maths.so 49 | ``` 50 | 51 | - `pybind11-dev` and `python3-pybind11` need to be installed. 52 | 53 | As a result you will get the python library `maths.so`. 54 | 55 | You can now import the "maths" module in Python3. Start Python3 in the directory where the `.so` file is located and: 56 | 57 | ```.py 58 | >>> import maths 59 | >>> print(maths.add(1,2)) 60 | 3 61 | >>> 62 | ``` 63 | 64 | You can also see the documentation with 65 | 66 | ```.py 67 | >>> import maths 68 | >>> help(maths) 69 | Help on module maths: 70 | 71 | NAME 72 | maths - pybind11 example plugin 73 | 74 | FUNCTIONS 75 | add(...) method of builtins.PyCapsule instance 76 | add(arg0: int, arg1: int) -> int 77 | 78 | A function which adds two numbers 79 | 80 | FILE 81 | /home/ale/src/cpp-pybind11-playground/add/maths.so 82 | ``` 83 | 84 | ## Notes 85 | 86 | My full g++ command is: 87 | 88 | ``` 89 | 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 90 | ``` 91 | 92 | ## Further steps 93 | 94 | - have a look at for creating a plugin (instead of a module) 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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. 10 | 11 | ## Getting pybind11 12 | 13 | There are three ways for getting pybind11: 14 | 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 | - In 2023, in Debian Testing you need to install `pybind11-dev` and `python3-pybind11`. 18 | - Compiling pybind11 in a separate directory. 19 | 20 | 21 | The samples in this repositories use a pre-compiled version of pybind11. If you're not getting pybind11 from your distribution repositories: 22 | 23 | - get the code from github 24 | - compile it: 25 | - `mkdir build && cd build` 26 | - `cmake -D CMAKE_INSTALL_PREFIX=/the/prefix/where/you/install/pybind11 -D PYBIND11_TEST=OFF ..` 27 | - `make install` 28 | 29 | The pybind11 authors prefer adding pybind11 as a git sub-project: you can of course follow their instruction and adapt the `CMakeLists.txt` files in this repository. 30 | 31 | ## The development environment 32 | 33 | You need: 34 | 35 | - a development environment for C++ (g++ or clang; cmake, ...) 36 | - the Python bindings (`libpython3-dev`, `python-cxx-dev`, ...) 37 | 38 | 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) 39 | 40 | ## Sample projects 41 | 42 | - [`add/`](add/): create a module providing a C++ `add` function that can be add two numbers. 43 | - [`add-cmake`](add-cmake/): the first example with CMake (all further examples use CMake). 44 | - [`classes/`](classes/): let Python use C++ classes. 45 | - [`eval-file/`](eval-file/): C++ evaluates a Python script in an external files that modifies the state of a C++ variable. 46 | - [`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) 47 | - [`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. 48 | - [`eval-file-pyqt5/`](eval-file-pyqt5/): The C++ code runs an external Python script that shows a PyQt5 alert. 49 | - [`eval-file-pyqt5-set-value/`](eval-file-pyqt5-set-value/): Setting a C++ value from a PyQt5 input dialog. 50 | - ... 51 | 52 | ## Links 53 | 54 | - an HN thread: 55 | - how to use cmake with pybind11 56 | 57 | ## Notes 58 | 59 | - 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: 60 | - read https://github.com/GooFit/GooFit/blob/master/python/goofit/Variable.cpp as an example for... 61 | 62 | ## alternatives 63 | 64 | - [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++. 65 | 66 | ## todo 67 | 68 | - [ ] adapt all `CMakeLists.txt` files to require Python 3.7. (`eval-set-object-module` does somehow require it) 69 | - [ ] use pyside2 on top of pyqt5 70 | - [ ] 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? 71 | - [ ] in the qt5-pyqt example there is a "visibility" warning. 72 | - [ ] Try to add a menu entry from pyqt5 73 | - [ ] Try with PySide2 74 | 75 | Document all directories: 76 | 77 | - [x] add 78 | - [x] add-cmake 79 | - [x] classes 80 | - [x] eval-file 81 | - [x] eval-set-object 82 | - [x] eval-set-object-module 83 | - [x] eval-file-pyqt5 84 | - [ ] eval-file-pyqt5-set-value 85 | - [ ] scripter-api 86 | - [ ] scripter-class 87 | - [ ] scripter-class-in-class 88 | - [ ] scripting 89 | - [ ] scripting-pyqt5 90 | - [ ] qt5-pyqt5 91 | 92 | open questions: 93 | 94 | - [ ] how to pass sys.argv to the `QApplication()` in `eval-file-pyqt5/python/pyqt5.py` 95 | --------------------------------------------------------------------------------