原文地址:https://www.codeproject.com/articles/14075/high-speed-charting-control
本文翻譯在CodeProject上的介紹(主要還是谷歌翻譯,看不太明白的地方,請(qǐng)對(duì)比原文蘸泻,敬請(qǐng)?jiān)彛┝鹞奖阕约汉秃竺嫒说膶W(xué)習(xí)(花費(fèi)了兩天時(shí)間嘲玫,希望是值得的)悦施。推薦一個(gè)前輩寫的東西:TeeChart替代品,MFC下好用的高速繪圖控件-(Hight-Speed Charting)去团,自己也轉(zhuǎn)載了這篇文章抡诞,在轉(zhuǎn)載的文章中根據(jù)自己的實(shí)驗(yàn)修改了一些東西,修改了什么現(xiàn)在也不記得了土陪,文章地址:TeeChart替代品昼汗,MFC下好用的高速繪圖控件-(Hight-Speed Charting) .
下面是我能找到的資料(點(diǎn)擊超鏈接下載)前三個(gè)是原文提供的(要想找到最新的,到原文中找)鬼雀,后三個(gè)是自己在其他的博客中找到的顷窒。
MFC動(dòng)態(tài)繪制曲線圖-HightSpeedChart實(shí)現(xiàn).rar
TeeChart和HightSpeedChart動(dòng)態(tài)繪圖.rar
2018年5月6日補(bǔ)充一點(diǎn)使用過程中遇到的問題:遇到的問題以及解決方案
Upgrading from Version 1.x to Version 2.0
Upgrading from Version 2.x to Version 3.x
1.Debug Assertion Failed! File:afxwin1.inl Line:639
這個(gè)控件是用來展示2D數(shù)據(jù),如下面的圖片源哩。
對(duì)于我之前的一個(gè)項(xiàng)目鞋吉,我需要在圖表控件上顯示連續(xù)的數(shù)據(jù)流。 我決定開發(fā)自己的控件励烦,因?yàn)槲艺也坏饺魏慰梢蕴峁┧桁`活性的自由軟件控件谓着。 其中一個(gè)主要的限制是,控件必須繪制大量的數(shù)據(jù)坛掠,并能夠迅速顯示它(在Pocket PC上)赊锚。 控件能夠通過僅繪制新的數(shù)據(jù)點(diǎn)而不是完整的數(shù)據(jù)序列來做到這一點(diǎn)并且圖表還能夠顯示靜態(tài)數(shù)據(jù)。
這種控件是我長(zhǎng)時(shí)間工作的結(jié)果屉栓,而且費(fèi)盡周折地為了提供足夠的靈活性來供需要它的人使用舷蒲。 對(duì)于使用者反饋我表示由衷的感謝:一個(gè)郵件,留言板中的一一句話或只是對(duì)本文評(píng)級(jí)友多。 當(dāng)我不知道是否還有人使用它時(shí)阿纤,我就沒有必要維護(hù)這個(gè)控件了。
這個(gè)控件是我花費(fèi)很長(zhǎng)時(shí)間的開發(fā)的結(jié)果夷陋,因此我對(duì)代碼的使用放置一些小條件:
該代碼可以以編譯的形式用于任何非商業(yè)和商業(yè)目的欠拾。代碼可以被重新開發(fā),只要它提供作者名字和完整的免責(zé)聲明骗绕。 更改源代碼需要得到作者的同意藐窄。
此代碼不提供任何安全保證。 我不會(huì)對(duì)使用此代碼造成的損失負(fù)責(zé)酬土。 使用它需要自己承擔(dān)風(fēng)險(xiǎn)荆忍。
1
2
3
This code may be used for any non-commercial
and commercial purposes in a compiled form.
The code may be redistributed as long as it remains
unmodified and providing that the author name
and the disclaimer remain intact. The sources
can be modified with the author consent only.
This code is provided without any guarantees.
I cannot be held responsible for the damage or
the loss of time it causes. Use it at your own risks.
1
2
3
4
5
6
7
8
9
10
鑒于開發(fā)這個(gè)控件所付出的努力,下面的要求并不過分: 如果你在在商業(yè)應(yīng)用程序中使用這個(gè)控件,那么請(qǐng)給我發(fā)郵件讓我知道刹枉。
控件的主要特點(diǎn)是:
高速繪圖(軸固定時(shí))叽唱,允許快速繪制數(shù)據(jù)
無限數(shù)量的數(shù)據(jù)序列(內(nèi)存是限制)
每個(gè)數(shù)據(jù)序列的數(shù)據(jù)量不受限制
支持線圖,點(diǎn)圖微宝,平面圖棺亭,柱狀圖,K線圖 和甘特圖系列
最多四個(gè)軸(左蟋软,下镶摘,右和上軸)
標(biāo)準(zhǔn)軸,對(duì)數(shù)軸或日期/時(shí)間軸
自動(dòng)伸縮的坐標(biāo)軸, 翻轉(zhuǎn)的坐標(biāo)軸(相互獨(dú)立)
軸標(biāo)簽
點(diǎn)標(biāo)簽
平滑的曲線
網(wǎng)格
圖例和標(biāo)題
交互性(在控件中發(fā)生特定事件時(shí)的通知)
支持手動(dòng)縮放和鼠標(biāo)平移
支持鼠標(biāo)指針
支持軸上的滾動(dòng)條
高度可定制(顏色岳守,標(biāo)題凄敢,標(biāo)簽,邊緣湿痢,字體等)
支持UNICODE
支持打印和保存到圖像文件
本文通過一系列簡(jiǎn)短的教程來涵蓋控件的大部分功能涝缝。 閱讀本文后,您將能夠快速地在自己的應(yīng)用程序中使用本控件譬重。
我決定從文章中刪除所有的類和函數(shù)的文檔拒逮,因?yàn)樗皇欠浅S押貌⑶椅液茈y維護(hù)。 此外害幅,隨著代碼的增長(zhǎng)消恍,要記錄的類和函數(shù)的列表變得過于廣泛以至于不能將所有內(nèi)容放在文章中。 作為替代以现,我提供了一個(gè)doxygen文檔狠怨,您可以從本文中(文章的開頭)下載:只需下載“Doxygen文檔”zip文件,解壓所有文件邑遏,雙擊“Index.html”文件佣赖,進(jìn)行查看。
此圖表控件允許您在屏幕上繪制一系列數(shù)據(jù)记盒。 此控件可以添加幾個(gè)不同類型數(shù)據(jù)序列并且最多可以使用四個(gè)軸憎蛤。 添加到圖表的數(shù)據(jù)序列與一個(gè)水平軸(底部或頂部)和一個(gè)垂直軸(右側(cè)或左側(cè))相關(guān)聯(lián)。 這兩個(gè)軸控制數(shù)據(jù)序列在圖表上的顯示方式纪吮。
為了能夠在應(yīng)用程序中使用次圖表控件俩檬,您首先需要在自己的工程里添加源代碼zip中包含的文件。
**注意:**控件在內(nèi)部使用動(dòng)態(tài)轉(zhuǎn)型碾盟,因此必須啟用RTTI(RunTime Type Information 運(yùn)行時(shí)自動(dòng)類型識(shí)別的機(jī)制)棚辽,否則可能會(huì)發(fā)生崩潰。 默認(rèn)情況下冰肴,VC6沒有啟用RTTI屈藐,因此要啟用它打開項(xiàng)目設(shè)置 - >“C / C ++”選項(xiàng)卡 - >“C ++語言”類別榔组,并確保“Enable Run-Time Type Information (RTTI) "選項(xiàng)已選中联逻。
在應(yīng)用程序中使用圖表控件有兩種方法:手動(dòng)插入搓扯,或通過資源編輯器插入。
1.#include "ChartCtrl"添加在對(duì)話框(Dialog)類的頭文件中
2.在對(duì)話框類中添加變量CChartCtrl:
//{{AFX_DATA(CChartDemoDlg)//}}AFX_DATACChartCtrl m_ChartCtrl;
1
2
3
4
3.在對(duì)話框類的OnInitDialog方法中添加這個(gè)控件的Create方法包归。
1.向?qū)υ捒蛸Y源添加自定義控件锨推,打開控件的屬性,并為Class屬性指定ChartCtrl箫踩。 為了避免滾動(dòng)條上的閃爍爱态,必須設(shè)置WS_CLIPCHILDREN樣式(0x02000000L)谭贪,如圖所示境钟。
2.#include "ChartCtrl.h"添加在對(duì)話框(Dialog)類的頭文件中
3.在對(duì)話框類中添加變量CChartCtrl:
//{{AFX_DATA(CChartDemoDlg)//}}AFX_DATACChartCtrl m_ChartCtrl;
1
2
3
4
4.在DoDataExchange函數(shù)中添加DDX_Control(不要忘了更改ID號(hào)和控件名字):
voidCChartDemoDlg::DoDataExchange(CDataExchange*pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CChartDemoDlg)// Add this line with the appropriate ID and variable nameDDX_Control(pDX,IDC_CHARTCTRL,m_ChartCtrl);//}}AFX_DATA_MAP}
1
2
3
4
5
6
7
8
幾種類型的數(shù)據(jù)序列可以添加到控制:點(diǎn)序列,線序列俭识,曲面序列慨削,柱狀圖序列,K線圖序列或甘特圖序列套媚。 點(diǎn)的數(shù)據(jù)格式可能因序列而異(例如缚态,K線圖和甘特圖系列使用不同的點(diǎn)格式)。
Series typeDescriptionCreate functionPoint type
Point seriesEach data point is represented by a single point on the screen. The appearance of the point can be customized.CreatePointsSerieSChartXYPoint
Line seriesThe data points are connected through a line. The appearance of this line can be customized and it can also be smoothed.CreateLineSerieSChartXYPoint
Surface seriesThe data points are connected through a line and the area under this line is filled with a specific brush. The series can also be displayed vertically.CreateSurfaceSerieSChartXYPoint
Bar seriesEach data point is plotted as a vertical bar of a certain width. Multiple bar series can be stacked next to each other without overlapping. The bars can also be plotted horizontally.CreateBarSerieSChartXYPoint
Candlestick seriesEach data point is made of five attributes: the low value, the high value, the open value, the close value and the X value (time). Each point is drawn as a candlestick. This series is used for plotting financial data.CreateCandlestickSerieSChartCandlestickPoint
Gantt seriesEach data point is made of three attributes: the start and end time and a Y value. Each point is drawn as a horizontal bar starting at the start time and finishing at the end time. The bar is positioned along the Y axis at its Y value.CreateGanttSerieSChartGanttPoint
一旦你選擇了一種系列堤瘤,你可以通過調(diào)用上表中列出的CChartCtrl類的輔助函數(shù)之一將其添加到圖表中玫芦。 這些函數(shù)接受兩個(gè)可選參數(shù):兩個(gè)布爾值來確定描述該系列是連接到副水平軸(頂軸)或者是連接大副垂直軸(右軸)。 如果未指定參數(shù)本辐,則數(shù)據(jù)系列將附加到主水平軸(底部軸)和主垂直軸(左軸)桥帆。
**警告: **在將任何系列添加到圖表之前,您需要?jiǎng)?chuàng)建該系列所連接的兩個(gè)軸慎皱。 如果不這樣做老虫,將導(dǎo)致控件失效(assert)。 有關(guān)詳細(xì)信息茫多,請(qǐng)參見“操縱軸”一節(jié)祈匙。
一旦將系列添加到圖表后,我們就可以使用數(shù)據(jù)填充該圖表天揖。 有兩種方法:將數(shù)據(jù)放到一個(gè)單元中一起添加夺欲,或者逐點(diǎn)添加。 后者用于有動(dòng)態(tài)數(shù)據(jù)時(shí):每次調(diào)用函數(shù)時(shí)都會(huì)更新圖表今膊。 雖然這個(gè)調(diào)用是快速的(在某些特定條件下)些阅,但是最好盡可能地將數(shù)據(jù)放到一個(gè)單元中。 下面是一個(gè)簡(jiǎn)單代碼示例万细,它在圖表中創(chuàng)建兩個(gè)系列扑眉,并用數(shù)據(jù)填充它們:一個(gè)系列在初始化時(shí)完全填充纸泄,另一個(gè)系列在調(diào)用OnDataReceived函數(shù)(僅存在于此示例的目的)時(shí)填充。?m_pLineSeries腰素,m_pPointsSeries和m_ChartCtrl是CMyClass類的成員變量聘裁。
voidCMyClass::Init(){....// SNIP: Creation of the axes in the chart. This MUST be done before.m_pLineSeries=m_ChartCtrl.CreateLineSerie();m_pPointsSeries=m_ChartCtrl.CreatePointsSerie();doubleYValues[10];for(inti=0;i<10;i++)XValues[i]=YValues[i]=i;m_pLineSerie->SetPoints(XValues,YValues,10);}voidCMyClass::OnDataReceived(doubleX,doubleY){m_pPointsSeries->AddPoint(X,Y);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
所有系列類繼承自同一抽象基類:CChartSerie。該類處理所有系列通用的功能弓千,但對(duì)具體的數(shù)據(jù)點(diǎn)沒有任何處理功能衡便。點(diǎn)的概念在子類CChartSerieBase中引入,它是一個(gè)模板類洋访,模板參數(shù)是要操作為點(diǎn)的數(shù)據(jù)類型镣陕。這很重要,因?yàn)樾蛄锌赡鼙仨毺幚聿煌臄?shù)據(jù)類型:例如點(diǎn)序列操作具有X和Y值的點(diǎn)姻政,但是K線圖系列操縱具有5個(gè)值(打開呆抑,關(guān)閉,高汁展,低和時(shí)間值)的點(diǎn)鹊碍。其他系列繼承自CChartSerieBase并提供他們操作的數(shù)據(jù)類型。?CChartSerieBase類已經(jīng)處理了大多數(shù)數(shù)據(jù)管理食绿,并通過純虛函數(shù)將渲染委托給子類侈咕。每個(gè)系列在創(chuàng)建時(shí)也會(huì)分配一個(gè)Id。此標(biāo)識(shí)可通過CChartSerie :: GetSerieId()檢索器紧,并可用于從圖表中刪除該系列耀销。
該系列的一個(gè)重要特征是控制點(diǎn)的順序:該系列中的所有點(diǎn)將根據(jù)它們的值重新排序。 默認(rèn)情況下铲汪,點(diǎn)是基于它們的X值排序的熊尉,但您可以通過對(duì)它們的Y值排序或不對(duì)它們進(jìn)行排序來改變這種行為(在這種情況下,系列保持將點(diǎn)添加到系列中的順序 )桥状。 對(duì)點(diǎn)進(jìn)行排序會(huì)對(duì)性能產(chǎn)生影響:如果點(diǎn)是有序的帽揪,則控件能夠從完整系列中檢索第一個(gè)和最后一個(gè)可見點(diǎn),并且僅繪制兩個(gè)點(diǎn)之間的點(diǎn)辅斟。 另一方面转晰,你將不能繪制像橢圓形的曲線。 您可以通過調(diào)用CChartSerieBase :: SetSeriesOrdering來更改點(diǎn)的順序士飒。
控件中的不同系列的功能通常是不言自明的查邢。 然而,柱狀圖系列需要一些解釋酵幕。
這個(gè)系列有點(diǎn)特別扰藕,如果其中幾個(gè)在同一個(gè)控件上繪制在一起,他們將互相影響芳撒。 目的是能夠繪制多個(gè)條形圖系列邓深,而不會(huì)重疊:它們是彼此相鄰繪制的未桥。 為此,您需要指定每個(gè)所屬的組(一個(gè)簡(jiǎn)單的整數(shù)標(biāo)識(shí)符)芥备。 同一組的系列彼此相鄰地繪制(或者對(duì)于水平條在彼此的頂部):參見兩個(gè)圖形的示例冬耿。 設(shè)置組ID是通過SetGroupId函數(shù)完成的。
Bar series with the same group IdBar series with different group Id
您還可以通過調(diào)用SetInterSpace靜態(tài)函數(shù)來控制所有柱形圖之間剩余的空間的寬度萌壳。 這將為所有系列設(shè)置以像素為單位的空間(因此亦镶,如果顯示多于兩個(gè)系列,則在任何位置使用相同的空間)袱瓮。 注意缤骨,您可以通過調(diào)用SetBarWidth單獨(dú)設(shè)置柱狀圖系列的寬度。
一旦使用數(shù)據(jù)填充您的系列尺借,您還可以在系列的特定點(diǎn)上添加標(biāo)簽:這個(gè)標(biāo)簽始終附加到特定點(diǎn)绊起。 現(xiàn)在,只提供一種類型的標(biāo)簽褐望,氣泡標(biāo)簽:包含文本的圓角矩形并用線連接到特定點(diǎn)上勒庄。 當(dāng)然串前,如果需要瘫里,您也可以提供自己的自定義標(biāo)簽(參見“擴(kuò)展功能”一節(jié))。
有兩種方式創(chuàng)建文本標(biāo)簽:靜態(tài)創(chuàng)建標(biāo)簽時(shí)荡碾,或動(dòng)態(tài)注冊(cè)一個(gè)對(duì)象谨读,當(dāng)標(biāo)簽請(qǐng)求時(shí),它將提供文本坛吁。 第一種方法是最簡(jiǎn)單的劳殖,但也不太靈活。 下面是一個(gè)代碼片段拨脉,顯示如何做(假設(shè)m_pSeries已經(jīng)創(chuàng)建并填充足夠的數(shù)據(jù)):
voidCMyClass::Init(){// SNIP...m_pSeries->CreateBalloonLabel(5,_T("This is a simple label"));}
1
2
3
4
5
此調(diào)用將創(chuàng)建一個(gè)帶有“This is a simple label”文本的標(biāo)簽哆姻,并將其附加到帶索引為5的點(diǎn)。該函數(shù)返回一個(gè)指向新創(chuàng)建的標(biāo)簽的指針玫膀,以便您可以修改其某些屬性或存儲(chǔ)以供以后使用矛缨。
第二種方法有點(diǎn)復(fù)雜,但提供了更多的靈活性:例如帖旨,您可以以更方便的方式在標(biāo)簽中顯示點(diǎn)屬性(例如X值箕昭,Y值,…)解阅。 為此落竹,您必須創(chuàng)建一個(gè)繼承自CChartLabelProvider <PointType>的類,并在創(chuàng)建標(biāo)簽時(shí)提供此類的實(shí)例货抄。 此類是模板類述召,模板參數(shù)是標(biāo)簽附加到的系列的點(diǎn)類型朱转。 這個(gè)類是一個(gè)簡(jiǎn)單的接口,你必須覆蓋TChartString GetText(CChartSerieBase <PointType> * pSerie积暖,unsigned uPtIndex)方法肋拔。 此函數(shù)應(yīng)返回必須在標(biāo)簽中顯示的文本。 它接收指向標(biāo)簽所附加的系列和點(diǎn)索引的指針呀酸。 這里有一個(gè)這樣的標(biāo)簽提供程序類的例子:
classCCustomLabelProvider:publicCChartLabelProvider<SChartXYPoint>{public:TChartStringGetText(CChartSerieBase<SChartXYPoint>*pSeries,unsigneduPtIndex){TChartStringStream ssText;SChartXYPoint Point=pSeries->GetPoint(uPtIndex);ssText<<_T("X value=")<<Point.X;returnssText.str();}};
1
2
3
4
5
6
7
8
9
10
11
此代碼段顯示如何將其與標(biāo)簽一起使用凉蜂。 注意m_pSeries應(yīng)該是一個(gè)操作SChartXYPoint點(diǎn)(點(diǎn),線性誉,面或者柱系列)的系列窿吩。 如果不是這樣,你的代碼將給出一個(gè)編譯錯(cuò)誤错览。
voidCMyClass::Init(){// SNIP...m_pLabelProvider=newCCustomLabelProvider();m_pSeries->CreateBalloonLabel(5,m_pLabelProvider);}
1
2
3
4
5
6
7
控件不獲取指針的所有權(quán)纫雁,因此,當(dāng)你不再需要時(shí)倾哺,你有責(zé)任刪除它轧邪。 在上面的例子中,它通常會(huì)在CMyClass析構(gòu)函數(shù)中被刪除羞海。 在上面的示例中忌愚,您可以為所有要添加的標(biāo)簽地方重復(fù)使用相同的標(biāo)簽類, 這也帶來另一個(gè)優(yōu)點(diǎn):如果你想在運(yùn)行時(shí)改變標(biāo)簽的格式却邓,你只需要在CustomLabelProvider中添加代碼硕糊。 不需要遍歷所有現(xiàn)有標(biāo)簽并更改其文本。?當(dāng)然腊徙,在這種情況下简十,需要刷新控件,因?yàn)楸仨氈匦吕L制標(biāo)簽撬腾。 還要注意TChartStringStream類的用法螟蝙,TChartStringStream類是由控件提供的別名(類似于TChartString)。 當(dāng)UNICODE被定義時(shí)民傻,它解析為std :: wstringstream胰默,當(dāng)未定義UNICODE時(shí),解析為std :: stringstream饰潜。
軸是圖表的一個(gè)重要特征初坠,因?yàn)樗鼈兛刂撇煌盗性诳刂浦械娘@示方式。 控件中最多可使用四個(gè)軸:底部彭雾,頂部碟刺,左側(cè)和右側(cè)。 控件的每個(gè)系列必須和一個(gè)水平軸和一個(gè)垂直軸相連接薯酝。 在圖表中添加系列時(shí)指定這些軸半沽。 底部和左側(cè)軸是主軸爽柒,頂部和右側(cè)軸是輔助軸(您將在控件的某些功能中遇到此問題)。 現(xiàn)在有三種類型的軸供選擇:標(biāo)準(zhǔn)軸者填,對(duì)數(shù)軸和日期/時(shí)間軸浩村。 您可以在不同位置選用不同類型的軸。
一旦您選擇了在不同位置使用哪些軸占哟,您需要先創(chuàng)建它們心墅,然后才能向控件添加任何數(shù)據(jù)。 為此榨乎,通過指定軸附加在哪個(gè)位置怎燥,簡(jiǎn)單地調(diào)用CreateStandardAxis,CreateLogarithmicAxis或CreateDateTimeAxis蜜暑。 如果已經(jīng)在該位置創(chuàng)建了軸铐姚,則控件將銷毀它并且用新的軸替換它。 這里有一個(gè)簡(jiǎn)單的代碼片段肛捍,顯示如何在底部創(chuàng)建日期/時(shí)間隐绵,在左側(cè)創(chuàng)建一個(gè)標(biāo)準(zhǔn)軸:
voidCMyClass::Init(){CChartStandardAxis*pBottomAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis);CChartLogarithmicAxis*pLeftAxis=m_ChartCtrl.CreateLogarithmicAxis(CChartCtrl::LeftAxis);}
1
2
3
4
5
6
7
一旦創(chuàng)建了這些軸,就可以對(duì)它們?cè)O(shè)置一些屬性拙毫。 大多數(shù)屬性在所有軸類型之間共享(例如自動(dòng)模式依许,最小值和最大值,軸標(biāo)簽恬偷,…)悍手。 軸可以設(shè)置為三種“自動(dòng)”模式:全自動(dòng),屏幕自動(dòng)和手動(dòng)模式袍患。
全自動(dòng)模式基于附加到該軸的所有系列計(jì)算軸最小值和最大值(所有系列的所有點(diǎn)的最小值用作軸的最小值,并使用所有系列的所有點(diǎn)的最大值 作為軸的最大值)竣付。
屏幕自動(dòng)模式基于與該軸相關(guān)的所有系列的所有可見點(diǎn)計(jì)算軸最小值和最大值诡延。 例如,如果圖表僅顯示連接到手動(dòng)底部軸和屏幕自動(dòng)左側(cè)軸的一個(gè)系列古胆,則左側(cè)軸將自適應(yīng)于當(dāng)前可見的點(diǎn)肆良,并且不考慮這些點(diǎn)有可能超過底軸的范圍(在全自動(dòng)模式下,底軸外部的點(diǎn)將被考慮)逸绎。 **警告:**如果系列的兩個(gè)軸都處于屏幕自動(dòng)模式惹恃,則結(jié)果未定義。
在手動(dòng)模式下棺牧,軸最小和最大值由用戶設(shè)置巫糙,不由控件計(jì)算抄伍。
在使用自動(dòng)軸模式下棕叫,如果將數(shù)據(jù)動(dòng)態(tài)添加到控件,如果新的數(shù)據(jù)點(diǎn)位于軸的范圍之外,那么控件將自動(dòng)刷新荚斯。 這里是一個(gè)代碼片段(繼續(xù)前一個(gè)代碼段),顯示一個(gè)全自動(dòng)軸(底部軸)和一個(gè)手動(dòng)軸(左軸羔巢,它是一個(gè)對(duì)數(shù)軸):
voidCMyClass::Init(){// SNIP ...pBottomAxis->SetAutomaticMode(CChartAxis::FullAutomatic);// The call to SetAutomaticMode(CChartAxis::NotAutomatic) is not// really needed because this is the default.pLeftAxis->SetAutomaticMode(CChartAxis::NotAutomatic);pLeftAxis->SetMinMax(0.01,1000);}
1
2
3
4
5
6
7
8
9
軸有一個(gè)模式是離散模式(默認(rèn)禁用)悍抑。此模式指定軸不顯示連續(xù)值,而只顯示離散值开呐,這些值是軸上刻度指定的值烟勋,而軸將不顯示其他的值。嘗試?yán)L制不同于顯示的節(jié)拍值的值是不可能的筐付。讓我們舉一個(gè)例子:假設(shè)你有一個(gè)底部標(biāo)準(zhǔn)軸神妹,間隔為1.0(所以,顯示的蜱是1家妆,2鸵荠,3等等)。嘗試?yán)L制X值為0.5的點(diǎn)將在相同位置顯示該點(diǎn)伤极,就好像它的值為1.0蛹找。事實(shí)上,你可以認(rèn)為兩個(gè)刻度之間的區(qū)域是一個(gè)常量值哨坪。這就是為什么刻度標(biāo)簽顯示在兩個(gè)刻度的中間庸疾,而不是刻度本身。
這里有一個(gè)小代碼片段当编,顯示離散軸對(duì)系列顯示方式的影響届慈。代碼片段下的兩個(gè)圖像顯示啟用離散模式(第一個(gè)圖像)或禁用(第二個(gè)圖像)的結(jié)果。
voidCMyClass::Init(){CChartStandardAxis*pBottomAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis);pBottomAxis->SetMinMax(0,10);CChartStandardAxis*pLeftAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::LeftAxis);pLeftAxis->SetMinMax(0,10);pBottomAxis->SetTickIncrement(false,1.0);pBottomAxis->SetDiscrete(true);CChartLineSerie*pSeries=m_ChartCtrl.CreateLineSerie();doubleXVal[20];doubleYVal[20];for(inti=0;i<20;i++){XVal[i]=YVal[i]=i/2.0;}pSeries->SetPoints(XVal,YVal,20);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Discrete mode enabledDiscrete mode disabled
使用日期/時(shí)間軸有點(diǎn)特別忿偷,下面是如何利用這個(gè)功能的解釋金顿。要了解日期/時(shí)間軸的重要一點(diǎn)是它們?cè)贑OleDateTime對(duì)象內(nèi)部工作。原因很簡(jiǎn)單:COleDateTime中有DATE類型的類鲤桥,DATE類型是一個(gè)雙精度型揍拆。由于圖表中的點(diǎn)表示為雙精度值,因此它非常適合:使用標(biāo)準(zhǔn)點(diǎn)(非日期/時(shí)間)和日期/時(shí)間點(diǎn)之間沒有差異茶凳,這使得后者的使用不太復(fù)雜嫂拴。所有點(diǎn)仍然存儲(chǔ)為雙精度型,無論是否是日期/時(shí)間贮喧。
創(chuàng)建日期/時(shí)間軸后筒狠,可以在控件中填充數(shù)據(jù)。為此目的箱沦,沒有改變:你必須從CChartSerie類調(diào)用void AddPoint(double X辩恼,double Y)或void SetPoints(double * X,double * Y,int Count)运挫。 CChartCtrl類提供了兩個(gè)靜態(tài)函數(shù)状共,讓你從COleDateTime轉(zhuǎn)換為雙精度,反之亦然:
doubleDateToValue(constCOleDateTime&Date)COleDateTimeValueToDate(doubleValue)
1
2
如果您有另一種格式的日期(例如time_t或SYSTEMTIME)谁帕,這不是一個(gè)問題峡继,因?yàn)镃OleDateTime對(duì)象可以從不同的時(shí)間格式構(gòu)造(檢查COleDateTime類的MSDN文檔,以了解從哪種格式可以構(gòu)造它)匈挖。
填充數(shù)據(jù)后碾牌,可以配置軸以顯示所需的內(nèi)容。 與日期/時(shí)間軸相關(guān)的幾個(gè)功能可用:
voidSetDateTimeIncrement(TimeInterval Interval,intMultiplier)voidSetDateTimeFormat(boolbAutomatic,constTChartString&strFormat)voidSetReferenceTick(COleDateTime referenceTick)
1
2
3
第一個(gè)允許您指定軸上顯示的兩個(gè)節(jié)拍之間的間隔儡循。兩個(gè)節(jié)拍之間的間隔將遵守正確的時(shí)間舶吗,這意味著如果指定1個(gè)月的節(jié)拍增量(Interval=CChartAxis::tiMonth and Multiplier=1),則兩個(gè)節(jié)拍之間的間隔將是不規(guī)則的(28,30或31天)择膝。第二個(gè)函數(shù)允許您指定刻度標(biāo)簽的格式誓琼。控件根據(jù)刻度間隔自動(dòng)格式化刻度標(biāo)簽肴捉,但您可以通過調(diào)用此函數(shù)覆蓋它腹侣。檢查MSDN上的COleDateTime :: Format函數(shù)的文檔以獲取更多信息。最后齿穗,SetReferenceTick(COleDateTime referenceTick)函數(shù)允許您為軸指定一個(gè)參考標(biāo)記傲隶。參考標(biāo)記是用作繪制標(biāo)記的參考的日期:在該日期總是存在標(biāo)記。當(dāng)您在SetDateTimeIncrement函數(shù)中指定的multiplier?不是1時(shí)窃页,這很有用跺株。例如,假設(shè)您指定了3個(gè)月的單位增量脖卖,并且您希望在2月(因此乒省,5月,8月胚嘲,…)有一個(gè)單位作儿,那么您可以調(diào)用此函數(shù)將2月1日設(shè)置為參考單位。默認(rèn)設(shè)置為2000年1月1日馋劈。
下面是一個(gè)簡(jiǎn)單的代碼片段,它創(chuàng)建一個(gè)日期/時(shí)間軸晾嘶,并顯示不同函數(shù)的用法:
voidCMyClass::Init(){// Sets the axis min value to January 1st 2006 and the axis// max value to December 31st 2007.COleDateTimeminValue(2006,1,1,0,0,0);COleDateTimemaxValue(2007,12,31,0,0,0);pBottomAxis->SetMinMax(CChartCtrl::DateToValue(minValue),CChartCtrl::DateToValue(maxValue));// Sets the tick increment to 4 months (disable automatic tick increment)pBottomAxis->SetTickIncrement(false,CChartDateTimeAxis::tiMonth,4);// Sets the tick label format for instance "Jan 2006"pBottomAxis->SetTickLabelFormat(false,_T("%b %Y"));}
1
2
3
4
5
6
7
8
9
10
11
12
13
控件的外觀方面可以根據(jù)不同的應(yīng)用場(chǎng)景做出更改妓雾,比如控件的不同部分(圖例,標(biāo)題垒迂,背景械姻,…)都可以修改。 所有與這些對(duì)象的交互是通過CChartCtrl類來實(shí)現(xiàn):一些將根據(jù)需要?jiǎng)?chuàng)建(例如axes或series)机断,一些在創(chuàng)建控件時(shí)創(chuàng)建(legend楷拳,titles绣夺,…)。 一般來說欢揖,你永遠(yuǎn)不會(huì)自己創(chuàng)建這些對(duì)象陶耍,而是將該任務(wù)委派給CChartCtrl類。 唯一的例外是當(dāng)您要使用自定義軸或自定義系列(請(qǐng)參閱“擴(kuò)展功能”部分)她混。 例如烈钞,下面是一個(gè)代碼段,設(shè)置漸變背景坤按,并將圖例放在控件的底部:
voidCMyClass::Init(){// SNIP// Disable the refresh of the controlm_ChartCtrl.EnableRefresh(false);// Set the gradient for the backgroundm_ChartCtrl.SetBackGradient(RGB(255,255,255),RGB(125,125,255),gtVertical);// Dock the legend at the bottomm_ChartCtrl.GetLegend()->DockLegend(CChartLegend::dsDockBottom);// Specifies that the legend entries are horizontally stackedm_ChartCtrl.GetLegend()->SetHorizontalMode(true);// Re-enable the refresh of the controlm_ChartCtrl.EnableRefresh(true);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
**重要:**從版本1.4的控件毯欣,每次調(diào)用控件上的一個(gè)屬性將導(dǎo)致控件的完全刷新(即使像改變一些文本的字體或?qū)ο蟮念伾?為了避免在沒有必要時(shí)刷新控件(例如,當(dāng)您同時(shí)更改多個(gè)屬性時(shí))臭脓,應(yīng)首先禁用刷新酗钞,更改屬性,然后重新啟用刷新来累,如上面的代碼段所示 砚作。
自從1.5版的控件開始支持UNICODE。 所有出現(xiàn)的std :: string對(duì)象已被TChartString對(duì)象替換佃扼,這只是一個(gè)typedef偎巢,如果未啟用UNICODE,則解析為std :: string兼耀,并在啟用UNICODE時(shí)解析為std :: wstring压昼。
有時(shí),應(yīng)用程序需要響應(yīng)用戶鼠標(biāo)操作瘤运。 例如窍霞,如果用戶點(diǎn)擊點(diǎn),則程序可以顯示關(guān)于被點(diǎn)擊的點(diǎn)的信息拯坟,這一節(jié)將解釋如何做到但金。
雖然原理是有點(diǎn)不同,但是無論你想聽在圖表上的一般鼠標(biāo)事件本身(點(diǎn)擊軸郁季,圖例冷溃,…)或你是否對(duì)特定系列的鼠標(biāo)事件感興趣。 這兩種情況都很容易實(shí)現(xiàn)梦裂。
你必須實(shí)現(xiàn)CChartMouseListener接口似枕,覆蓋你感興趣的方法,并通過調(diào)用CChartCtrl :: RegisterMouseListener(CChartMouseListener * pMouseListener)將該類的實(shí)例注冊(cè)到圖表控件年柠。 根據(jù)鼠標(biāo)事件發(fā)生在控件的哪個(gè)部分:標(biāo)題凿歼,圖例,軸或繪圖區(qū),調(diào)用該接口上的不同函數(shù)答憔。 對(duì)于所有這些函數(shù)味赃,總是傳遞兩個(gè)參數(shù):MouseEvent,它是列出鼠標(biāo)事件類型(鼠標(biāo)移動(dòng)虐拓,左鍵單擊心俗,…)的枚舉,以及一個(gè)CPoint對(duì)象侯嘀,它包含的發(fā)生事件的點(diǎn)的屏幕坐標(biāo)另凌。?對(duì)于某些函數(shù),需要時(shí)傳遞一些其他參數(shù)戒幔。 例如吠谢,當(dāng)單擊一個(gè)軸時(shí),指向該軸的指針被傳遞給該函數(shù)诗茎。
下面是CChartMouseListener的實(shí)現(xiàn)工坊,它對(duì)軸的點(diǎn)擊作出反應(yīng),并顯示一個(gè)消息框:
classCCustomMouseListener:publicCChartMouseListener{public:voidOnMouseEventAxis(MouseEvent mouseEvent,CPoint point,CChartAxis*pAxisClicked){if(mouseEvent==CChartMouseListener::LButtonDoubleClick){MessageBox(_T("Axis clicked"),_T("Info"),MB_OK);}}};
1
2
3
4
5
6
7
8
9
10
11
12
然后你必須創(chuàng)建一個(gè)這個(gè)類的實(shí)例并注冊(cè)它:
m_pMouseListener=newCCustomMouseListener();m_ChartCtrl.RegisterMouseListener(m_pMouseListener);
1
2
這里也需要自己刪除指針敢订。
響應(yīng)系列上的事件與響應(yīng)一般事件非常相似王污,只是監(jiān)聽器是CChartSeriesMouseListener的一個(gè)實(shí)例,它是一個(gè)模板類楚午,模板參數(shù)是系列的點(diǎn)類型昭齐。 這是需要的,以避免當(dāng)您要檢索點(diǎn)的特定值時(shí)不必要的轉(zhuǎn)型矾柜。 另一個(gè)區(qū)別是阱驾,您必須在系列本身上注冊(cè)監(jiān)聽器,而不是在圖表控件上注冊(cè)怪蔑。
下面是CChartSeriesMouseListener的實(shí)現(xiàn)里覆,它對(duì)系列的點(diǎn)擊做出反應(yīng),如果點(diǎn)擊發(fā)生在點(diǎn)上缆瓣,它將顯示一個(gè)帶有點(diǎn)的Y值的消息框:
classCCustomMouseListener:publicCChartSeriesMouseListener<SChartXYPoint>{public:voidOnMouseEventSeries(MouseEvent mouseEvent,CPoint point,CChartSerieBase<SChartXYPoint>*pSerie,unsigneduPointIndex){if(mouseEvent==CChartMouseListener::LButtonDoubleClick&&uPointIndex!=INVALID_POINT){TChartStringStream ssText;SChartXYPoint Point=pSeries->GetPoint(uPointIndex);ssText<<_T("Y value=")<<Point.Y;TChartString strText=ssText.str();MessageBox(NULL,strText.c_str(),_T("Info"),MB_OK);}}};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
**注意:**當(dāng)用戶不點(diǎn)擊一個(gè)點(diǎn)時(shí)喧枷,OnMouseEventSeries函數(shù)也可以被調(diào)用。 例如當(dāng)用戶在兩個(gè)點(diǎn)之間但仍然在該系列上點(diǎn)擊時(shí)情況弓坞。 在這種情況下隧甚,為uPointIndex參數(shù)傳遞INVALID_POINT。
然后渡冻,您必須創(chuàng)建此類的實(shí)例并將其注冊(cè)到系列中:
m_pMouseListener=newCCustomMouseListener();m_pSeries.RegisterMouseListener(m_pMouseListener);
1
2
**注意:**只有當(dāng)系列操作SChartXYPoint類型的點(diǎn)(點(diǎn)呻逆,線,面或者柱狀圖系列)時(shí)菩帝,這才會(huì)起作用。 如果不是這樣,您的代碼將生成編譯錯(cuò)誤呼奢。
出于性能原因宜雀,禁止檢測(cè)系列上的鼠標(biāo)移動(dòng)事件。 要啟用它握础,請(qǐng)參閱doxygen文檔中的CChartSerie :: EnableMouseNotifications函數(shù)辐董。
您還可以向控件添加光標(biāo)。 支持兩種類型的光標(biāo):“十字線”光標(biāo)和“拉線”光標(biāo)禀综。 第一個(gè)是在鼠標(biāo)移動(dòng)的繪圖區(qū)域上顯示的簡(jiǎn)單十字简烘,第二個(gè)是與特定軸關(guān)聯(lián)的水平或垂直線,您可以通過單擊它并使用鼠標(biāo)移動(dòng)來拖動(dòng)定枷。 對(duì)于每個(gè)光標(biāo)孤澎,您可以注冊(cè)一個(gè)偵聽器,以便在移動(dòng)光標(biāo)時(shí)通知它欠窒。 這里是一段代碼覆旭,用于創(chuàng)建與底部和左側(cè)軸相關(guān)聯(lián)的“十字準(zhǔn)線”光標(biāo)以及與底部軸相關(guān)聯(lián)的“拉線”光標(biāo):
// Creates a cross-hair cursor associated with the two primary axes.CChartCrossHairCursor*pCrossHair=m_ChartCtrl.CreateCrossHairCursor();// Creates a dragline cursor associated with the bottom axis.CChartDragLineCursor*pDragLine=m_ChartCtrl.CreateDragLineCursor(CChartCtrl::BottomAxis);// Hides the mouse when it is over the plotting area.m_ChartCtrl.ShowMouseCursor(false);
1
2
3
4
5
6
7
8
注意到對(duì)CChartCtrl :: ShowMouseCursor的調(diào)用結(jié)束。 默認(rèn)情況下岖妄,鼠標(biāo)總是可見的型将,但是當(dāng)您使用十字光標(biāo)時(shí),當(dāng)它在繪圖區(qū)域時(shí)隱藏有時(shí)是需要的荐虐。
如果希望在光標(biāo)位置更改時(shí)收到通知七兜,則必須實(shí)現(xiàn)CChartCursorListener接口,創(chuàng)建其實(shí)例并使用光標(biāo)注冊(cè)它:
classCCustomCursorListener:publicCChartCursorListener{public:voidOnCursorMoved(CChartCursor*pCursor,doublexValue,doubleyValue){TChartStringStream ssText;ssText<<_T("Cursor moved: xPos=")<<xValue<<_T(", yPos=")<<yValue;// Do something with the string...}};
1
2
3
4
5
6
7
8
9
10
11
CCustomCursorListener*pCursorListener=newCCustomCursorListener;pDragLine->RegisterListener(pCursorListener);
1
2
OnCursorMoved函數(shù)接收一個(gè)X和Y值福扬,但對(duì)于拖動(dòng)光標(biāo)腕铸,只使用這些值中的一個(gè):如果光標(biāo)與水平軸相關(guān)聯(lián),則使用X值忧换,否則使用Y值恬惯。
在版本1.1的控件中,縮放和平移功能已被添加到控件亚茬。 使用鼠標(biāo)左鍵控制縮放酪耳,用鼠標(biāo)右鍵控制平移。 要縮放圖表的特定部分刹缝,只需左鍵單擊圖表(這將是縮放矩形的左上角)碗暗,然后拖動(dòng)到右下角。 將出現(xiàn)一個(gè)矩形梢夯。 一旦松開鼠標(biāo)按鈕言疗,四個(gè)軸將自動(dòng)調(diào)整到您選擇的區(qū)域。 默認(rèn)情況下啟用縮放颂砸,但您可以通過調(diào)用CChartCtrl :: SetZoomEnabled(bool bEnabled)來禁用縮放噪奄。 您還可以通過調(diào)用CChartAxis :: SetZoomLimit(double dLimit)為每個(gè)軸指定縮放限制死姚。 它指定縮放時(shí)軸的最小范圍。 默認(rèn)值為0.001勤篮。
要平移控件都毒,右鍵單擊控件上的某處并移動(dòng)鼠標(biāo)。 鼠標(biāo)下的點(diǎn)將“跟隨”鼠標(biāo)的移動(dòng)(實(shí)際上碰缔,軸的最小和最大值將改變)账劲。 默認(rèn)情況下啟用平移,但您可以通過調(diào)用CChartCtrl :: SetPanEnabled(bool bEnabled)來禁用它金抡。
如果您左鍵單擊圖表(例如開始縮放)瀑焦,但如果您移動(dòng)到左上角,所有使用縮放和平移功能所做的修改將被取消(控制將處于它的狀態(tài) 在使用平移和縮放操作之前)梗肝。 最后榛瓮,還有一種方法通過調(diào)用CChartAxis :: SetPanZoomEnabled(bool bEnabled)禁用特定軸的平移和縮放功能。
線和點(diǎn)系列允許以高速率繪制數(shù)據(jù)统捶。 這通常在要繪制來自外部設(shè)備(例如榆芦,傳感器)的數(shù)據(jù)時(shí)完成。 這是可能的喘鸟,因?yàn)楫?dāng)您向此類系列添加點(diǎn)時(shí)匆绣,控件不會(huì)完全刷新,只會(huì)繪制最后一個(gè)點(diǎn)(或最后一個(gè)線段)什黑,這是非常有效的崎淳。 但是,如果希望控件能夠足夠快地繪制數(shù)據(jù)愕把,則必須考慮幾點(diǎn)拣凹。
一個(gè)重要的事情是,使用自動(dòng)軸可能會(huì)降低很多性能恨豁。這是因?yàn)槿绻粋€(gè)點(diǎn)繪制在軸范圍之外嚣镜,則軸范圍將被自動(dòng)調(diào)整,這意味著控制將被完全刷新橘蜜。因此菊匿,如果您使用自動(dòng)底部軸線并具有“滾動(dòng)”軌跡,則每個(gè)新點(diǎn)都將位于軸的當(dāng)前范圍之外计福,并且將對(duì)每個(gè)點(diǎn)執(zhí)行控制刷新跌捆。處理的更好的方法是使用固定軸并且每秒手動(dòng)地增加軸的范圍(或以合理的速率)。
另一個(gè)重要的點(diǎn)是象颖,你不應(yīng)該在向一個(gè)系列添加一個(gè)新點(diǎn)之后調(diào)用RefreshCtrl佩厚。這當(dāng)然會(huì)完全地刷新控件,但是應(yīng)該避免這樣做说订。最后抄瓦,如果您需要同時(shí)應(yīng)用幾個(gè)修改或添加幾個(gè)點(diǎn)到控件潮瓶,您應(yīng)該在EnableRefresh(false)和EnableRefresh(true)之間封裝這些調(diào)用(請(qǐng)參閱“自定義外觀”部分)。
在某些特定情況下闺鲸,您需要使用新功能擴(kuò)展控件筋讨,例如新的系列類型。目前摸恍,您可以自定義四個(gè)組件:序列,軸赤屋,點(diǎn)標(biāo)簽和光標(biāo)立镶。
要提供新軸,新標(biāo)簽或新光標(biāo)类早,您只需繼承基類(CChartAxis媚媒,CChartLabel或CChartCursor)并實(shí)現(xiàn)所需的虛擬函數(shù)。一旦完成涩僻,您可以通過調(diào)用不同函數(shù)的自定義版本(CChartCtrl :: AttachCustomAxis缭召,CChartCtrl :: AttachCustomLabel或CChartCtrl :: AttachCustomCursor)附加您的新對(duì)象。?CChartLabel類是一個(gè)模板類逆日。這個(gè)主題有點(diǎn)廣泛嵌巷,進(jìn)入了很多細(xì)節(jié),但最簡(jiǎn)單的方法是看看不同的現(xiàn)有類室抽。
如果你想提供新的系列搪哪,這有點(diǎn)不同:你首先要考慮你想要在你的系列中操縱的點(diǎn)的類型。如果你只需要使用X和Y值來操作點(diǎn)坪圾,那么你可以繼承CChartXYSerie晓折,它提供了很多功能來操作這些點(diǎn)。然后兽泄,您必須實(shí)現(xiàn)所需的虛擬函數(shù)漓概。看看下面的系列:CChartLineSerie病梢,CChartPointSerie胃珍,CChartSurfaceSerie和CChartBarSerie具體示例。
如果你的系列操縱其他類型的點(diǎn)飘千,那么你首先必須為點(diǎn)包含以下方法創(chuàng)建一個(gè)結(jié)構(gòu):double GetX()堂鲜,double GetXMin(),double GetXMax()护奈,double GetY()缔莲,double GetYMin()和double GetYMax()。一旦完成霉旗,您必須繼承CChartSerieBase并將此點(diǎn)作為模板參數(shù)痴奏。然后蛀骇,您必須提供所需的虛擬功能《敛穑看看下面的系列具體例子:CChartCandlestickSerie和CChartGanttSerie擅憔。
Upgrading from Version 1.x to Version 2.0
在版本2.0中,對(duì)控件進(jìn)行重構(gòu)檐晕,導(dǎo)致API的更改暑诸。 主要的可見變化是每個(gè)軸類型現(xiàn)在有其單獨(dú)的類(CChartStandardAxis,CChartDateTimeAxis和CChartLogarithmicAxis)辟灰。 這也意味著默認(rèn)情況下沒有創(chuàng)建軸个榕,并且您必須在向圖表添加系列之前自己創(chuàng)建軸(否則代碼將斷言)。 這包括在“操縱軸”部分芥喇。
另一個(gè)變化是添加系列到圖表的方式:AddSerie已經(jīng)在CChartCtrl類中刪除西采,并已被幫助函數(shù)替代,以創(chuàng)建特定的系列類型(CreateLineSerie继控,CreatePointsSerie械馆,…)。 這些函數(shù)返回確切的系列類型武通,因此不再需要鑄造霹崎。 這在“操縱系列”一節(jié)中有詳細(xì)描述。
Upgrading from Version 2.x to Version 3.x
版本3.0.0的主要變化是厅须,系列基類現(xiàn)在已經(jīng)作為模板類仿畸,模板參數(shù)是系列操作的點(diǎn)類型。如果您沒有通過提供新的系列類型擴(kuò)展控件朗和,這將不會(huì)在您的代碼中有所不同错沽。如果你提供了一個(gè)新的系列類型,你的類必須繼承CCharSerieBase并提供它操作的點(diǎn)的類型眶拉。如果你的系列使用只有X和Y值的點(diǎn)千埃,你可以簡(jiǎn)單地繼承CChartXYSerie∫渲玻看看現(xiàn)有的系列更多的例子放可。
另一個(gè)小的修改是標(biāo)簽提供程序現(xiàn)在也是模板類(出于同樣的原因)。并且監(jiān)聽系列中的鼠標(biāo)事件現(xiàn)在從圖表上的鼠標(biāo)事件中分離出來朝刊。這兩點(diǎn)在“在點(diǎn)上添加標(biāo)簽”部分和“鼠標(biāo)事件通知”部分中有很好的解釋耀里。
最后,CChartAxis :: SetAutomatic方法已被標(biāo)記為已棄用拾氓,您應(yīng)該使用CChartAxis :: SetAutomaticMode(已經(jīng)引入了一個(gè)額外的自動(dòng)模式)冯挎。
本節(jié)只是兩個(gè)代碼片段,顯示了如何使用控件咙鞍。 第一個(gè)片段再現(xiàn)了示波器示例的圖像(參見本文頂部)房官,第二個(gè)示例再現(xiàn)了“2008年收入”圖像趾徽。 代碼是文檔化的,所以它不應(yīng)該太難理解翰守。
Oscilloscope example:
// Disable the refresh of the control (avoid multiple refresh).m_ChartCtrl.EnableRefresh(false);// Create a bottom and left axesCChartStandardAxis*pBottomAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis);CChartStandardAxis*pLeftAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::LeftAxis);// Sets the min and max values of the bottom and left axis to -15 -> 15pBottomAxis->SetMinMax(-15,15);pLeftAxis->SetMinMax(-15,15);// Add a new series of type line to the control and add data to itCChartLineSerie*pLineSeries=m_ChartCtrl.CreateLineSerie();// Specifies that the points in the series are not ordered (needed to be able// to draw an ellipse).pLineSeries->SetSeriesOrdering(poNoOrdering);for(inti=0;i<361;i++){doubleX=10*sin(i/360.0*2*3.141592);doubleY=10*cos((i-60)/360.0*2*3.141592);pLineSeries->AddPoint(X,Y);}// Defines the different colors (back color, axes color, ...)COLORREF BackColor=RGB(0,50,0);COLORREF GridColor=RGB(0,180,0);COLORREF TextColor=RGB(0,180,0);COLORREF SerieColor=RGB(0,255,0);// Specifies a sunken border for the controlm_ChartCtrl.SetEdgeType(EDGE_SUNKEN);// Sets the color of the border and the back colorm_ChartCtrl.SetBorderColor(TextColor);m_ChartCtrl.SetBackColor(BackColor);//Sets the color of the different elements of the bottom axism_ChartCtrl.GetBottomAxis()->SetAxisColor(TextColor);m_ChartCtrl.GetBottomAxis()->SetTextColor(TextColor);m_ChartCtrl.GetBottomAxis()->GetGrid()->SetColor(GridColor);// Sets the color of the different elements of the left axism_ChartCtrl.GetLeftAxis()->SetAxisColor(TextColor);m_ChartCtrl.GetLeftAxis()->SetTextColor(TextColor);m_ChartCtrl.GetLeftAxis()->GetGrid()->SetColor(GridColor);// Sets the color of the title, change the font to Times New Roman// and add a stringm_ChartCtrl.GetTitle()->SetColor(TextColor);m_ChartCtrl.GetTitle()->SetFont(140,_T("Times New Roman"));m_ChartCtrl.GetTitle()->AddString(_T("An example of oscilloscope"));// Change the color of the line seriespLineSeries->SetColor(SerieColor);// Finally re-enable the refresh of the control. This will refresh the// control if any refresh was still 'pending'.m_ChartCtrl.EnableRefresh(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
“Income over 2008” example:
srand((unsignedint)time(NULL));// Disable the refreshm_ChartCtrl.EnableRefresh(false);COleDateTimeMin(2008,1,1,0,0,0);COleDateTimeMax(2008,10,1,0,0,0);// Create the bottom axis and configure it properlyCChartDateTimeAxis*pBottomAxis=m_ChartCtrl.CreateDateTimeAxis(CChartCtrl::BottomAxis);pBottomAxis->SetMinMax(Min,Max);pBottomAxis->SetDiscrete(true);pBottomAxis->SetTickIncrement(false,CChartDateTimeAxis::tiMonth,1);pBottomAxis->SetTickLabelFormat(false,_T("%b"));// Create the left axis and configure it properlyCChartStandardAxis*pLeftAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::LeftAxis);pLeftAxis->SetMinMax(0,100);pLeftAxis->GetLabel()->SetText(_T("Units sold"));// Create the right axis and configure it properlyCChartStandardAxis*pRightAxis=m_ChartCtrl.CreateStandardAxis(CChartCtrl::RightAxis);pRightAxis->SetVisible(true);pRightAxis->GetLabel()->SetText(_T("Income (kEuros)"));pRightAxis->SetMinMax(0,200);// Configure the legendm_ChartCtrl.GetLegend()->SetVisible(true);m_ChartCtrl.GetLegend()->SetHorizontalMode(true);m_ChartCtrl.GetLegend()->UndockLegend(80,50);// Add text to the title and set the font & colorm_ChartCtrl.GetTitle()->AddString(_T("Income over 2008"));CChartFont titleFont;titleFont.SetFont(_T("Arial Black"),120,true,false,true);m_ChartCtrl.GetTitle()->SetFont(titleFont);m_ChartCtrl.GetTitle()->SetColor(RGB(0,0,128));// Sets a gradient backgroundm_ChartCtrl.SetBackGradient(RGB(255,255,255),RGB(150,150,255),gtVertical);// Create two bar series and a line series and populate them with dataCChartBarSerie*pBarSeries1=m_ChartCtrl.CreateBarSerie();CChartBarSerie*pBarSeries2=m_ChartCtrl.CreateBarSerie();CChartLineSerie*pLineSeries=m_ChartCtrl.CreateLineSerie(false,true);intlowIndex=-1;intlowVal=999;for(inti=0;i<9;i++){COleDateTimeTimeVal(2008,i+1,1,0,0,0);intDesktopVal=20+rand()%(100-30);pBarSeries1->AddPoint(TimeVal,DesktopVal);intLaptopVal=10+rand()%(80-20);pBarSeries2->AddPoint(TimeVal,LaptopVal);intIncome=DesktopVal+LaptopVal*1.5;if(Income<lowVal){lowVal=Income;lowIndex=i;}pLineSeries->AddPoint(TimeVal,Income);}// Configure the series properlypBarSeries1->SetColor(RGB(255,0,0));pBarSeries1->SetName(_T("Desktops"));pBarSeries2->SetColor(RGB(68,68,255));pBarSeries2->SetGradient(RGB(200,200,255),gtVerticalDouble);pBarSeries2->SetName(_T("Laptops"));pBarSeries2->SetBorderColor(RGB(0,0,255));pBarSeries2->SetBorderWidth(3);pLineSeries->SetColor(RGB(0,180,0));pLineSeries->SetName(_T("Total income"));pLineSeries->SetWidth(2);pLineSeries->EnableShadow(true);// Add a label on the line series.TChartStringStream labelStream;labelStream<<_T("Min income: ")<<lowVal;CChartBalloonLabel<SChartXYPoint>*pLabel=pLineSeries->CreateBalloonLabel(lowIndex,labelStream.str()+_T(" kEuros"));CChartFont labelFont;labelFont.SetFont(_T("Microsoft Sans Serif"),100,false,true,false);pLabel->SetFont(labelFont);// Re enable the refreshm_ChartCtrl.EnableRefresh(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Quite a lot of work is involved in the development of this control and, as any other software project, it might still contain bugs or errors in the documentation. If you encounter such a problem, please let me know (even if you fixed it yourself) so that I can fix the issue as soon as possible. Other users of the control will thank you for that. The same if you encounter errors in the documentation or typos in the article.
I’m also more or less constantly working on this control to add new features. If you have some requirement for a nice feature that could be useful for others, please let me know and I’ll add it to my wishlist. However, as I’m working on this control in my spare time, my time is rather limited.
Finally, if you liked this control, do not hesitate to drop me a word in the discussion forum or to rate the article, this is much appreciated. Thank you.
08/05/2006: Release of version 1.0
19/08/2006: Release of version 1.1
Bug fix in ScreenToValue function (CChartAxis)
Bug fix in RemoveAllSeries function (CChartCtrl)
Added support for manual zoom
Added support for mouse panning
Ability to specify a tick increment on the axis
Added support for resizing the control
09/04/2007: Release of version 1.2
GDI leak corrected
Invisible series are not taken in account for auto axis and legend (thanks to jerminator-jp)
Ability to change the text color of the axis
Ability to change the color of the border of the drawing area
Surface series added
16/02/2008: Release of version 1.3
Added date/time axis
Bug fix in how the logarithmic labels are displayed (trailing 0)
Ability to change the color of the zoom rectangle
Removed compiler warnings for VC2005
Bug fix in the zoom
14/04/2008: Release of version 1.4
Added support for scrollbars
Bar series added
Legend can be docked on any side or floating
Support for legend in horizontal mode
Support for transparent background on the legend
Support for shadow for several objects
RemovePointsFromBegin, RemovePointsFromEnd and AddPoints in the CChartSeries class
Support for gradient background
EnableRefresh and UndoPanZoom functions added in CChartCtrl
Possibility to enable/disable the zoom for a specific axis and to set its limit
Speed improvement on the series (min and max cached, ordering of the series)
Series can be removed using their pointers
Bug fix for invisible series in the legend
Bug fix for logarithmic axis (1 digit was not displayed)
Bug fix when removing series from the control
Bug fix if the pen width is bigger than 1 for line series
Bug fix for automatic axis
20/08/2008: Release of version 1.5
Added support for UNICODE
Added support for printing
Auto-hide scrollbars
Baseline selection for bar series
Performance patch
Scrollbar flickering removed (see here)
Bug fix: scrollbar is now updated when axis is panned
Bug fix: calling AddPoint was not drawing the new point
Bug fix: tick labels for log axis were not always correct (rounding error)
Bug fix: last point of ChartPointSerie was not displayed
Bug fix: moving the mouse outside the control doesn’t stop the zoom or pan operation (the button can be released outside the control)
13/04/2009: Release of version 2.0
The different axis types are now separated into different classes
Modified the way to add series to the control for improved flexibility
Added cursors
Ability to display discrete axes
Ability to be notified about mouse events occurring on the control
Added labels on points
Ability to display a smooth curve
Added ChartFont: allows for italic, bold or underlined fonts
Added the SetReferenceTick function for date/time axis
Ability to store user data for each point
Series now have an Id
Removed the CChartObject class
Points are now stored in a standard array instead of a std::vector for efficiency
Binary search implemented for finding the first and last visible points (for efficiency)
The line series now uses PolyLine instead of MoveTo/LineTo (efficiency)
Bug fix when using date/time axis with a tick interval in years
Bug fix: bar series were drawn from the wrong axis
11/06/2009: Release of version 2.0.1
Optimization: the pan feature has been smoothed
Optimization: points with the same X and Y values are not plotted anymore for the line series.
Bug fix: in some situations, the code was crashing when accessing points outside the valid range
Bug fix: when series were removed, the legend was accessing removed series (which crashed)
Bug fix: when a series was cleared, new points were not drawn properly
Bug fix: inserting a point for which the X value already existed in the series did not add the point properly
Bug fix with the CChartFont class
07/08/2009: Release of version 2.0.2
Bug fix: the control was crashing when a series with no points and no ordering was added
Bug fix: the shadow of the line was not drawn correctly
Bug fix: when an automatic date/time axis was used without any data, the code crashed
28/12/2009: Release of version 3.0.0
Series are now template classes with the template parameter being the point type. This allows the control to manipulate any type of points
Added candlestick and Gantt series
Added support to save the chart to an image file
Bar series can be stacked
Added a new automatic mode for axes: the screen automatic mode
Listening for mouse events on a series has been moved to a CChartSeriesMouseListener class
Bug fix: when a point X or Y value is modified, the series is reordered
Bug fix: setting a tick increment on a standard axis did not show the digits properly
17/01/2010: Release of version 3.0.1
Bug fix: when using labels with the points series, the border of the points was changing color. Fixed by providing a way to specify the border color.
Bug fix: the code was crashing when clicking on a series without having registered a mouse listener on the series.
Bug fix: detection of mouse events on certain series type was crashing
Bug fix: CChartTitle::SetVisible was not implemented
13/07/2010: Release of version 3.0.2
Bug fix: the high-speed functionality has been removed by mistake
Bug fix: the draw function of the line series was not drawing points
Bug fix: replaced Clear() by clear() in the ClearSerie function.
Bug fix: Added implementation of ctor/dtor for the CChartCursorListener class
Bug fix: memory leak when the series was cleared (labels were not deleted)
I would like to thank all the people from this community, they were a great help when I started programming. Thanks also to all the people who contributed to this control with their various help or feedback: toxcct, Chris Maunder, Kevin Hoffman, jerminator-jp, Laurie Gellatly, Eugene Pustovoyt, Andrej Ritter, Nick Holgate, Nick Schultz, Johann Obermayr, Pierre Schramm and Kevin Winter. A special thanks to Bruno Lavier for the time spent working on the control. I hope I didn’t forget anybody.
Chris Maunder’s Colour Picker Control?(used in the demo application)
Drawing smooth curves with Bezier Primitives
This article, along with any associated source code and files, is licensed under?The Code Project Open License (CPOL)
1.Debug Assertion Failed! File:afxwin1.inl Line:639
這個(gè)BUG是多線程使用過程中遇到的孵奶,ChartControl類不能在多線程中直接使用,直接使用可能出現(xiàn)搶DC的情況出現(xiàn)蜡峰,多謝前輩的指點(diǎn):使用CDC:GetTextExtent出現(xiàn)了有關(guān)問題的奇怪有關(guān)問題,m_hDC 是有效的,但GetTextExtent內(nèi)部調(diào)用GetExtentPoint32有時(shí)失敗
這個(gè)BUG的特殊之處就是你使用斷點(diǎn)調(diào)試使調(diào)不出來的( ……這個(gè)是不是很厲害…… ),所以一定的注意許多函數(shù)并不是線程安全的了袁。
解決辦法:?所有繪圖操作都使用消息響應(yīng)的方式進(jìn)行實(shí)現(xiàn),定義消息事示,并在繪圖界面對(duì)話框類中直接定義響應(yīng)函數(shù)早像,進(jìn)行繪制,而不是在工作線程中直接繪圖肖爵,這個(gè)樣子就不會(huì)和對(duì)話框爭(zhēng)奪DC,從而避免失效臀脏。