当前位置: 首页 > news >正文

C++调用GnuPlot一维绘图

这篇文章介绍了一个C++类Gnuplot,用于通过管道与Gnuplot程序交互,实现数据可视化。类的主要功能包括:1) 通过Windows管道创建与Gnuplot进程的通信;2) 提供多种绘图方法,如绘制一维数组(折线图)、多条曲线和二维数组(热力图);3) 支持设置图表标题、坐标轴标签;4) 支持将图表保存为PNG或PDF格式。示例代码展示了如何使用该类绘制正弦波曲线和热力图。该封装简化了C++程序调用Gnuplot进行数据可视化的过程。

gnuplot_i.hpp

#ifndef GNUPLOT_I_HPP
#define GNUPLOT_I_HPP#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <windows.h>class Gnuplot {
private:HANDLE hChildStd_IN_Rd = NULL;HANDLE hChildStd_IN_Wr = NULL;HANDLE hChildStd_OUT_Rd = NULL;HANDLE hChildStd_OUT_Wr = NULL;HANDLE hChildProcess = NULL;bool is_open;public:Gnuplot() : is_open(false) {// 创建匿名管道//创建安全属性结构体,设置句柄可继承(子进程能使用父进程创建的管道句柄)SECURITY_ATTRIBUTES saAttr;saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);saAttr.bInheritHandle = TRUE;saAttr.lpSecurityDescriptor = NULL;//创建输出管道(子进程输出 → 父进程读取)if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))throw std::runtime_error("创建输出管道失败");// 创建输入管道(父进程写入 → 子进程输入)//hChildStd_IN_Rd:子进程用于读取输入的句柄(读端)//hChildStd_IN_Wr:父进程用于写入命令的句柄(写端)if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0))throw std::runtime_error("创建输入管道失败");// 设置子进程启动信息STARTUPINFO siStartInfo;PROCESS_INFORMATION piProcInfo;ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));siStartInfo.cb = sizeof(STARTUPINFO);siStartInfo.hStdError = hChildStd_OUT_Wr;siStartInfo.hStdOutput = hChildStd_OUT_Wr;siStartInfo.hStdInput = hChildStd_IN_Rd;siStartInfo.dwFlags |= STARTF_USESTDHANDLES;// 准备Gnuplot命令行std::wstring gnuplot_cmd = L"gnuplot.exe -persistent";// 创建Gnuplot进程if (!CreateProcess(NULL,const_cast<LPWSTR>(gnuplot_cmd.c_str()),NULL, NULL, TRUE, 0, NULL, NULL,&siStartInfo, &piProcInfo)) {throw std::runtime_error("启动Gnuplot进程失败");}// 关闭不需要的句柄CloseHandle(piProcInfo.hProcess);CloseHandle(piProcInfo.hThread);CloseHandle(hChildStd_OUT_Wr);CloseHandle(hChildStd_IN_Rd);is_open = true;}~Gnuplot() {close();}void close() {if (is_open) {sendCommand("quit\n");CloseHandle(hChildStd_IN_Wr);CloseHandle(hChildStd_OUT_Rd);is_open = false;}}// 保存图表到文件Gnuplot& saveToFile(const std::string& filename, const std::string& format = "png", int width = 800, int height = 600) {if (format == "png") {sendCommand("set terminal pngcairo size " + std::to_string(width) + "," + std::to_string(height));} else if (format == "pdf") {sendCommand("set terminal pdfcairo size " + std::to_string(width/72.0) + "," + std::to_string(height/72.0) + " font 'Arial,10'");}sendCommand("set output '" + filename + "'");sendCommand("replot");sendCommand("set output"); // 恢复标准输出return *this;}// 绘制一维数组(折线图)Gnuplot& plotArray(const std::vector<double>& key,const std::vector<double>& data, const std::string& title = "Data Plot") {if(key.size() != data.size())throw std::runtime_error("key.size() != data.size()");sendCommand("plot '-' with lines title '" + title + "'");// 发送数据std::string dataStr;for (size_t i = 0; i < data.size(); ++i) {dataStr += std::to_string(key[i]) + " " + std::to_string(data[i]) + "\n";}dataStr += "e\n";DWORD dwWritten;WriteFile(hChildStd_IN_Wr, dataStr.c_str(), dataStr.length(), &dwWritten, NULL);return *this;}// 绘制多条曲线Gnuplot& plotArrays(const std::vector<std::vector<double>>& keys,const std::vector<std::vector<double>>& datasets,const std::vector<std::string>& titles,const std::vector<std::string>& styles = {}) {if (datasets.empty() || datasets.size() != titles.size() || keys.size() != titles.size()) {throw std::invalid_argument("数据集和标题数量不匹配");}// 构建plot命令std::string cmd = "plot ";for (size_t i = 0; i < datasets.size(); ++i) {std::string style = (i < styles.size()) ? styles[i] : "lines";cmd += "'-' with " + style + " title '" + titles[i] + "'";if (i < datasets.size() - 1) {cmd += ", ";}}sendCommand(cmd);// 依次发送每个数据集for (int j = 0; j < datasets.size(); j++) {std::vector<double> data = datasets[j];std::vector<double> key = keys[j];for (size_t i = 0; i < data.size(); ++i) {sendCommand(std::to_string(key[i]) + " " + std::to_string(data[i]));}sendCommand("e");}return *this;}// 绘制二维数组(热力图)Gnuplot& plotHeatmap(const std::vector<std::vector<double>>& data, const std::string& title = "Heatmap") {sendCommand("set view map");sendCommand("set pm3d");sendCommand("set palette rgbformulae 33,13,10");sendCommand("splot '-' matrix with pm3d title '" + title + "'");// 发送矩阵数据std::string dataStr;for (const auto& row : data) {for (double val : row) {dataStr += std::to_string(val) + " ";}dataStr += "\n";}dataStr += "e\n";DWORD dwWritten;WriteFile(hChildStd_IN_Wr, dataStr.c_str(), dataStr.length(), &dwWritten, NULL);return *this;}Gnuplot& sendCommand(const std::string& cmd) {if (!is_open) throw std::runtime_error("Gnuplot连接已关闭");DWORD dwWritten;std::string cmd_with_nl = cmd + "\n";if (!WriteFile(hChildStd_IN_Wr, cmd_with_nl.c_str(),cmd_with_nl.length(), &dwWritten, NULL)) {throw std::runtime_error("向Gnuplot发送命令失败");}return *this;}Gnuplot& setTitle(const std::string& title) {return sendCommand("set title \"" + escapeString(title) + "\"");}Gnuplot& setXLabel(const std::string& label) {return sendCommand("set xlabel \"" + escapeString(label) + "\"");}Gnuplot& setYLabel(const std::string& label) {return sendCommand("set ylabel \"" + escapeString(label) + "\"");}// 转义双引号和反斜杠static std::string escapeString(const std::string& str) {std::string result;for (char c : str) {if (c == '\"' || c == '\\') result += '\\';result += c;}return result;}
};
#endif // GNUPLOT_I_HPP

