如何在C++中優雅地繪制圖表

如何在C++項目中優雅地繪制圖表

  • matplotlib
    • prepare
    • matplotlibcpp.h
    • python3
    • vs configure
    • test
  • Gnuplot
    • prepare
    • gnuplot
    • gnuplot-iostream.h
    • boost
    • vs configure
    • test
  • MathGL

在C++項目中,在進行一些數據分析時往往不夠直觀,若能借助圖表進行分析可以達到事半功倍的效果,通常可以將數據寫出到文本,再在excel或python上進行圖表繪制和分析,那為什么不直接在像python一樣,借助三方庫進行圖表繪制呢?以下將介紹簡單易行的方法,嘗試在C++中優雅地繪制圖表(測試環境:Windows+VS2019)。


matplotlib

實際上是調用Python中的matplotlib提供的C++ API,無需編譯,直接包含頭文件然后在C++中配置相應python環境就可以,對于熟悉python的伙伴來說幾乎沒有學習成本(其本身語法也很簡單)。

prepare

  1. 可訪問github的網絡
  2. git(非必須)
  3. python3(< 3.11)
  4. 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】下中新增一個屬性表:

在這里插入圖片描述
雙擊該屬性表,開始配置:

【通用屬性】-【VC++目錄】-【包含目錄】添加:

D:\Program Files\MatplotlibCpp
D:\ProgramData\anaconda3\envs\Env310\include
D:\ProgramData\anaconda3\envs\Env310\Lib\site-packages\numpy\_core\include

在這里插入圖片描述
注意:不同版本的numpy包含目錄所在路徑可能不同,可通過以下代碼確定:

import numpy
print(numpy.get_include())

【通用屬性】-【VC++目錄】-【庫目錄】添加:

D:\ProgramData\anaconda3\envs\Env310\libs

在這里插入圖片描述
【通用屬性】-【鏈接器】-【常規】-【附加庫目錄】添加:

D:\ProgramData\anaconda3\envs\Env310

在這里插入圖片描述

【通用屬性】-【鏈接器】-【輸入】-【附加依賴項】添加:

python3.lib

在這里插入圖片描述

保存屬性表,在解決方案管理器中右擊,更改屬性:
【配置屬性】-【調試】-【工作目錄】更改為:

D:\ProgramData\anaconda3\envs\Env310

在這里插入圖片描述

test

添加C++測試代碼,如:

#include <cmath>
#include "matplotlibcpp.h"namespace plt = matplotlibcpp;#define M_PI 3.141592653589793238432643int main()
{// Prepare data.int n = 5000; // number of data pointsstd::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 plotsplt::show();
}

運行,可得圖片如下,配置完成。

在這里插入圖片描述


Gnuplot

類似matplotlib,先安裝gnuplot本體,然后通過C++API調用庫,不同的是其可通過命令在終端中使用,需要一定學習成本。

prepare

  1. 可訪問github的網絡
  2. git(非必須)
  3. boost
  4. vs環境

gnuplot

在官網 可找到最新穩定版下載頁面,下載編譯好的二進制文件。

在這里插入圖片描述
解壓后長這個樣子:
在這里插入圖片描述
bin文件夾下雙擊打開gnupolt.exe,輸入測試代碼如下:

plot [-6:6] [-3:3] sin(x),cos(x)

可得:
在這里插入圖片描述

將整個gunplot文件夾剪切至D:\Program Files\下,并重命名為Gnuplot(個人喜好),然后將D:\Program Files\Gnuplot\bin添加至系統環境變量Path中,通過重啟或者其他方式確保環境變量生效(打開任意終端,輸入gunplot未報錯即可)。

在這里插入圖片描述

那么同樣可以在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 front  noinvert 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);
}

結果如下:
在這里插入圖片描述
但是,以這種方式,如果要批量繪制自定義的數據,仍需要先存數據為文本,然后再讀取,這不是我想要的直接繪制的效果。但是不慌,再做一些簡單配置就可以實現了。


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庫:

在這里插入圖片描述
根據所需環境選擇對應版本。

在這里插入圖片描述

這里我根據自己的環境選擇較早的1.80.0 VS140版(注意和自己的環境對應)。
在這里插入圖片描述
雙擊運行:
在這里插入圖片描述
更改安裝目錄:
在這里插入圖片描述

在這里插入圖片描述


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

在這里插入圖片描述

應用,保存屬性表。


test

添加C++測試代碼,如:


#include <cmath>
#include <vector>
#include <gnuplot-iostream.h>#define M_PI 3.141592653589793238432643int 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;
}

