上面一节《C++使用matplotlib绘图》讲解了使用三方库调用matplotlib库进行绘图的方法。
但是因为matplotlibcpp这个库已经很久没有更新了,所以我们也继续研究了一下自己使用pybind11封装matplotlib python给C++调用的可能性,结果是可以的,过程如下。
本例基于conanio/gcc9镜像封装完成。步骤如下,
- 在conanio/gcc9容器中安装matplotlib
pip install matplotlib
- conanfile.txt添加如下依赖,主要是pybind11就可以,其他两个库这个工程没有使用到,但也是常用库
[requires]
nlohmann_json/3.11.3
boost/1.72.0
pybind11/2.12.0
[generators]
cmake
- CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(46_invoke_py2)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")
set ( CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_STANDARD 17)
add_definitions(-g)
add_definitions(-DSAVE_PERCEPTION_CONCAT_IMG)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include_directories(${INCLUDE_DIRS} /opt/pyenv/versions/3.7.13/include/python3.7m/)
LINK_DIRECTORIES(${LINK_DIRS} /opt/pyenv/versions/3.7.13/lib/)
file( GLOB main_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file( GLOB source_files ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
foreach( main_file ${main_file_list} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${main_file})
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${main_file} ${source_files})
target_link_libraries(${file} ${CONAN_LIBS} pthread python3.7m)
endforeach( main_file ${main_file_list})
matplotlib.h
#ifndef _FREDRIC_MATPLOT_LIB_H_
#define _FREDRIC_MATPLOT_LIB_H_
#include <pybind11/embed.h>
#include <map>
#include <set>
#include <vector>
#include <string>
namespace py = pybind11;
using namespace py::literals;
namespace matplotlibc {
struct matplotlib_t {
matplotlib_t() {
py::initialize_interpreter();
plt = py::module_::import("matplotlib.pyplot");
}
~matplotlib_t() {
plt.release();
py::finalize_interpreter();
}
template<typename NumericX>
void to_py_list(const std::vector<NumericX>& x, py::list* result_x) {
for (const auto &elem : x) {
result_x->append(elem);
}
}
void to_kw_args(std::map<std::string, std::string> const& keywords, std::set<std::string> const& real_keys,
py::kwargs* kw_args) {
for (const auto& pair : keywords) {
if(real_keys.find(pair.first) != real_keys.end()) {
(*kw_args)[py::str(pair.first)] = std::atof(pair.second.data());
} else {
(*kw_args)[py::str(pair.first)] = pair.second;
}
}
}
template<typename NumericX, typename NumericY>
bool scatter(const std::vector<NumericX>& x,
std::vector<NumericY> const& y,
double const s=1.0,
std::map<std::string, std::string> const& keywords = {}) {
py::list result_x;
py::list result_y;
to_py_list(x, &result_x);
to_py_list(y, &result_y);
py::object res;
if(keywords.size() > 0) {
py::kwargs kw_args;
to_kw_args(keywords, {"linewidths"}, &kw_args);
res = plt.attr("scatter")(result_x, result_y, s, **kw_args);
} else {
res = plt.attr("scatter")(result_x, result_y, s);
}
return !res.is_none();
}
void clf() {
plt.attr("clf")();
}
void save(std::string const& file_name) {
plt.attr("savefig")(file_name.c_str());
}
private:
py::module_ plt;
};
}
#endif
main.cpp
#include <iostream>
#include "matplotlib.h"
namespace plot = matplotlibc;
int main(int argc, char* argv[]) {
plot::matplotlib_t plt;
std::vector<float> x {1.0f, 2.0f, 3.0f, 4.0f};
std::vector<float> y {1.0f, 2.0f, 3.0f, 4.0f};
plt.clf();
plt.scatter(x, y, 50, {{"color", "green"}, {"linewidths", "20"}});
plt.save("result.png");
return EXIT_SUCCESS;
}
本例只是封装了scatter函数,如果你有特殊的需求,也可以封装其他函数,例如plot函数等等。套路差不多。
程序输出如下,