如安在C++中优雅地绘制图表
在C++项目中,在进行一些数据分析时每每不够直观,若能借助图表进行分析可以到达事半功倍的效果,通常可以将数据写出到文本,再在excel或python上进行图表绘制和分析,那为什么不直接在像python一样,借助三方库进行图表绘制呢?以下将先容简单易行的方法,实验在C++中优雅地绘制图表(测试情况:Windows+VS2019)。matplotlib
现实上是调用Python中的matplotlib提供的C++ API,无需编译,直接包罗头文件然后在C++中设置相应python情况就可以,对于认识python的同伴来说险些没有学习成本(其本身语法也很简单)。
prepare
[*]可访问github的网络
[*]git(非必须)
[*]python3(< 3.11)
[*]vs情况
matplotlibcpp.h
有git的直接clone到本地:
git clone https://github.com/lava/matplotlib-cpp.git
没有git可以下载代码或者直接在线复制 matplotlibcpp.h。
对 matplotlibcpp.h 作如下修改:
新版的matplotlibcpp.h中支持了C++17语法,将其改回C++11,且有重界说,必要表明。
将353行至356行代码用以下代码覆盖:
static_assert(sizeof(long long) == 8, "long type must occupy 8 bytes");
//template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
static_assert(sizeof(unsigned long long) == 8, "long type must occupy 8 bytes");
//template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
python3
matplotlibcpp.h 语法与python3.11以上版本不兼容,报错如下:
1>matpoltlibTest.cpp
1>D:\Program Files\MatplotlibCpp\matplotlibcpp.h(174): error C4996: 'Py_SetProgramName': deprecated in 3.11
1>d:\programdata\anaconda3\include\pylifecycle.h(37): note: 参见“Py_SetProgramName”的声明
1>D:\Program Files\MatplotlibCpp\matplotlibcpp.h(182): error C4996: 'PySys_SetArgv': deprecated in 3.11
1>d:\programdata\anaconda3\include\sysmodule.h(13): note: 参见“PySys_SetArgv”的声明
利用3.11以下的python版本即可,这里我创建一个新的假造情况:
(base)>conda create -n Env310 python=3.10.16
(base)>conda activate Env310
(Env310)>conda install matplotlib numpy
将 复制到 D:\Program Files\MatplotlibCpp 下(非必须)。
vs configure
创建一个vs项目,切换为Release x64平台,在属性管理器的【Release | 64】下中新增一个属性表:
https://i-blog.csdnimg.cn/direct/91c21ee930714788849e74b3dd9467f8.png#pic_center
双击该属性表,开始设置:
【通用属性】-【VC++目录】-【包罗目录】添加:
D:\Program Files\MatplotlibCpp
D:\ProgramData\anaconda3\envs\Env310
\include
D:\ProgramData\anaconda3\envs\Env310
\Lib\site-packages\numpy\_core\include
https://i-blog.csdnimg.cn/direct/ea2eff8882344d939b02bcbf8cbfc3ff.png#pic_center
注意:不同版本的numpy包罗目录地点路径可能不同,可通过以下代码确定:
import numpy
print(numpy.get_include())
【通用属性】-【VC++目录】-【库目录】添加:
D:\ProgramData\anaconda3\envs\Env310
\libs
https://i-blog.csdnimg.cn/direct/e5515da3bb254e58a9bafff31e4258b3.png#pic_center
【通用属性】-【链接器】-【常规】-【附加库目录】添加:
D:\ProgramData\anaconda3\envs\Env310
https://i-blog.csdnimg.cn/direct/c45aa6760a9346db9b6fcdce088b3c72.png#pic_center
【通用属性】-【链接器】-【输入】-【附加依赖项】添加:
python3.lib
https://i-blog.csdnimg.cn/direct/557ff34826054688bfb79cea97ef8014.png#pic_center
保存属性表,在解决方案管理器中右击,更改属性:
【设置属性】-【调试】-【工作目录】更改为:
D:\ProgramData\anaconda3\envs\Env310
https://i-blog.csdnimg.cn/direct/45eb7628745d46edaa7c5035efd3595c.png#pic_center
test
添加C++测试代码,如:
#include <cmath>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
#define M_PI 3.141592653589793238432643
int main()
{
// Prepare data.
int n = 5000; // number of data points
std::vector<double> x(n), y(n);
for (int i = 0; i < n; ++i)
{
double t = 2 * M_PI * i / n;
x.at(i) = 16 * sin(t) * sin(t) * sin(t);
y.at(i) = 13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t);
}
// plot() takes an arbitrary number of (x,y,format)-triples.
// x must be iterable (that is, anything providing begin(x) and end(x)),
// y must either be callable (providing operator() const) or iterable.
plt::plot(x, y, "r-", x, [](double d)
{
return 12.5 + abs(sin(d));
}, "k-");
// show plots
plt::show();
}
运行,可得图片如下,设置完成。
https://i-blog.csdnimg.cn/direct/d30142070ebc43bfa184f3831a397a2d.png#pic_center
Gnuplot
雷同matplotlib,先安装gnuplot本体,然后通过C++API调用库,不同的是其可通过下令在终端中利用,必要一定学习成本。
prepare
[*]可访问github的网络
[*]git(非必须)
[*]boost
[*]vs情况
gnuplot
在官网 可找到最新稳固版下载页面,下载编译好的二进制文件。
https://i-blog.csdnimg.cn/direct/8dab3068c4e14792a65da158e72c32c0.png#pic_center
解压后长这个样子:
https://i-blog.csdnimg.cn/direct/fd3c92b2c3a0471cbb2f23f494ba64a7.png#pic_center
在bin文件夹下双击打开gnupolt.exe,输入测试代码如下:
plot [-6:6] [-3:3] sin(x),cos(x)
可得:
https://i-blog.csdnimg.cn/direct/c9bdbdef7afe471799f3f8c4699e3dff.png#pic_center
将整个gunplot文件夹剪切至D:\Program Files\下,并重命名为Gnuplot(个人喜好),然后将D:\Program Files\Gnuplot\bin添加至系统情况变量Path中,通过重启或者其他方式确保情况变量生效(打开任意终端,输入gunplot未报错即可)。
https://i-blog.csdnimg.cn/direct/4bba92788f614b19ab48fb0dd47ac2b5.png#pic_center
那么同样可以在c++代码中调用该exe:
#include <stdio.h>
void main()
{
FILE* pipe = _popen("gnuplot", "w");
if (pipe == NULL)
{
exit(-1);
}
fprintf(pipe, "set terminal wxt size 600, 400\n");
fprintf(pipe, "unset border\n");
fprintf(pipe, "set dummy u, v\n");
fprintf(pipe, "set angles degrees\n");
fprintf(pipe, "set parametric\n");
fprintf(pipe, "set view 60, 136, 1.22, 1.26\n");
fprintf(pipe, "set samples 64, 64\n");
fprintf(pipe, "set isosamples 13, 13\n");
fprintf(pipe, "set mapping spherical\n");
fprintf(pipe, "set style data lines\n");
fprintf(pipe, "unset xtics\n");
fprintf(pipe, "unset ytics\n");
fprintf(pipe, "unset ztics\n");
fprintf(pipe, "set title 'Labels colored by GeV plotted in spherical coordinate system'\n");
fprintf(pipe, "set urange [ -90.0000 : 90.0000 ] noreverse nowriteback\n");
fprintf(pipe, "set vrange [ 0.00000 : 360.000 ] noreverse nowriteback\n");
fprintf(pipe, "set xrange [ * : * ] noreverse writeback\n");
fprintf(pipe, "set x2range [ * : * ] noreverse writeback\n");
fprintf(pipe, "set yrange [ * : * ] noreverse writeback\n");
fprintf(pipe, "set y2range [ * : * ] noreverse writeback\n");
fprintf(pipe, "set zrange [ * : * ] noreverse writeback\n");
fprintf(pipe, "set cblabel 'GeV'\n");
fprintf(pipe, "set cbrange [ 0.00000 : 8.00000 ] noreverse nowriteback\n");
fprintf(pipe, "set rrange [ * : * ] noreverse writeback\n");
fprintf(pipe, "set colorbox user\n");
fprintf(pipe, "set colorbox vertical origin screen 0.9, 0.2 size screen 0.02, 0.75 frontnoinvert bdefault\n");
fprintf(pipe, "NO_ANIMATION = 1\n");
fprintf(pipe, "splot cos(u)*cos(v),cos(u)*sin(v),sin(u) notitle with lines lt 5\n");
fprintf(pipe, "pause mouse\n");
_pclose(pipe);
}
结果如下:
https://i-blog.csdnimg.cn/direct/902ba2c4da0f4a7a8bbbbf6ba212acf5.png#pic_center
但是,以这种方式,如果要批量绘制自界说的数据,仍必要先存数据为文本,然后再读取,这不是我想要的直接绘制的效果。但是不慌,再做一些简单设置就可以实现了。
gnuplot-iostream.h
有git的直接clone到本地
git clone https://github.com/dstahlke/gnuplot-iostream.git
没有git可以下载代码或者直接在线复制 gnuplot-iostream.h
对 gnuplot-iostream.h 作如下修改
新版的gnuplot-iostream.h中支持了C++17语法,将其改回C++11,跟上面的matplotlibcpp.h是同样类型的改动
168-169行:
static_assert( is_like_stl_container<std::vector<int>>);
static_assert(!is_like_stl_container<int>);
改为:
static_assert(is_like_stl_container<std::vector<int>>, "Should be STL-like container");
static_assert(!is_like_stl_container<int>, "int is not STL-like container");
186行:
static_assert(is_boost_tuple_nulltype<boost::tuples::null_type>);
改为:
static_assert(is_boost_tuple_nulltype<boost::tuples::null_type>, "Should be boost null type");
197-200行:
static_assert( is_boost_tuple<boost::tuple<int>>);
static_assert( is_boost_tuple<boost::tuple<int, int>>);
static_assert(!is_boost_tuple<std::tuple<int>>);
static_assert(!is_boost_tuple<std::tuple<int, int>>);
改为:
static_assert(is_boost_tuple<boost::tuple<int>>, "Should be boost::tuple");
static_assert(is_boost_tuple<boost::tuple<int, int>>, "Should be boost::tuple");
static_assert(!is_boost_tuple<std::tuple<int>>, "std::tuple is not boost::tuple");
static_assert(!is_boost_tuple<std::tuple<int, int>>, "std::tuple is not boost::tuple");
2436-2437行
static_assert( is_eigen_matrix<Eigen::MatrixXf>);
static_assert(!is_eigen_matrix<int>);
改为
static_assert(is_eigen_matrix<Eigen::MatrixXf>, "Should be Eigen::Matrix");
static_assert(!is_eigen_matrix<int>, "int is not an Eigen::Matrix");
2442行
static_assert(dont_treat_as_stl_container<Eigen::MatrixXf>);
改为
static_assert(dont_treat_as_stl_container<Eigen::MatrixXf>, "Eigen::Matrix should not be treated as an STL container");
然后在上述gnuplot的安装目录D:\Program Files\Gnuplot下新建include文件夹,将改动后的gnuplot-iostream.h拷贝过去。
boost
由于gnuplot的C++调用必要boost支持,这里也先容一下boost的设置,因为有预编译好的库,所以也比较easy。
在boost官网的下载页面找到预编译好的Windows库:
https://i-blog.csdnimg.cn/direct/3f3c39451fb64d6ca0037ca35ebb394e.png#pic_center
根据所需情况选择对应版本。
https://i-blog.csdnimg.cn/direct/1110a34dfb2443fc865f76f29903ccd3.png#pic_center
这里我根据本身的情况选择较早的1.80.0 VS140版(注意和本身的情况对应)。
https://i-blog.csdnimg.cn/direct/7e0a3754e2e7481694f7ad428e15602f.png#pic_center
双击运行:
https://i-blog.csdnimg.cn/direct/68c1017832a0495cac9f630604ebb8a0.png#pic_center
更改安装目录:
https://i-blog.csdnimg.cn/direct/1805be4637b54da981ce623d53089f94.png#pic_center
https://i-blog.csdnimg.cn/direct/190063d03d73471a88c2290a2da3d58c.png#pic_center
vs configure
创建一个vs项目,切换为Release x64平台,在属性中将【平台工具集】改为Visual Studio 2015 (v140),在属性管理器的【Release | 64】下中新增一个属性表【Gnupolt_Release_x64.props】
双击该属性表,开始设置
【通用属性】-【VC++目录】-【包罗目录】添加:
D:\Program Files\Gnuplot\include
D:\Program Files\boost_1_80_0\boost
【通用属性】-【VC++目录】-【库目录】添加:
D:\Program Files\boost_1_80_0\lib64-msvc-14.0
https://i-blog.csdnimg.cn/direct/9d5b80d9e7b44c8e85de4bb2dae34322.png#pic_center
应用,保存属性表。
test
添加C++测试代码,如:
#include <cmath>
#include <vector>
#include <gnuplot-iostream.h>
#define M_PI 3.141592653589793238432643
int main()
{
// 初始化 Gnuplot 对象
Gnuplot gp;
// 设置标题和样式
gp << "set title 'Sharper Heart Shape'\n";
gp << "set size ratio -1\n"; // 确保 x 和 y 轴比例一致
gp << "set nokey\n"; // 不显示图例
gp << "set samples 1000\n";// 增加采样点使曲线更平滑
gp << "set xrange [-20:20]\n"; // 设置 x 轴范围
gp << "set yrange [-20:20]\n"; // 设置 y 轴范围
// 定义参数化方程的数据点
std::vector<std::pair<double, double>> points;
const int num_points = 1000; // 数据点数量
for (int i = 0; i <= num_points; ++i)
{
double t = 2 * M_PI * i / num_points; // 参数 t 从 0 到 2π
double x = 16 * pow(sin(t), 3); // 使用新的 x 方程
double y = (13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)); // 使用新的 y 方程
points.emplace_back(x, y);
}
// 绘制爱心形状
gp << "plot '-' with lines lc rgb 'red' lw 2 title 'Heart'\n";
for (const auto& point : points)
{
gp << point.first << " " << point.second << "\n";
}
gp << "e\n"; // 结束数据流
return 0;
}
运行,可得图片如下,设置完成。
https://i-blog.csdnimg.cn/direct/becca2aafefa4755b69281b54785bf86.png#pic_center
MathGL
必要特定的情况和复杂的设置,其发布的库也是Linux平台下的,在Windows必要和其他依赖库一起重新编译。
同样在官网找到最新版下载入口,这里有两个版本,我两个版本都下载并设置了,很不幸都在测试阶段报错了,要么缺少头文件要么缺依赖库。固然可以拿源码本身重新cmake,但其必要依赖其他可视化库(背离了简单易行的初衷),本着世上无难事只要肯放弃的精神,停息MathGL的测试。
https://i-blog.csdnimg.cn/direct/a40e9aed60ba4863b5d724ebfda2e543.png#pic_center
测试代码:
#include <mgl2/mgl.h>
int main()
{
mglGraph gr;
gr.SetRange('x', -1, 1);
gr.SetRange('y', -1, 1);
gr.Axis();
gr.FPlot("sin(1.7*2*pi*x) + sin(1.9*2*pi*x)", "r-2");
gr.WriteFrame("test.png");
return 0;
}
mathgl-8.0.3-ucrt64 报错:
1>D:\Program Files\MathGL2\include\mgl2\data_cf.h(26,10): fatal error C1083: 无法打开包括文件: “gsl/gsl_vector.h”: No such file or directory
mathgl-8.0.3.LGPL-ucrt64 报错:
1>D:\Program Files\MathGL2\include\mgl2\data.h(27,10): fatal error C1083: 无法打开包括文件: “armadillo”: No such file or directory
打完收工。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]