運行,可得圖片如下,配置完成。
在這里插入圖片描述


MathGL

需要特定的環境和復雜的配置,其發布的庫也是Linux平臺下的,在Windows需要和其他依賴庫一起重新編譯。

同樣在官網找到最新版下載入口,這里有兩個版本,我兩個版本都下載并配置了,很不幸都在測試階段報錯了,要么缺少頭文件要么缺依賴庫。雖然可以拿源碼自己重新cmake,但其需要依賴其他可視化庫(背離了簡單易行的初衷),本著世上無難事只要肯放棄的精神,暫停MathGL的測試。

在這里插入圖片描述
測試代碼:

#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

打完收工。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76943.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76943.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76943.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue3使用keep-alive緩存組件與踩坑日記

目錄 一.了解一下KeepAlive 二.使用keep-alive標簽緩存組件 1.聲明Home頁面名稱 三.在路由出口使用keep-alive標簽 四.踩坑點1&#xff1a;可能需要配置路由&#xff08;第三點完成后有效可忽略&#xff09; 五.踩坑點2&#xff1a;沒有找到正確的路由出口 一.了解一下Kee…

ros通信機制學習——latched持久化機制

點云的地圖的發送邏輯中&#xff0c;我發現每次使用rostopic echo 時只會打印一次&#xff0c;然后就不會再打印了。并且rviz中也是始終都會顯示的&#xff0c;這里面其實就是用到了latched持久話機制&#xff0c;可以接受這最后一次發布的消息。 我們通過一個具體的項目來學習…

力扣每日打卡 1922. 統計好數字的數目 (中等)

力扣 1922. 統計好數字的數目 中等 前言一、題目內容二、解題方法1. 暴力解法&#xff08;會超時&#xff0c;此法不通&#xff09;2. 快速冪運算3. 組合計數的思維邏輯分析組合計數的推導例子分析思維小結論 4.官方題解4.1 方法一&#xff1a;快速冪 三、快速冪運算快速冪運算…

如何使用通義靈碼玩轉Docker - AI助手提升開發效率

一、引言 Docker 作為一種流行的虛擬化技術&#xff0c;能夠幫助開發者快速搭建所需的運行環境。然而&#xff0c;對于初學者來說&#xff0c;掌握 Docker 的基本概念和使用方法可能會遇到一些挑戰。本文將介紹如何利用通義靈碼這一智能編碼助手&#xff0c;幫助你更高效地學習…

從一到無窮大 #45:InfluxDB MCP Server 構建:從工程實踐到價值重構

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。 本作品 (李兆龍 博文, 由 李兆龍 創作)&#xff0c;由 李兆龍 確認&#xff0c;轉載請注明版權。 文章目錄 工程實踐遇到的問題MCP Host選擇開發流程 結果展現可能性展望工作生活帶來的變化 MCP…

JAVA SDK通過proxy對接google: GCS/FCM

前言&#xff1a;因為國內調用google相關api需要通過代理訪問(不想設置全局代理)&#xff0c;所以在代理這里經常遇到問題&#xff0c;先說一下結論 GCS 需要設置全局代理或自定義代理選擇器&#xff0c; FCM sdk admin 在初始化firebaseApp時是支持設置的。 GCS: 開始時嘗試在…

【NLP】24. spaCy 教程:自然語言處理核心操作指南(進階)

spaCy 中文教程&#xff1a;自然語言處理核心操作指南&#xff08;進階&#xff09; 1. 識別文本中帶有“百分號”的數字 import spacy# 創建一個空的英文語言模型 nlp spacy.blank("en")# 處理輸入文本 doc nlp("In 1990, more than 60% of people in East…

關于香橙派OrangePi 5 Ultra 這個開源板子,開發Android

我下載了它資料中的開源Android13 系統SDK&#xff0c; 這個SDK連個git 都沒有&#xff0c;把這種代碼釋放能稱為開源嗎&#xff1f;&#xff1f; 并且也就是說你買了這個板子&#xff0c;里面是沒有任何關于RK3588的開發文檔&#xff0c;如果你沒玩過其他RK平臺&#xff0c;估…

WHAT - React Portal 機制:將子組件渲染到 DOM 的指定節點

文章目錄 適合場景基本語法示例&#xff1a;Modal 彈窗1. 創建一個簡單的 Modal.tsx2. 在 App 中使用 為什么要用 Portal&#xff1f;TypeScript 中 Portal 類型定義&#xff1f; 適合場景 React Portal 是 React 提供的一種機制&#xff0c;讓你可以將子組件渲染到 DOM 的指定…

數據結構---跳表

