曲線繪制實戰(zhàn):TeeChart與HightSpeedChart選型與性能優(yōu)化)
簡介面向 MFC 界面開發(fā)者的動態(tài)繪圖示例工程重點演示 TeeChart 與 CChartCtrl 兩種常用繪圖控件在對話框程序中繪制實時曲線的方法適合需要快速搭建數(shù)據(jù)監(jiān)控或波形顯示界面的中初級 MFC 開發(fā)者學(xué)習(xí)、改造。壓縮包共 88 個文件以 38 個頭文件、27 個 C 源文件為主還包含內(nèi)聯(lián)實現(xiàn)、Visual Studio 工程配置、說明文檔和資源文件整體約 343KB結(jié)構(gòu)緊湊可直接導(dǎo)入工程參考。目前已有 5052 人學(xué)習(xí)下載。工程內(nèi)含完整的 TeeChart 封裝示例與 CChartCtrl 全套 Chart 系列源碼覆蓋控件初始化、坐標(biāo)軸與圖例設(shè)置、數(shù)據(jù)更新、曲線動態(tài)刷新等關(guān)鍵環(huán)節(jié)同時附帶多篇繪圖控件文章鏈接可延伸了解 Hight-Speed Charting 等更多高頻繪圖方案對需要掌握 MFC 動態(tài)繪圖思路的開發(fā)者很有參考價值。 在MFC下做動態(tài)曲線繪制說句實在話很多老項目到了這一步都會糾結(jié)一陣子。不是沒得選而是可選方案太多——TeeChart功能全但體積重HightSpeedChart輕快但收斂兩個湊在一起正好覆蓋了從“儀表盤大雜燴”到“專業(yè)高速數(shù)據(jù)監(jiān)視器”兩頭的需求。這篇文章就把我實際用這兩款控件做動態(tài)曲線的過程掰開揉碎講清楚包括控件集成、數(shù)據(jù)推送、坐標(biāo)軸滾動、防閃爍、性能優(yōu)化以及那些代碼文檔里不會告訴你的坑。適合正在用MFC做數(shù)據(jù)采集、設(shè)備監(jiān)控、波形顯示或者被“曲線一多就卡死”折磨的朋友參考。1. 為什么偏偏要在MFC下做動態(tài)曲線1.1 這類需求最常見的業(yè)務(wù)場景我見過最多的場景是工控領(lǐng)域的數(shù)據(jù)采集和設(shè)備監(jiān)測串口或網(wǎng)口不斷上報電壓、溫度、轉(zhuǎn)速、振動信號界面需要把這些實時數(shù)據(jù)畫成滾動曲線同時用戶還得能回看歷史、縮放局部。另一個常見場景是實驗室里的波形顯示采樣率動不動就是每秒幾千上萬點曲線不能掉幀界面還不能閃爍。這兩類需求放在一起就是“動態(tài)繪制”最真實的寫照。動態(tài)和靜態(tài)的最大區(qū)別在于數(shù)據(jù)是持續(xù)追加的圖形每秒鐘都要刷新并且圖表窗口要么跟著最新數(shù)據(jù)自動滾動要么在固定窗口內(nèi)顯示最新一段。這意味著繪圖控件不能簡單調(diào)用一次Invalidate就完事必須考慮數(shù)據(jù)緩存、增量追加、范圍重算和渲染效率。MFC 至今還在被大量工業(yè)項目使用原因很好理解老代碼積累多、穩(wěn)定性要求高、團(tuán)隊熟悉度高。在這種環(huán)境下引入第三方繪圖控件首先就要考慮它能不能和 CView、CDialog、CFormView 這些經(jīng)典架構(gòu)融合。TeeChart 和 HightSpeedChart 走的恰好是兩條不同的融合路線正好適合做對比選型。1.2 兩個控件的定位差異TeeChart 是 Steema 公司出品的商業(yè)圖表控件對應(yīng) MFC 的版本主要通過 ActiveX 或原生類庫方式集成。它最大的優(yōu)勢是圖表類型豐富折線、面積、柱狀、餅圖、極坐標(biāo)、甘特圖基本都齊了還有坐標(biāo)軸、圖例、標(biāo)注、多系列、3D 效果這些完整配套。你可以把它理解成“圖表界的大超市”想要什么基本都能找到。HightSpeedChart 指的通常是 CodeProject 上那款開源的 High-Speed Charting 控件作者是 Timothy Weber以一個 CChartCtrl 類為核心整套源碼可以直接編進(jìn) MFC 工程里。它沒有那么多花哨的圖表類型專注做高頻數(shù)據(jù)曲線內(nèi)部做了數(shù)據(jù)減采樣和局部重繪優(yōu)化特別適合每秒幾千上萬個點的實時數(shù)據(jù)流。它的定位更像“專攻一件事的賽車”跑得快但別指望它給你畫儀表盤。兩者不沖突。我個人的選型經(jīng)驗是如果項目里除了實時曲線還要做報表統(tǒng)計、歷史趨勢分析直接上 TeeChart省得自己拼好幾個控件如果項目目標(biāo)很單純就是高性能實時監(jiān)視HightSpeedChart 體積小、上手快、還免費性價比極高。2. TeeChart的MFC集成配置、FastLine與滾動窗口2.1 工程里引入TeeChart的幾種方式TeeChart 在 MFC 里的集成大體分兩條路。一條是 ActiveX 控件方式在對話框資源編輯器里把 TeeChart 組件拖到窗體上它會自動生成一個 CWnd 派生包裝類通常是 CTChart。這種方式最直觀界面設(shè)計階段就能看到圖表效果適合交互復(fù)雜、需要多次試調(diào)布局的場景。另一條是原生類庫方式TeeChart 也提供 C 類接口通過頭文件和靜態(tài)庫直接鏈接進(jìn)工程運(yùn)行時不需要 ActiveX 注冊。這條路線隱藏了 COM 的復(fù)雜度部署時少了一堆注冊環(huán)節(jié)對講究裝機(jī)即用的工業(yè)軟件更友好。我實際項目里大多采用 ActiveX 動態(tài)創(chuàng)建的方式。原因有兩個一是開發(fā)調(diào)試時可以借助 ActiveX 自帶的右鍵“Edit Chart”快速調(diào)整樣式和坐標(biāo)軸改完代碼立刻能驗證二是多語言資源切換時ActiveX 的屬性接口封裝得更完整。關(guān)鍵代碼如下// 在對話框頭文件中聲明包裝對象 CTChart m_wndChart; // OnInitDialog 中創(chuàng)建控件 CRect rcChart(10, 10, 800, 400); BOOL bOK m_wndChart.CreateControl( _T(TeeChart.TChart.9), WS_CHILD | WS_VISIBLE | WS_BORDER, rcChart, this, IDC_TEE_CHART); if (!bOK) { AfxMessageBox(_T(TeeChart 控件創(chuàng)建失敗請檢查是否已安裝授權(quán)。)); return FALSE; }注意上面用的 ProgID 里的版本號要跟你裝的一致裝的是 8 就寫成TeeChart.TChart.8裝的是 9 就寫成.9版本對不上會直接創(chuàng)建失敗。2.2 動態(tài)數(shù)據(jù)推送的核心調(diào)用控件創(chuàng)建出來之后第一步是準(zhǔn)備一個曲線系列。實時曲線建議用 FastLine它是 TeeChart 專門為高速折線提供的系列類型底層對大量數(shù)據(jù)點的渲染做了特殊優(yōu)化比普通 Line 快得多。// 獲取第一個系列并設(shè)置為 FastLine 類型也可以用 AddSeries 新增 CSeries series m_wndChart.Series(0); series.SetSeriesType(cdoFastLine); series.SetColor(RGB(0, 128, 255)); // 清空舊數(shù)據(jù)準(zhǔn)備新一輪動態(tài)推送 series.Clear();之后每次采集到新數(shù)據(jù)就執(zhí)行一次追加double dX GetTickCount64() / 1000.0; // 以秒為單位作為 X 軸時間 double dY CurrentValue; // 來自傳感器或采集卡的實際數(shù)值 CSeries series m_wndChart.Series(0); series.AddXY(dX, dY, _T(), clTeeColor);這一句AddXY是整個動態(tài)繪制的核心。要注意 ActiveX 接口在每次調(diào)用時都要重新獲取Series(0)不要把它長期緩存成一個變量尤其是當(dāng)圖表序列在運(yùn)行中被動態(tài)增刪時緩存的引用可能失效輕則數(shù)據(jù)畫不上重則直接拋異常。2.3 關(guān)鍵參數(shù)滾動、壓縮與坐標(biāo)軸只往里加數(shù)據(jù)還不夠還要讓坐標(biāo)軸動起來。實時監(jiān)視曲線最常見的需求是X 軸跟隨當(dāng)前時間滾動Y 軸根據(jù)數(shù)據(jù)范圍自動縮放。TeeChart 的坐標(biāo)軸對象提供了一組專門用于滾動和自動范圍的方法。// 獲取底部 X 軸和左側(cè) Y 軸 CAxis bottomAxis m_wndChart.GetAxis().GetBottom(); CAxis leftAxis m_wndChart.GetAxis().GetLeft(); // 開啟自動縮放確保 Y 軸隨數(shù)據(jù)上下浮動 leftAxis.SetAutomatic(TRUE); // 設(shè)置滾動模式只顯示最近 10 秒的數(shù)據(jù)窗口 bottomAxis.SetAutomatic(FALSE); bottomAxis.SetMinimum(CurrentTime - 10); bottomAxis.SetMaximum(CurrentTime);這段代碼解決了兩個常見問題一是 Y 軸不做自動縮放的話數(shù)據(jù)跑出可見范圍后就只能看到一條頂天立地的直線二是 X 軸如果不固定滾動窗口曲線只在靠近右邊一小塊區(qū)域顯示沒有滾動效果。把SetMinimum和SetMaximum放到一個OnTimer或者采集線程里每次更新就能看到曲線像示波器一樣平滑右移。數(shù)據(jù)量一旦很大FastLine 自身會觸發(fā)壓縮模式。它不會把每一個點都原樣畫到屏幕上而是根據(jù)屏幕像素寬度計算每個像素列上需要保留的關(guān)鍵極值點其他點直接丟棄。這種“像素級降采樣”是 TeeChart 能在數(shù)據(jù)量很大時依然保持流暢的根本原因。你可以通過GetAspect().SetView3D(FALSE)把 3D 效果關(guān)掉進(jìn)一步降低繪圖開銷。3. HightSpeedChart實操從源碼到高性能曲線3.1 控件源碼的集成步驟High-Speed Charting 用起來比 TeeChart 簡單得多因為整個控件就是一組源代碼文件一般是 ChartCtrl.h、ChartCtrl.cpp 以及配套的 AxisCtrl 等文件。把這些文件拷到工程目錄下添加到 MFC 工程里編譯就能用不需要注冊任何 COM 組件。集成時有個細(xì)節(jié)容易踩坑這個控件最初是針對純 MFC 窗口設(shè)計的如果你的工程用了 Unicode 字符集而下載的版本里字符串處理沒做寬窄字符適配編譯時會報一堆C2664類型轉(zhuǎn)換錯誤。解決方法是全局搜索代碼里的TCHAR、CString和_T()宏把硬編碼的char*字符串和strcpy全部替換為_tcscpy之類的安全版本。我的做法是直接在工程里關(guān)閉_UNICODE或者統(tǒng)一轉(zhuǎn)成寬字符哪個省事用哪個。控件創(chuàng)建方面有兩種常見方式。一是在資源編輯器里放一個 Static Text 控件當(dāng)占位符運(yùn)行時把圖表控件父子化到那個位置// 或者直接動態(tài)創(chuàng)建 CRect rcChart(10, 10, 800, 400); m_wndChart.Create(_T(STATIC), _T(), WS_CHILD | WS_VISIBLE | WS_BORDER, rcChart, this, IDC_CHART);靜態(tài)文本占位的方式更靈活因為 Design Time 階段可以隨意拖動位置和大小運(yùn)行后只需一句SubclassDlgItem就把控件“貼”上去了。3.2 數(shù)據(jù)添加與顯示范圍控制HightSpeedChart 的數(shù)據(jù)接口和 TeeChart 風(fēng)格差異很大。它以“添加一個點”或“設(shè)置整段數(shù)據(jù)”為基本操作并且特別強(qiáng)調(diào)通過設(shè)置顯示范圍來限制繪圖區(qū)域而不是把所有數(shù)據(jù)都丟給系統(tǒng)讓它自己決定。// 先設(shè)置顯示范圍X 軸 0~100Y 軸 0~100 m_wndChart.SetDisplayRange(0, 100, 0, 100); // 向圖表中添加數(shù)據(jù)點 for (int i 0; i nCount; i) { m_wndChart.AddData(dX[i], dY[i]); }AddData內(nèi)部不是直接把所有點寫入屏幕而是在繪制時通過可見范圍做裁剪只畫落在當(dāng)前矩形區(qū)域內(nèi)的數(shù)據(jù)點。這就是它高性能的真正秘密數(shù)據(jù)再多每次重繪也只處理當(dāng)前窗口里能看到的那部分。動態(tài)實時場景下配合SetDisplayRange向右平移窗口double dNewMax CurrentTime; double dNewMin dNewMax - 10; // 同樣的 10 秒窗口 m_wndChart.SetDisplayRange(dNewMin, dNewMax, yMin, yMax);這套組合拳打下來效果和 TeeChart 的滾動窗口幾乎一樣但底層計算量小得多。我在一臺老舊雙核工控機(jī)上實測過每秒追加 2000 個點CPU 占用依然能穩(wěn)定在個位數(shù)百分比。3.3 自適應(yīng)縮放與交互體驗HightSpeedChart 雖然沒有 TeeChart 那種“雙擊彈出編輯器”的雜技但基礎(chǔ)交互還是齊全的。它支持鼠標(biāo)框選放大、右鍵拖拽平移、雙緩沖重繪尤其可貴的是它在縮放時依然只渲染可見點所以你放大到局部之后曲線精度不會因為縮減采樣而丟失。交互相關(guān)的核心邏輯集中在鼠標(biāo)消息的處理里。如果不想要默認(rèn)交互可以在預(yù)處理消息的地方攔掉 WM_LBUTTONDOWN 和 WM_MOUSEMOVE。如果希望保留縮放功能但不想用戶縮到完全沒法看可以在鼠標(biāo)釋放后強(qiáng)制做一次范圍校驗void CMyView::OnLButtonUp(UINT nFlags, CPoint point) { // 縮放過頭的保護(hù)確保 X 軸范圍不小于 0.1 秒 double dLeft, dRight, dTop, dBottom; m_wndChart.GetDisplayRange(dLeft, dRight, dTop, dBottom); if (dRight - dLeft 0.1) { double dCenter (dLeft dRight) / 2.0; m_wndChart.SetDisplayRange(dCenter - 0.05, dCenter 0.05, dTop, dBottom); } CView::OnLButtonUp(nFlags, point); }這個細(xì)節(jié)看起來不起眼但操作體驗差別很大。沒有范圍保護(hù)的話用戶在高速數(shù)據(jù)流上多框選幾次就會把坐標(biāo)軸縮到小數(shù)點后八位圖像變成一堆雜亂無章的色塊極其勸退。4. 動態(tài)繪制核心環(huán)節(jié)采樣、線程與防閃爍4.1 為什么數(shù)據(jù)一多就卡先說結(jié)論卡頓的根源絕大多數(shù)不是繪圖本身而是無效重繪和被無限放大的數(shù)據(jù)點。GDI 把一條折線渲染出來的開銷和點的數(shù)量成正比。如果你每次刷新都把歷史所有點重新畫一遍數(shù)據(jù)量翻一倍耗時就翻一倍。假如歷史數(shù)據(jù)累計了 20 萬個點即便每幀只畫 1000 個可見點如果代碼里沒做范圍裁剪實際還是要遍歷全部 20 萬個點去判斷哪些落在可見區(qū)域性能就這么白白消耗掉了。TeeChart 和 HightSpeedChart 解決這個問題的思路一致壓縮或裁剪。TeeChart 的 FastLine 在點數(shù)超過像素寬度時會啟用在每個像素列抽取最大最小值的策略HightSpeedChart 則通過顯示范圍裁剪配合內(nèi)置的下采樣機(jī)制確保重繪點數(shù)不超過可見區(qū)域容量。動態(tài)繪制場景里還有一個容易被忽略的點Invalidate的觸發(fā)頻率不要無腦調(diào)到最高每秒鐘刷新 30 次以上人眼基本分辨不出來還白白占掉 CPU。我一般把刷新率限制在每秒 20 到 25 次。4.2 定時器與后臺線程協(xié)同MFC 里最直接的動態(tài)繪制驅(qū)動方式是SetTimer在OnTimer里讀取緩沖區(qū)數(shù)據(jù)并推送給圖表控件。這個方式簡單可靠適合數(shù)據(jù)采集頻率不高、UI 操作不復(fù)雜的場景。如果數(shù)據(jù)采集來自單獨的采集線程問題就來了。Windows 窗口消息必須在 UI 線程處理直接跨線程調(diào)用AddData或者Invalidate是危險的輕則偶發(fā)崩潰重則界面假死。我踩過這個坑幾次之后總結(jié)出一套穩(wěn)定的做法采集線程只負(fù)責(zé)寫入數(shù)據(jù)到帶鎖的環(huán)形緩沖區(qū)然后通過PostMessage通知 UI 線程“有新數(shù)據(jù)了”UI 線程在消息響應(yīng)里把緩沖區(qū)數(shù)據(jù)一次性取出并推送給圖表控件。#define WM_UPDATE_CHART (WM_APP 101) // 采集線程 void DataAcquisitionThread() { for (;;) { // 讀取硬件數(shù)據(jù)寫入環(huán)形緩沖區(qū)... ::PostMessage(g_hMainWnd, WM_UPDATE_CHART, 0, 0); } } // UI 線程消息響應(yīng) LRESULT CMainFrame::OnUpdateChart(WPARAM wParam, LPARAM lParam) { // 從緩沖區(qū)批量取出數(shù)據(jù)調(diào)用 AddData / AddXY return 0; }這樣既避免了跨線程訪問窗口對象的風(fēng)險又把界面刷新任務(wù)歸攏到了 UI 線程UI 響應(yīng)速度一直很穩(wěn)。如果想進(jìn)一步降低 UI 開銷可以讓消息響應(yīng)函數(shù)控制刷新頻率比如積攢一定量的數(shù)據(jù)再刷一次而不是每條數(shù)據(jù)都發(fā)一條消息。4.3 雙緩沖與防閃爍處理動態(tài)曲線最容易讓用戶吐槽的就是“閃瞎眼”。閃爍的本質(zhì)是窗口在擦除背景和重繪前景之間產(chǎn)生了明顯的空白時刻。MFC 的CWnd::OnEraseBkgnd默認(rèn)會用窗口背景色填充整個客戶區(qū)如果控件沒有做雙緩沖每一次重繪都會先白屏再畫線肉眼感知就是閃爍。TeeChart 控件自身對閃爍處理得比較完善內(nèi)部具備緩沖機(jī)制一般不需要額外干預(yù)。HightSpeedChart 源碼里也包含了雙緩沖邏輯但如果集成時被工程里的某些設(shè)置干擾比如父窗口設(shè)置了WS_CLIPCHILDREN而子控件沒有正確配合就可能出現(xiàn)閃爍。一個通用的加固方案是重寫繪圖消息強(qiáng)制在內(nèi)存 DC 上繪制完成后整體貼到屏幕void CMyChartView::OnPaint() { CPaintDC dc(this); CDC memDC; memDC.CreateCompatibleDC(dc); CBitmap bmp; bmp.CreateCompatibleBitmap(dc, m_rcClient.Width(), m_rcClient.Height()); CBitmap* pOldBmp memDC.SelectObject(bmp); // 在這里調(diào)用圖表控件的繪制功能 // ... dc.BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOldBmp); }另外還有一個看起來蠢但非常有效的土辦法如果曲線是在一個靜態(tài)背景上滾動就把背景畫到一張持久化的位圖里每次重繪只更新曲線區(qū)域別讓系統(tǒng)去重畫那些完全沒變的網(wǎng)格線。5. 常見問題與排查技巧實錄5.1 數(shù)據(jù)量上來以后界面明顯卡頓先按數(shù)據(jù)數(shù)量級判斷每秒幾百點時卡多半是刷新機(jī)制有問題比如每條數(shù)據(jù)都觸發(fā)一次Invalidate每秒幾千點以上時卡就要檢查有沒有開壓縮或裁剪。我用 TeeChart 時遇到過一種情況FastLine 已經(jīng)開了但因為我給曲線添加了過多裝飾屬性比如每點顯示標(biāo)記、每點有顏色漸變FastLine 的優(yōu)化就被繞過了繪制性能直線下降。去掉這些“非必要且高開銷”的屬性之后性能立刻恢復(fù)正常。HightSpeedChart 方面最要命的操作是在每幀重繪前調(diào)用RemoveAll再AddData。這個操作會把內(nèi)部數(shù)據(jù)結(jié)構(gòu)整個清空重建點越多越慢。正確做法是持續(xù)追加數(shù)據(jù)只在真正需要清理歷史時才清空。5.2 曲線閃爍嚴(yán)重優(yōu)先確認(rèn)雙緩沖是否真的生效。HightSpeedChart 的源碼里有對應(yīng)的緩沖開關(guān)有的版本是通過宏控制的有的版本是默認(rèn)開啟但會被父窗口OnEraseBkgnd干擾。排查時可以在窗口 OnEraseBkgnd 里直接返回 TRUE禁止擦除背景配合控件的緩沖繪圖能解決大部分閃爍問題。如果是 TeeChart檢查一下是否開啟了硬件加速或者 D2D 渲染模式?jīng)]有的話在數(shù)據(jù)量大的情況下也可能出現(xiàn)局部閃爍。不過老實說TeeChart 大部分閃爍問題都出在創(chuàng)建控件的窗口樣式上——缺少WS_CLIPCHILDREN會導(dǎo)致子窗口和父窗口之間的背景重疊刷新。5.3 多線程更新導(dǎo)致崩潰幾乎所有的崩潰都來自 UI 控件在工作線程中被直接調(diào)用。MFC 控件歸根結(jié)底封裝了 HWND窗口句柄和消息隊列都綁定在創(chuàng)建它的線程上。解決思路很固定工作線程負(fù)責(zé)數(shù)據(jù)UI 線程負(fù)責(zé)控件。我用PostMessage配合自定義消息之后再也沒有出現(xiàn)過這塊的崩潰。還有一個隱蔽的坑程序退出時采集線程還在運(yùn)行定時器還在觸發(fā)此時對話框已經(jīng)銷毀控件句柄失效再調(diào)用控件接口就會訪問已釋放的內(nèi)存。退出前一定要先 KillTimer、通知采集線程停止再銷毀控件。5.4 控件自適應(yīng)窗口大小與高DPI動態(tài)曲線窗口免不了被用戶拖拽改變尺寸。如果控件不支持跟隨父窗口縮放就會出現(xiàn)大面積空白或者曲線被截斷的尷尬畫面。MFC 對話框里的控件跟隨窗口縮放最常規(guī)的做法是在 OnSize 里用 SetWindowPos 實時調(diào)整控件位置和大小。高 DPI 下還要注意坐標(biāo)換算。同樣的數(shù)據(jù)點在 100% 縮放和 150% 縮放下像素位置都不一樣。TeeChart 對 DPI 的處理相對成熟控件內(nèi)部有比較完善的坐標(biāo)換算HightSpeedChart 這類源碼控件往往需要自己在 WM_DPICHANGED 里做額外處理最省事的方案是讓工程整體走系統(tǒng) DPI 縮放不做手動縮放雖然字體和位圖會輕微模糊但坐標(biāo)關(guān)系始終正確。追求高清晰度再考慮逐控件適配這個成本不低要有心理準(zhǔn)備。5.5 常見問題速查表問題現(xiàn)象常見原因處理思路曲線不定時崩潰跨線程調(diào)用控件接口PostMessage 切到 UI 線程再更新數(shù)據(jù)多了就卡沒有啟用壓縮裁剪開啟 FastLine、SetDisplayRange、下采樣畫面閃爍背景擦除 無緩沖重寫 OnEraseBkgnd、開啟雙緩沖滾動不平滑刷新頻率過高限制刷新率到 20~25 FPS縮放后失焦或空白范圍超出數(shù)據(jù)覆蓋增加范圍保護(hù)限制縮放極限字體模糊高 DPI 下系統(tǒng)縮放按需適配 DPI或統(tǒng)一走系統(tǒng)縮放控件創(chuàng)建失敗ProgID 版本號不對檢查 TeeChart 版本并替換版本號6. 選型建議與個人體會如果你現(xiàn)在正在糾結(jié)用哪個我總結(jié)一下最直觀的判斷標(biāo)準(zhǔn)。項目里已經(jīng)有 TeeChart 授權(quán)或者需要大量柱狀圖、面積圖、儀表盤來配合曲線展示直接用它就好功能全面解決后續(xù)擴(kuò)展問題。項目目標(biāo)只有“快”一個字?jǐn)?shù)據(jù)量大、界面單調(diào)、預(yù)算有限HightSpeedChart 是最合適的選擇。我個人在多數(shù)中等復(fù)雜度項目里會把兩者搭配使用。TeeChart 做主窗體的綜合趨勢分析和歷史報表展示HightSpeedChart 做實時數(shù)據(jù)監(jiān)視子窗口。兩個控件協(xié)作得很自然各取所長。最后再補(bǔ)一個小技巧動態(tài)曲線的開發(fā)調(diào)試階段別拿真實設(shè)備數(shù)據(jù)來測一定要先用程序模擬一段正弦波、方波或噪聲信號作為數(shù)據(jù)源。一方面能穩(wěn)定復(fù)現(xiàn)問題另一方面可以對照標(biāo)準(zhǔn)波形確認(rèn)坐標(biāo)軸縮放和滾動邏輯是否正確。等基礎(chǔ)功能跑通了再接真實數(shù)據(jù)這樣排錯效率高得多。寫好一套通用的數(shù)據(jù)接入接口模擬數(shù)據(jù)和真實數(shù)據(jù)切換也就是一個條件編譯的事這個前期投入非常值。本文還有配套的精品資源點擊獲取