matplotlibcpp 总的来说还是十分强大的,但是其安装环境配置很麻烦,而且当使用多线程编程时,若是使用子线程来绘图,将会导致最后主线程无法退出。
- matplotlibcpp 下载:
git clone https://github.com/lava/matplotlib-cpp.git
,matplotlib-cpp文件夹下仅有matplotlibcpp .h这个头文件可用,可直接复制粘贴到自己的源文件处或导入到自定义include目录下,如:
- matplotlibcpp 下载:
cd ~
mkdir include
cp /path/to/matplotlib-cpp/matplotlibcpp.h ~/include
echo "export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:$HOME/include" >> ~/.bashrc
source ~/.bashrc
#以后放在这个文件夹下的头文件可直接 #include<matplotlibcpp.h>
- 安装两个python包:
numpy
以及matplotlib
- 安装两个python包:
conda install numpy
conda install matplotlib
- matplotlibcpp 里面include两个头文件,因此需要导入到
CPLUS_INCLUDE_PATH
环境变量中-> <Python.h> <numpy/arrayobject.h>Python.h
的位置在/home/xbq/miniconda3/include/python3.11
,而numpy/arrayobject.h
的位置在/home/xbq/miniconda3/lib/python3.11/site-packages/numpy/core/include/
下。因此将这两个一起导入环境变量
echo "export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/home/xbq/miniconda3/include/python3.11:/home/xbq/miniconda3/lib/python3.11/site-packages/numpy/core/include/" >> ~/.bashrc
source ~/.bashrc
- 以上完成了头文件的导入部分,后续还需要完成库文件的导入,如:
#include <matplotlibcpp.h>
namespace plt = matplotlibcpp;
int main(){
plt::plot({1,3,2,4});
//linux 没有图形界面
//plt::show();
plt::save("1.png");
return 0;
}
# -L 指定库文件搜索路径
# -l 指定库文件名称
g++ test1.cpp -o 1 -L/home/xbq/miniconda3/lib -lpython3.11
- 但是如果我想使用 taskflow 这个库来迭代一些过程,通过 matplotlibcpp 来展示迭代是否收敛。然后主线程无法退出:
#include <iostream>
#include <taskflow/taskflow.hpp>
#include <matplotlibcpp.h>
#include <thread>
#include <chrono>
namespace plt = matplotlibcpp;
int main() {
tf::Executor executor;
tf::Taskflow taskflow;
tf::Task A=taskflow.emplace([](){
plt::plot({1,3,2,4});
plt::save("1.png");
plt::close();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
});
executor.run_n(taskflow,10).wait();
std::cout << 1 << std::endl;
return 0;
}
//g++ progress_bar_drawing.cpp -o 1 -pthread -L/home/xbq/miniconda3/lib -lpython3.11
代码执行到 return 0;
这里,但是主线程无法退出,使用sdt::exit(0)
也无济于事。如果是在主线程循环作图的话就没有问题。
子线程已经结束,主线程无法退出
- 对于以上问题可使用子进程来解决,这样一来事情变得越来越麻烦了,为了避免环境配置等各种问题,最佳方案就是A语言将数据保存到一份文本文档里面,然后使用B语言来读取这个数据绘图。这样比A语言直接调用B语言API库要简单得多,且不需要担心bug问题。只是这样一来逼格就大大地降低了,似乎和程序员的初衷相违背。
#include <taskflow/taskflow.hpp>
#include <vector>
#include <thread>
#include <chrono>
#include <random>
#include <fstream>
std::vector<int> form_list();
int main(){
std::vector<std::vector<int>> a;
tf::Executor executor(3);
tf::Taskflow taskflow;
tf::Task A=taskflow.emplace([&](){
a.push_back(form_list());
std::ofstream out("ant.txt");
for (size_t i = 0; i < a.size(); i++){
for (size_t j = 0; j < a[i].size(); j++){
out << a[i][j] << " ";
}
out << std::endl;
}
out.close();
int result = std::system("python ant_draw.py ant.txt");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
});
executor.run_n(taskflow,10).wait();
return 0;
}
std::vector<int> form_list(){
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> distribution(0, 100);
std::vector<int> resullt;
for (size_t i = 0; i < 10; i++){resullt.push_back(distribution(gen));}
return resullt;
}
import matplotlib.pyplot as plt
import sys
import statistics
import math
if len(sys.argv) != 2:
print("Usage: python script.py <filename>")
sys.exit(1)
file=sys.argv[1]
numbers_list=[]
for line in open(file):
current_list=[]
for num in line.strip().split():
current_list.append(float(num))
numbers_list.append(current_list)
x=[i for i in range(1,len(numbers_list)+1)]
y=[statistics.mean(i) for i in numbers_list]
errors=[math.log10(statistics.stdev(i)) for i in numbers_list] #误差对数化
# 创建柱状图
fig, ax = plt.subplots()
# 绘制柱状图
bars = ax.bar(x, y, yerr=errors, capsize=5, color='blue', alpha=0.7)
# 添加标签和标题
ax.set_xlabel('Iterations')
ax.set_ylabel('Mutation number')
ax.set_title('Convergence of ant colony algorithm')
# 添加误差线的标签
#for bar, error in zip(bars, errors):
# height = bar.get_height()
# ax.text(bar.get_x() + bar.get_width() / 2, height + 0.1, f'{height:.1f} ± E^{error:.1f}', ha='center', va='bottom')
plt.savefig("ant_draw.png")