目錄 一、跳表的概念 為什么要使用隨機值來確定層高 二、跳表的分析 &#xff08;1&#xff09;查找過程 &#xff08;2&#xff09;性能分析 三、跳表的實現 四、與紅黑樹哈希表的對比 skiplist本質上也是一種查找結構&#xff0c;用于解決算法中的查找問題&#xff0c…

PCDN通過個人路由器,用更靠近用戶的節點來分發內容,從而達到更快地網絡反應速度

PCDN&#xff08;P2P CDN&#xff09;的核心思想正是利用個人路由器、家庭寬帶設備等分布式邊緣節點&#xff0c;通過就近分發內容來降低延遲、提升網絡響應速度&#xff0c;同時降低傳統CDN的帶寬成本。以下是其技術原理和優勢的詳細分析&#xff1a; 1. 為什么PCDN能更快&…

用excel做九乘九乘法表

公式&#xff1a; IF($A2>B 1 , 1, 1,A2 & “" & B$1 & “” & $A2B$1,”")

凡泰極客亮相QCon2025鴻蒙專場,解析FinClip“技術+生態”雙引擎

2025年4月10日&#xff0c;備受矚目的QCon開發者技術峰會盛大舉行&#xff0c;本次活動開設鴻蒙專場以“HarmonyOS NEXT 創新特性與行業實踐”為主題&#xff0c;匯聚了眾多鴻蒙生態的領軍人物與技術專家&#xff0c;共同探討鴻蒙操作系統的技術創新與行業應用。 凡泰極客CTO徐…

java HttpServletRequest 和 HttpServletResponse

HttpServletRequest 和 HttpServletResponse 詳解 1. HttpServletRequest&#xff08;HTTP 請求對象&#xff09; HttpServletRequest 是 Java Servlet API 提供的接口&#xff0c;用于封裝客戶端的 HTTP 請求信息。它繼承自 ServletRequest&#xff0c;并增加了 HTTP 協議相…

HAL TIM PWM產生 藍橋杯

目錄 0.原理 0.1 CNT和CCR關系 0.2 PWM模式1模式2 1. cubemx配置 需求(將PA1輸出1Khz的 50&#xff05;占空比的方波) 1.0 PWM的頻率計算: 2.代碼 0.原理 0.1 CNT和CCR關系 CNT計數器和CCR比較器進行比較,如果是向上計數,CNT逐漸增加,CCR是虛線位置,也是用戶自定義的…

python入門:簡單介紹和python和pycharm軟件安裝/學習網址/pycharm設置(改成中文界面,主題,新建文件)

Python 目前是 AI 開發的首選語言 軟件安裝 python解釋器 官網下載 Python |Python.org 勾選 Add python.exe to PATH 將python.exe添加到PATH 勾選這個選項會將Python的可執行文件路徑添加到系統的環境變量PATH中。這樣做的好處是&#xff0c;你可以在命令行中從任何位置直…

CMD命令行筆記

CMD命令行筆記&#xff0c;涵蓋常用命令及實用技巧&#xff0c;適合快速查閱&#xff1a; 一、基礎操作 打開CMD Win R → 輸入 cmd → 回車管理員模式&#xff1a;右鍵開始菜單 → 選擇“命令提示符&#xff08;管理員&#xff09;” 常用命令 help&#xff1a;查看所有命令…

android中dp和px的關系

關于android的dp和px的關系是我剛開始學習android的第一個知識點&#xff0c;不知不覺學安卓也有一年了&#xff0c;但是偶然間我發現我理解的dp和px的關系一直是錯的&#xff0c;真的是有一點搞笑&#xff0c;今天特意寫一篇博客紀念一下這個我理解錯一年的知識點。 dp和px之間…

(四)機器學習---邏輯回歸及其Python實現

之前我們提到了常見的任務和算法&#xff0c;本篇我們使用邏輯回歸來進行分類 分類問題回歸問題聚類問題各種復雜問題決策樹√線性回歸√K-means√神經網絡√邏輯回歸√嶺回歸密度聚類深度學習√集成學習√Lasso回歸譜聚類條件隨機場貝葉斯層次聚類隱馬爾可夫模型支持向量機高…

【汽車產品開發項目管理——端到端的汽車產品誕生流程】

MPU&#xff1a;集成運算器、寄存器和控制器的中央處理器芯片 MCU&#xff1a;微控制單元&#xff0c;將中央處理器CPU、存儲器ROM/RAM、計數器、IO接口及多種外設模塊集成在單一芯片上的微型計算機系統。 汽車產品開發項目屬性&#xff1a;臨時性、獨特性、漸進明細性、以目標…