├── .gitignore ├── plot.png ├── .vscode ├── settings.json └── tasks.json ├── CMakeLists.txt ├── main.cpp ├── .clang-format └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tttapa/Pybind11-Matplotlib-Cpp/HEAD/plot.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "cmath": "cpp" 4 | }, 5 | "C_Cpp.default.cppStandard": "c++17" 6 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(Pybind11-Matplotlib-Cpp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | find_package(pybind11 REQUIRED) 7 | add_executable(main main.cpp) 8 | target_link_libraries(main pybind11::embed) -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exp, std::cos 2 | 3 | #include // py::scoped_interpreter 4 | #include // bindings from C++ STL containers to Python types 5 | 6 | namespace py = pybind11; 7 | 8 | int main() { 9 | // Create an exponentially decaying sinusoidal signal as an example 10 | std::vector signal(1024); 11 | for (size_t i = 0; i < signal.size(); ++i) 12 | signal[i] = std::exp(i / -256.0) * std::cos(2 * M_PI * 8 * i / 1024.0); 13 | 14 | // Start the Python interpreter 15 | py::scoped_interpreter guard{}; 16 | using namespace py::literals; 17 | 18 | // Save the necessary local variables in a Python dict 19 | py::dict locals = py::dict{ 20 | "signal"_a = signal, 21 | }; 22 | 23 | // Execute Python code, using the variables saved in `locals` 24 | py::exec(R"( 25 | 26 | import matplotlib.pyplot as plt 27 | 28 | plt.plot(signal) 29 | plt.show() 30 | 31 | )", 32 | py::globals(), locals); 33 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "shell", 8 | "label": "Build", 9 | "command": "make -j$(($(nproc) * 2))", 10 | "options": { 11 | "cwd": "${workspaceFolder}/build", 12 | }, 13 | "problemMatcher": "$gcc", 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true, 17 | }, 18 | }, 19 | { 20 | "type": "shell", 21 | "label": "Build & Run", 22 | "command": "./main", 23 | "options": { 24 | "cwd": "${workspaceFolder}/build", 25 | }, 26 | "problemMatcher": [], 27 | "group": { 28 | "kind": "build", 29 | "isDefault": true, 30 | }, 31 | "dependsOn": "Build", 32 | }, 33 | { 34 | "type": "shell", 35 | "label": "Clean", 36 | "command": "make clean", 37 | "options": { 38 | "cwd": "${workspaceFolder}/build", 39 | }, 40 | "problemMatcher": [], 41 | }, 42 | { 43 | "type": "shell", 44 | "label": "Clear build Directory and Run CMake", 45 | "command": "mkdir -p build && cd build && rm -rf ./* && cmake .. -DPYTHON_EXECUTABLE=$(which python3)", 46 | "options": { 47 | "cwd": "${workspaceFolder}", 48 | }, 49 | "problemMatcher": [], 50 | }, 51 | ] 52 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: true 72 | IndentPPDirectives: None 73 | IndentWidth: 4 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBlockIndentWidth: 2 83 | ObjCSpaceAfterProperty: false 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 2 86 | PenaltyBreakBeforeFirstCallParameter: 19 87 | PenaltyBreakComment: 300 88 | PenaltyBreakFirstLessLess: 120 89 | PenaltyBreakString: 1000 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Right 93 | RawStringFormats: 94 | - Delimiter: pb 95 | Language: TextProto 96 | BasedOnStyle: google 97 | ReflowComments: false 98 | SortIncludes: true 99 | SortUsingDeclarations: true 100 | SpaceAfterCStyleCast: true 101 | SpaceAfterTemplateKeyword: true 102 | SpaceBeforeAssignmentOperators: true 103 | SpaceBeforeParens: ControlStatements 104 | SpaceInEmptyParentheses: false 105 | SpacesBeforeTrailingComments: 2 106 | SpacesInAngles: false 107 | SpacesInContainerLiterals: true 108 | SpacesInCStyleCastParentheses: false 109 | SpacesInParentheses: false 110 | SpacesInSquareBrackets: false 111 | Standard: Cpp11 112 | TabWidth: 8 113 | UseTab: Never 114 | ... 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plotting Data using Matplotlib from C++ 2 | 3 | I had to write some code in C++ where I wanted to plot the results of a 4 | signal processing function. 5 | The existing C++ plotting libraries had quite a learning curve, and I was 6 | already familiar with Python's Matplotlib, so I didn't want to learn an entirely 7 | new library. 8 | I then found [matplotlib-cpp](https://github.com/lava/matplotlib-cpp), C++ 9 | bindings for Matplotlib. It worked fine, but was quite limited. 10 | However, that gave me the idea to use pybind11 to call Python Matplotlib code 11 | from my C++ applications. 12 | 13 | The results are great. The C++ code just does all of its calculations, starts 14 | the Python interpreter, and passes the data to be plotted to a piece of Python 15 | code that uses Matplotlib to display the data. 16 | 17 | This repository is a simple template that I use myself when I start a new 18 | project where I want to plot data from C++. 19 | It contains a very simple C++ example program and a CMakeLists.txt file that 20 | builds the application. 21 | There are also build tasks for Visual Studio Code, which I use as my main 22 | editor. 23 | 24 | ## Installing the Dependencies 25 | 26 | To build and run the example, you need a the usual tools: a compiler, make, 27 | cmake. 28 | 29 | ~~~sh 30 | sudo apt install g++ make cmake 31 | ~~~ 32 | 33 | You also need Python 3, of course. Either install them through the repositories: 34 | 35 | ~~~sh 36 | sudo apt install python3-dev python3-pip 37 | ~~~ 38 | 39 | Or if you want the latest Python version, check out these instructions on how to 40 | build it yourself: 41 | [building Python 3 from source](https://tttapa.github.io/Pages/Ubuntu/Software-Installation/Python.html) 42 | 43 | You'll also need Matplotlib for the plots: 44 | 45 | ~~~sh 46 | python3 -m pip install --user matplotlib 47 | ~~~ 48 | 49 | Next, install pybind11. You can install it system-wide or for your user only. 50 | 51 | **System-wide** 52 | 53 | ~~~sh 54 | cd /tmp 55 | git clone https://github.com/pybind/pybind11 --depth=1 56 | mkdir pybind11/build && cd pybind11/build 57 | cmake .. -DPYBIND11_TEST=OFF 58 | sudo make install 59 | ~~~ 60 | 61 | **Local** 62 | 63 | ~~~sh 64 | cd /tmp 65 | git clone https://github.com/pybind/pybind11 --depth=1 66 | mkdir pybind11/build && cd pybind11/build 67 | cmake .. -DPYBIND11_TEST=OFF -DCMAKE_INSTALL_PREFIX=$HOME/.local 68 | make install 69 | echo "export CMAKE_PREFIX_PATH=\$HOME/.local/lib/cmake" >> ~/.profile 70 | source ~/.profile 71 | ~~~ 72 | 73 | Finally, just clone this repository: 74 | 75 | ~~~sh 76 | cd 77 | git clone https://github.com/tttapa/Pybind11-Matplotlib-Cpp 78 | ~~~ 79 | 80 | ## Building and Running the Example using the Command Line 81 | 82 | Create a `build` directory, and run CMake. You can specify the path to your 83 | Python executable if you want to use a specific one, otherwise, just use `which` 84 | to determine the default one. 85 | 86 | ~~~sh 87 | cd Pybind11-Matplotlib-Cpp 88 | mkdir build && cd build 89 | cmake .. -DPYTHON_EXECUTABLE=`which python3` 90 | ~~~ 91 | 92 | Next, simply run Make to build: 93 | 94 | ~~~sh 95 | make 96 | ~~~ 97 | 98 | Finally, run the application: 99 | 100 | ~~~sh 101 | ./main 102 | ~~~ 103 | 104 | You should see a Matplotlib plot opening. 105 | 106 | ![Plot](plot.png) 107 | 108 | ## Building and Running the Example using Visual Studio Code 109 | 110 | I also included some build tasks for Visual Studio Code that you can use. 111 | To build the application in VSCode, open this folder using `Ctrl+K Ctrl+O`, 112 | then hit `Ctrl+P`, type `task`, then a space and then "Clear build Directory 113 | and Run CMake". Press enter to execute the task. 114 | Next, hit `Ctrl+Shift+B`, select "Build & Run" using the arrow keys, and hit 115 | enter to build and run the application. 116 | 117 | There are other tasks for running "make clean" and for building without running 118 | the application as well. 119 | 120 | VSCode will probably give you a warning that `Python.h` was not found in the 121 | include path. You can just click the yellow light bulb, and select `Add to 122 | "includePath": /usr/include/python3.x`. --------------------------------------------------------------------------------