main.cpp

#include "gnuplot_i.hpp"
#include <vector>
#include <cmath>int main() {try {// 示例1:绘制一维数组(正弦波)std::vector<std::vector<double>> Datas;std::vector<std::vector<double>> keys;std::vector<double> sinData;std::vector<double> key;for (double x = 0; x < 2 * 3.14159; x += 0.1) {sinData.push_back(std::sin(x));key.push_back(x*10);}std::vector<double> sinData1;std::vector<double> key1;for (double x = 0; x < 100; x += 1) {sinData1.push_back(x);key1.push_back(x);}keys.push_back(key);Datas.push_back(sinData);keys.push_back(key1);Datas.push_back(sinData1);std::vector<std::string> titles;titles.push_back("曲线1");titles.push_back("曲线2");// 创建Gnuplot对象Gnuplot gp;// 绘制一维数据gp.setTitle("").setXLabel("X").setYLabel("Y").plotArrays(keys, Datas, titles);Gnuplot gp1;gp1.setTitle("").setXLabel("X").setYLabel("Y").plotArrays(keys, Datas, titles);std::cout << "按Enter键退出..." << std::endl;std::cin.get();/*** // 示例2:绘制二维数组(热力图)std::vector<std::vector<double>> heatmapData(20, std::vector<double>(20, 0.0));for (int i = 0; i < 20; ++i) {for (int j = 0; j < 20; ++j) {heatmapData[i][j] = std::sin(i/5.0) * std::cos(j/5.0);}}std::cout << "按Enter键继续查看热力图..." << std::endl;std::cin.get();// 绘制二维数据gp.setTitle("热力图示例").setXLabel("X轴").setYLabel("Y轴").plotHeatmap(heatmapData, "函数 f(x,y) = sin(x/5)*cos(y/5)");// 保存热力图到文件gp.saveToFile("heatmap.png", "png");std::cout << "图表已生成并保存到 heatmap.png" << std::endl;std::cout << "按Enter键退出..." << std::endl;std::cin.get();*/} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;return 1;}return 0;
}    

http://www.dtcms.com/a/297380.html

相关文章:

  • 后端项目中大量 SQL 执行的性能优化
  • Oracle 19C Data Guard :从原理到实践的高可用解决方案
  • 在VSCode配置Java开发环境的保姆级教程(适配各类AI编程IDE)
  • CentOS8 使用 Docker 搭建 Jellyfin 家庭影音服务器
  • 电科金仓新一代数据库一体机:以 “云数据库 - AI 版” 引领 AI 时代数据库变革
  • 文心4.5开源之路:从封闭到开放的力量
  • PHP 面向对象
  • HTML:从 “小白” 到 “标签侠” 的修炼手册
  • vue 渲染 | 不同类型的元素渲染的方式(vue组件/htmlelement/纯 html)
  • 低空飞行调度系统
  • STM32-PWM输入捕获的配置
  • 私有化大模型架构解决方案构建指南
  • js实现宫格布局图片放大交互动画
  • 文件包含学习总结
  • 数据库设计双刃剑:范式规范与反范式性能的终极权衡
  • 在 IntelliJ IDEA 中打开这个用于设置 Git 用户名(Name)和邮箱(Email)的特定弹窗
  • 【C++详解】模板进阶 非类型模板参数,函数模板特化,类模板全特化、偏特化,模板分离编译
  • Linux下使用VSCode配置GCC环境与调试指南
  • 【JavaEE】Spring Web MVC(上)
  • [spring6: HttpSecurity]-全新写法
  • 【小沐学GIS】基于Unity3d绘制三维数字地球Earth(Unity3d、OpenGL、GIS)
  • Cacti 前台命令注入漏洞(CVE-2022-46169)
  • Dockerfile 文件及指令详解
  • 《C++初阶之STL》【vector容器:详解 + 实现】
  • 【Docker项目实战】在Docker环境下部署go-file文件分享工具
  • 伯俊科技× OB Cloud:零售业落地AI的“三步走”渐进式发展实践
  • Go、Node.js、Python、PHP、Java五种语言的直播推流RTMP协议技术实施方案和思路-优雅草卓伊凡
  • 冠捷科技 | 内生外化,精准触达,实现数字化转型精准赋能
  • 我从农村来到了大城市
  • (LeetCode 面试经典 150 题) 57. 插入区间 (数组)