版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
-.z.High-speedChartingControl--MFC繪制圖表(折線圖、餅圖、柱形圖)控件介紹對于我之前的一個項目,我需要在圖表控件上顯示連續(xù)的數(shù)據(jù)流。我決定開發(fā)自己的控件,因為我找不到任何可以提供所需靈活性的自由軟件控件。其中一個主要的限制是,控件必須繪制大量的數(shù)據(jù),并能夠迅速顯示它(在PocketPC上)??丶軌蛲ㄟ^僅繪制新的數(shù)據(jù)點而不是完整的數(shù)據(jù)序列來做到這一點并且圖表還能夠顯示靜態(tài)數(shù)據(jù)。這種控件是我長時間工作的結果,而且費盡周折地為了提供足夠的靈活性來供需要它的人使用。對于使用者反饋我表示由衷的感:一個,留言板中的一一句話或只是對本文評級。當我不知道是否還有人使用它時,我就沒有必要維護這個控件了。免責聲明這個控件是我花費很長時間的開發(fā)的結果,因此我對代碼的使用放置一些小條件:該代碼可以以編譯的形式用于任何非商業(yè)和商業(yè)目的。代碼可以被重新開發(fā),只要它提供作者名字和完整的免責聲明。更改源代碼需要得到作者的同意。此代碼不提供任何安全保證。我不會對使用此代碼造成的損失負責。使用它需要自己承擔風險。Thiscodemaybeusedforanynon-mercialandmercialpurposesinapiledform.Thecodemayberedistributedaslongasitremainsunmodifiedandprovidingthattheauthornameandthedisclaimerremainintact.Thesourcescanbemodifiedwiththeauthorconsentonly.Thiscodeisprovidedwithoutanyguarantees.Icannotbeheldresponsibleforthedamageorthelossoftimeitcauses.Useitatyourownrisks.鑒于開發(fā)這個控件所付出的努力,下面的要求并不過分:如果你在在商業(yè)應用程序中使用這個控件,則請給我發(fā)讓我知道。主要特點控件的主要特點是:高速繪圖(軸固定時),允許快速繪制數(shù)據(jù)無限數(shù)量的數(shù)據(jù)序列(存是限制)每個數(shù)據(jù)序列的數(shù)據(jù)量不受限制支持線圖,點圖,平面圖,柱狀圖,K線圖和甘特圖系列最多四個軸(左,下,右和上軸)標準軸,對數(shù)軸或日期/時間軸自動伸縮的坐標軸,翻轉的坐標軸(相互獨立)軸標簽點標簽平滑的曲線網(wǎng)格圖例和標題交互性(在控件中發(fā)生特定事件時的通知)支持手動縮放和鼠標平移支持鼠標指針支持軸上的滾動條高度可定制(顏色,標題,標簽,邊緣,字體等)支持UNICODE支持打印和保存到圖像文件文檔結構本文通過一系列簡短的教程來涵蓋控件的大部分功能。閱讀本文后,您將能夠快速地在自己的應用程序中使用本控件。我決定從文章中刪除所有的類和函數(shù)的文檔,因為它不是非常友好并且我很難維護。此外,隨著代碼的增長,要記錄的類和函數(shù)的列表變得過于廣泛以至于不能將所有容放在文章中。作為替代,我提供了一個do*ygen文檔,您可以從本文中(文章的開頭)下載:只需下載“Do*ygen文檔”zip文件,解壓所有文件,雙擊“Inde*.html”文件,進行查看。入門學習此圖表控件允許您在屏幕上繪制一系列數(shù)據(jù)。此控件可以添加幾個不同類型數(shù)據(jù)序列并且最多可以使用四個軸。添加到圖表的數(shù)據(jù)序列與一個水平軸(底部或頂部)和一個垂直軸(右側或左側)相關聯(lián)。這兩個軸控制數(shù)據(jù)序列在圖表上的顯示方式。為了能夠在應用程序中使用次圖表控件,您首先需要在自己的工程里添加源代碼zip中包含的文件。注意:控件在部使用動態(tài)轉型,因此必須啟用RTTI(RunTimeTypeInformation運行時自動類型識別的機制),否則可能會發(fā)生崩潰。默認情況下,VC6沒有啟用RTTI,因此要啟用它打開項目設置->“C/C++”選項卡->“C++語言”類別,并確?!癊nableRun-TimeTypeInformation(RTTI)“選項已選中。在應用程序中使用圖表控件有兩種方法:手動插入,或通過資源編輯器插入。手動插入1.*include"ChartCtrl"添加在對話框(Dialog)類的頭文件中2.在對話框類中添加變量CChartCtrl://{{AF*_DATA(CChartDemoDlg)//}}AF*_DATACChartCtrlm_ChartCtrl;3.在對話框類的OnInitDialog方法中添加這個控件的Create方法。使用資源管理器1.向對話框資源添加自定義控件,打開控件的屬性,并為Class屬性指定ChartCtrl。為了避免滾動條上的閃爍,必須設置WS_CLIPCHILDREN樣式(0*02000000L),如圖所示。2.*include"ChartCtrl.h"添加在對話框(Dialog)類的頭文件中3.在對話框類中添加變量CChartCtrl://{{AF*_DATA(CChartDemoDlg)//}}AF*_DATACChartCtrlm_ChartCtrl;4.在DoDataE*change函數(shù)中添加DD*_Control(不要忘了更改ID號和控件名字):AddavariableoftypeCChartCtrlinyourdialogclass:HideCopyCode//{{AF*_DATA(CChartDemoDlg)//}}AF*_DATA操作數(shù)據(jù)序列幾種類型的數(shù)據(jù)序列可以添加到控制:點序列,線序列,曲面序列,柱狀圖序列,K線圖序列或甘特圖序列。點的數(shù)據(jù)格式可能因序列而異(例如,K線圖和甘特圖系列使用不同的點格式)。一旦你選擇了一種系列,你可以通過調(diào)用上表中列出的CChartCtrl類的輔助函數(shù)之一將其添加到圖表中。這些函數(shù)接受兩個可選參數(shù):兩個布爾值來確定描述該系列是連接到副水平軸(頂軸)或者是連接大副垂直軸(右軸)。如果未指定參數(shù),則數(shù)據(jù)系列將附加到主水平軸(底部軸)和主垂直軸(左軸)。警告:在將任何系列添加到圖表之前,您需要創(chuàng)建該系列所連接的兩個軸。如果不這樣做,將導致控件失效(assert)。有關詳細信息,請參見“操縱軸”一節(jié)。一旦將系列添加到圖表后,我們就可以使用數(shù)據(jù)填充該圖表。有兩種方法:將數(shù)據(jù)放到一個單元中一起添加,或者逐點添加。后者用于有動態(tài)數(shù)據(jù)時:每次調(diào)用函數(shù)時都會更新圖表。雖然這個調(diào)用是快速的(在*些特定條件下),但是最好盡可能地將數(shù)據(jù)放到一個單元中。下面是一個簡單代碼示例,它在圖表中創(chuàng)建兩個系列,并用數(shù)據(jù)填充它們:一個系列在初始化時完全填充,另一個系列在調(diào)用OnDataReceived函數(shù)(僅存在于此示例的目的)時填充。m_pLineSeries,m_pPointsSeries和m_ChartCtrl是CMyClass類的成員變量。voidCMyClass::Init(){....//SNIP:Creationofthea*esinthechart.ThisMUSTbedonebefore.m_pLineSeries=m_ChartCtrl.CreateLineSerie();m_pPointsSeries=m_ChartCtrl.CreatePointsSerie();doubleYValues[10];for(inti=0;i<10;i++)*Values[i]=YValues[i]=i;m_pLineSerie->SetPoints(*Values,YValues,10);}voidCMyClass::OnDataReceived(double*,doubleY){m_pPointsSeries->AddPoint(*,Y);}所有系列類繼承自同一抽象基類:CChartSerie。該類處理所有系列通用的功能,但對具體的數(shù)據(jù)點沒有任何處理功能。點的概念在子類CChartSerieBase中引入,它是一個模板類,模板參數(shù)是要操作為點的數(shù)據(jù)類型。這很重要,因為序列可能必須處理不同的數(shù)據(jù)類型:例如點序列操作具有*和Y值的點,但是K線圖系列操縱具有5個值(打開,關閉,高,低和時間值)的點。其他系列繼承自CChartSerieBase并提供他們操作的數(shù)據(jù)類型。CChartSerieBase類已經(jīng)處理了大多數(shù)數(shù)據(jù)管理,并通過純虛函數(shù)將渲染委托給子類。每個系列在創(chuàng)建時也會分配一個Id。此標識可通過CChartSerie::GetSerieId()檢索,并可用于從圖表中刪除該系列。該系列的一個重要特征是控制點的順序:該系列中的所有點將根據(jù)它們的值重新排序。默認情況下,點是基于它們的*值排序的,但您可以通過對它們的Y值排序或不對它們進行排序來改變這種行為(在這種情況下,系列保持將點添加到系列中的順序)。對點進行排序會對性能產(chǎn)生影響:如果點是有序的,則控件能夠從完整系列中檢索第一個和最后一個可見點,并且僅繪制兩個點之間的點。另一方面,你將不能繪制像橢圓形的曲線。您可以通過調(diào)用CChartSerieBase::SetSeriesOrdering來更改點的順序。控件中的不同系列的功能通常是不言自明的。然而,柱狀圖系列需要一些解釋。柱狀圖系列這個系列有點特別,如果其中幾個在同一個控件上繪制在一起,他們將互相影響。目的是能夠繪制多個條形圖系列,而不會重疊:它們是彼此相鄰繪制的。為此,您需要指定每個所屬的組(一個簡單的整數(shù)標識符)。同一組的系列彼此相鄰地繪制(或者對于水平條在彼此的頂部):參見兩個圖形的示例。設置組ID是通過SetGroupId函數(shù)完成的。您還可以通過調(diào)用SetInterSpace靜態(tài)函數(shù)來控制所有柱形圖之間剩余的空間的寬度。這將為所有系列設置以像素為單位的空間(因此,如果顯示多于兩個系列,則在任何位置使用相同的空間)。注意,您可以通過調(diào)用SetBarWidth單獨設置柱狀圖系列的寬度。在點上添加標簽一旦使用數(shù)據(jù)填充您的系列,您還可以在系列的特定點上添加標簽:這個標簽始終附加到特定點?,F(xiàn)在,只提供一種類型的標簽,氣泡標簽:包含文本的圓角矩形并用線連接到特定點上。當然,如果需要,您也可以提供自己的自定義標簽(參見“擴展功能”一節(jié))。有兩種方式創(chuàng)建文本標簽:靜態(tài)創(chuàng)建標簽時,或動態(tài)注冊一個對象,當標簽請求時,它將提供文本。第一種方法是最簡單的,但也不太靈活。下面是一個代碼片段,顯示如何做(假設m_pSeries已經(jīng)創(chuàng)建并填充足夠的數(shù)據(jù)):voidCMyClass::Init(){//SNIP...m_pSeries->CreateBalloonLabel(5,_T("Thisisasimplelabel"));}此調(diào)用將創(chuàng)建一個帶有“Thisisasimplelabel”文本的標簽,并將其附加到帶索引為5的點。該函數(shù)返回一個指向新創(chuàng)建的標簽的指針,以便您可以修改其*些屬性或存儲以供以后使用。第二種方法有點復雜,但提供了更多的靈活性:例如,您可以以更方便的方式在標簽中顯示點屬性(例如*值,Y值,…)。為此,您必須創(chuàng)建一個繼承自CChartLabelProvider<PointType>的類,并在創(chuàng)建標簽時提供此類的實例。此類是模板類,模板參數(shù)是標簽附加到的系列的點類型。這個類是一個簡單的接口,你必須覆蓋TChartStringGetTe*t(CChartSerieBase<PointType>*pSerie,unsigneduPtInde*)方法。此函數(shù)應返回必須在標簽中顯示的文本。它接收指向標簽所附加的系列和點索引的指針。這里有一個這樣的標簽提供程序類的例子:classCCustomLabelProvider:publicCChartLabelProvider<SChart*YPoint>{public:TChartStringGetTe*t(CChartSerieBase<SChart*YPoint>*pSeries,unsigneduPtInde*){TChartStringStreamssTe*t;SChart*YPointPoint=pSeries->GetPoint(uPtInde*);ssTe*t<<_T("*value=")<<Point.*;returnssTe*t.str();}};此代碼段顯示如何將其與標簽一起使用。注意m_pSeries應該是一個操作SChart*YPoint點(點,線,面或者柱系列)的系列。如果不是這樣,你的代碼將給出一個編譯錯誤。voidCMyClass::Init(){//SNIP...m_pLabelProvider=newCCustomLabelProvider();m_pSeries->CreateBalloonLabel(5,m_pLabelProvider);}控件不獲取指針的所有權,因此,當你不再需要時,你有責任刪除它。在上面的例子中,它通常會在CMyClass析構函數(shù)中被刪除。在上面的示例中,您可以為所有要添加的標簽地方重復使用相同的標簽類,這也帶來另一個優(yōu)點:如果你想在運行時改變標簽的格式,你只需要在CustomLabelProvider中添加代碼。不需要遍歷所有現(xiàn)有標簽并更改其文本。當然,在這種情況下,需要刷新控件,因為必須重新繪制標簽。還要注意TChartStringStream類的用法,TChartStringStream類是由控件提供的別名(類似于TChartString)。當UNICODE被定義時,它解析為std::wstringstream,當未定義UNICODE時,解析為std::stringstream。對軸的操作軸是圖表的一個重要特征,因為它們控制不同系列在控制中的顯示方式??丶凶疃嗫墒褂盟膫€軸:底部,頂部,左側和右側??丶拿總€系列必須和一個水平軸和一個垂直軸相連接。在圖表中添加系列時指定這些軸。底部和左側軸是主軸,頂部和右側軸是輔助軸(您將在控件的*些功能中遇到此問題)?,F(xiàn)在有三種類型的軸供選擇:標準軸,對數(shù)軸和日期/時間軸。您可以在不同位置選用不同類型的軸。一旦您選擇了在不同位置使用哪些軸,您需要先創(chuàng)建它們,然后才能向控件添加任何數(shù)據(jù)。為此,通過指定軸附加在哪個位置,簡單地調(diào)用CreateStandardA*is,CreateLogarithmicA*is或CreateDateTimeA*is。如果已經(jīng)在該位置創(chuàng)建了軸,則控件將銷毀它并且用新的軸替換它。這里有一個簡單的代碼片段,顯示如何在底部創(chuàng)建日期/時間,在左側創(chuàng)建一個標準軸:voidCMyClass::Init(){CChartStandardA*is*pBottomA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::BottomA*is);CChartLogarithmicA*is*pLeftA*is=m_ChartCtrl.CreateLogarithmicA*is(CChartCtrl::LeftA*is);}一旦創(chuàng)建了這些軸,就可以對它們設置一些屬性。大多數(shù)屬性在所有軸類型之間共享(例如自動模式,最小值和最大值,軸標簽,…)。軸可以設置為三種“自動”模式:全自動,屏幕自動和手動模式。全自動模式基于附加到該軸的所有系列計算軸最小值和最大值(所有系列的所有點的最小值用作軸的最小值,并使用所有系列的所有點的最大值作為軸的最大值)。屏幕自動模式基于與該軸相關的所有系列的所有可見點計算軸最小值和最大值。例如,如果圖表僅顯示連接到手動底部軸和屏幕自動左側軸的一個系列,則左側軸將自適應于當前可見的點,并且不考慮這些點有可能超過底軸的圍(在全自動模式下,底軸外部的點將被考慮)。警告:如果系列的兩個軸都處于屏幕自動模式,則結果未定義。在手動模式下,軸最小和最大值由用戶設置,不由控件計算。在使用自動軸模式下,如果將數(shù)據(jù)動態(tài)添加到控件,如果新的數(shù)據(jù)點位于軸的圍之外,則控件將自動刷新。這里是一個代碼片段(繼續(xù)前一個代碼段),顯示一個全自動軸(底部軸)和一個手動軸(左軸,它是一個對數(shù)軸):voidCMyClass::Init(){//SNIP...pBottomA*is->SetAutomaticMode(CChartA*is::FullAutomatic);//ThecalltoSetAutomaticMode(CChartA*is::NotAutomatic)isnot//reallyneededbecausethisisthedefault.pLeftA*is->SetAutomaticMode(CChartA*is::NotAutomatic);pLeftA*is->SetMinMa*(0.01,1000);}處于離散模式下的軸軸有一個模式是離散模式(默認禁用)。此模式指定軸不顯示連續(xù)值,而只顯示離散值,這些值是軸上刻度指定的值,而軸將不顯示其他的值。嘗試繪制不同于顯示的節(jié)拍值的值是不可能的。讓我們舉一個例子:假設你有一個底部標準軸,間隔為1.0(所以,顯示的蜱是1,2,3等等)。嘗試繪制*值為0.5的點將在相同位置顯示該點,就好像它的值為1.0。事實上,你可以認為兩個刻度之間的區(qū)域是一個常量值。這就是為什么刻度標簽顯示在兩個刻度的中間,而不是刻度本身。這里有一個小代碼片段,顯示離散軸對系列顯示方式的影響。代碼片段下的兩個圖像顯示啟用離散模式(第一個圖像)或禁用(第二個圖像)的結果。voidCMyClass::Init(){CChartStandardA*is*pBottomA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::BottomA*is);pBottomA*is->SetMinMa*(0,10);CChartStandardA*is*pLeftA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::LeftA*is);pLeftA*is->SetMinMa*(0,10);pBottomA*is->SetTickIncrement(false,1.0);pBottomA*is->SetDiscrete(true);CChartLineSerie*pSeries=m_ChartCtrl.CreateLineSerie();double*Val[20];doubleYVal[20];for(inti=0;i<20;i++){*Val[i]=YVal[i]=i/2.0;}pSeries->SetPoints(*Val,YVal,20);}使用日期/時間軸使用日期/時間軸有點特別,下面是如何利用這個功能的解釋。要了解日期/時間軸的重要一點是它們在COleDateTime對象部工作。原因很簡單:COleDateTime中有DATE類型的類,DATE類型是一個雙精度型。由于圖表中的點表示為雙精度值,因此它非常適合:使用標準點(非日期/時間)和日期/時間點之間沒有差異,這使得后者的使用不太復雜。所有點仍然存儲為雙精度型,無論是否是日期/時間。創(chuàng)建日期/時間軸后,可以在控件中填充數(shù)據(jù)。為此目的,沒有改變:你必須從CChartSerie類調(diào)用voidAddPoint(double*,doubleY)或voidSetPoints(double**,double*Y,intCount)。CChartCtrl類提供了兩個靜態(tài)函數(shù),讓你從COleDateTime轉換為雙精度,反之亦然:doubleDateToValue(constCOleDateTime&Date)COleDateTimeValueToDate(doubleValue)如果您有另一種格式的日期(例如time_t或SYSTEMTIME),這不是一個問題,因為COleDateTime對象可以從不同的時間格式構造(檢查COleDateTime類的MSDN文檔,以了解從哪種格式可以構造它)。填充數(shù)據(jù)后,可以配置軸以顯示所需的容。與日期/時間軸相關的幾個功能可用:voidSetDateTimeIncrement(TimeIntervalInterval,intMultiplier)voidSetDateTimeFormat(boolbAutomatic,constTChartString&strFormat)voidSetReferenceTick(COleDateTimereferenceTick)第一個允許您指定軸上顯示的兩個節(jié)拍之間的間隔。兩個節(jié)拍之間的間隔將遵守正確的時間,這意味著如果指定1個月的節(jié)拍增量
(Interval=CChartA*is::tiMonthandMultiplier=1),則兩個節(jié)拍之間的間隔將是不規(guī)則的(28,30或31天)。第二個函數(shù)允許您指定刻度標
簽的格式??丶鶕?jù)刻度間隔自動格式化刻度標簽,但您可以通過調(diào)用此函數(shù)覆蓋它。檢查MSDN上的COleDateTime::Format函數(shù)的文檔以獲
取更多信息。最后,SetReferenceTick(COleDateTimereferenceTick)函數(shù)允許您為軸指定一個參考標記。參考標記是用作繪制標記的參考
的日期:在該日期總是存在標記。當您在SetDateTimeIncrement函數(shù)中指定的multiplier不是1時,這很有用。例如,假設您指定了3個月的單
位增量,并且您希望在2月(因此,5月,8月,…)有一個單位,則您可以調(diào)用此函數(shù)將2月1日設置為參考單位。默認設置為2000年1月1日。下面是一個簡單的代碼片段,它創(chuàng)建一個日期/時間軸,并顯示不同函數(shù)的用法:voidCMyClass::Init(){//Setsthea*isminvaluetoJanuary1st2006andthea*is//ma*valuetoDecember31st2007.COleDateTimeminValue(2006,1,1,0,0,0);COleDateTimema*Value(2007,12,31,0,0,0);pBottomA*is->SetMinMa*(CChartCtrl::DateToValue(minValue),CChartCtrl::DateToValue(ma*Value));//Setsthetickincrementto4months(disableautomatictickincrement)pBottomA*is->SetTickIncrement(false,CChartDateTimeA*is::tiMonth,4);//Setstheticklabelformatforinstance"Jan2006"pBottomA*is->SetTickLabelFormat(false,_T("%b%Y"));}自定義外觀控件的外觀方面可以根據(jù)不同的應用場景做出更改,比如控件的不同部分(圖例,標題,背景,…)都可以修改。所有與這些對象的交互是通過CChartCtrl類來實現(xiàn):一些將根據(jù)需要創(chuàng)建(例如a*es或series),一些在創(chuàng)建控件時創(chuàng)建(legend,titles,…)。一般來說,你永遠不會自己創(chuàng)建這些對象,而是將該任務委派給CChartCtrl類。唯一的例外是當您要使用自定義軸或自定義系列(請參閱“擴展功能”部分)。例如,下面是一個代碼段,設置漸變背景,并將圖例放在控件的底部:voidCMyClass::Init(){//SNIP//Disabletherefreshofthecontrolm_ChartCtrl.EnableRefresh(false);//Setthegradientforthebackgroundm_ChartCtrl.SetBackGradient(RGB(255,255,255),RGB(125,125,255),gtVertical);//Dockthelegendatthebottomm_ChartCtrl.GetLegend()->DockLegend(CChartLegend::dsDockBottom);//Specifiesthatthelegendentriesarehorizontallystackedm_ChartCtrl.GetLegend()->SetHorizontalMode(true);//Re-enabletherefreshofthecontrolm_ChartCtrl.EnableRefresh(true);}重要:從版本1.4的控件,每次調(diào)用控件上的一個屬性將導致控件的完全刷新(即使像改變一些文本的字體或對象的顏色)。為了避免在沒有必要時刷新控件(例如,當您同時更改多個屬性時),應首先禁用刷新,更改屬性,然后重新啟用刷新,如上面的代碼段所示。自從1.5版的控件開始支持UNICODE。所有出現(xiàn)的std::string對象已被TChartString對象替換,這只是一個typedef,如果未啟用UNICODE,則解析為std::string,并在啟用UNICODE時解析為std::wstring。響應鼠標事件有時,應用程序需要響應用戶鼠標操作。例如,如果用戶點擊點,則程序可以顯示關于被點擊的點的信息,這一節(jié)將解釋如何做到。雖然原理是有點不同,但是無論你想聽在圖表上的一般鼠標事件本身(點擊軸,圖例,…)或你是否對特定系列的鼠標事件感興趣。這兩種情況都很容易實現(xiàn)。響應圖表上的鼠標事件你必須實現(xiàn)CChartMouseListener接口,覆蓋你感興趣的方法,并通過調(diào)用CChartCtrl::RegisterMouseListener(CChartMouseListener*pMouseListener)將該類的實例注冊到圖表控件。根據(jù)鼠標事件發(fā)生在控件的哪個部分:標題,圖例,軸或繪圖區(qū),調(diào)用該接口上的不同函數(shù)。對于所有這些函數(shù),總是傳遞兩個參數(shù):MouseEvent,它是列出鼠標事件類型(鼠標移動,左鍵單擊,…)的枚舉,以及一個CPoint對象,它包含的發(fā)生事件的點的屏幕坐標。對于*些函數(shù),需要時傳遞一些其他參數(shù)。例如,當單擊一個軸時,指向該軸的指針被傳遞給該函數(shù)。下面是CChartMouseListener的實現(xiàn),它對軸的點擊作出反應,并顯示一個消息框:classCCustomMouseListener:publicCChartMouseListener{public:voidOnMouseEventA*is(MouseEventmouseEvent,CPointpoint,CChartA*is*pA*isClicked){if(mouseEvent==CChartMouseListener::LButtonDoubleClick){MessageBo*(_T("A*isclicked"),_T("Info"),MB_OK);}}};然后你必須創(chuàng)建一個這個類的實例并注冊它:m_pMouseListener=newCCustomMouseListener();m_ChartCtrl.RegisterMouseListener(m_pMouseListener);這里也需要自己刪除指針。響應系列上的鼠標事件響應系列上的事件與響應一般事件非常相似,只是監(jiān)聽器是CChartSeriesMouseListener的一個實例,它是一個模板類,模板參數(shù)是系列的點類型。這是需要的,以避免當您要檢索點的特定值時不必要的轉型。另一個區(qū)別是,您必須在系列本身上注冊監(jiān)聽器,而不是在圖表控件上注冊。下面是CChartSeriesMouseListener的實現(xiàn),它對系列的點擊做出反應,如果點擊發(fā)生在點上,它將顯示一個帶有點的Y值的消息框:classCCustomMouseListener:publicCChartSeriesMouseListener<SChart*YPoint>{public:voidOnMouseEventSeries(MouseEventmouseEvent,CPointpoint,CChartSerieBase<SChart*YPoint>*pSerie,unsigneduPointInde*){if(mouseEvent==CChartMouseListener::LButtonDoubleClick&&uPointInde*!=INVALID_POINT){TChartStringStreamssTe*t;SChart*YPointPoint=pSeries->GetPoint(uPointInde*);ssTe*t<<_T("Yvalue=")<<Point.Y;TChartStringstrTe*t=ssTe*t.str();MessageBo*(NULL,strTe*t.c_str(),_T("Info"),MB_OK);}}};注意:當用戶不點擊一個點時,OnMouseEventSeries函數(shù)也可以被調(diào)用。例如當用戶在兩個點之間但仍然在該系列上點擊時情況。在這種情況下,為uPointInde*參數(shù)傳遞INVALID_POINT。然后,您必須創(chuàng)建此類的實例并將其注冊到系列中:m_pMouseListener=newCCustomMouseListener();m_pSeries.RegisterMouseListener(m_pMouseListener);注意:只有當系列操作SChart*YPoint類型的點(點,線,面或者柱狀圖系列)時,這才會起作用。如果不是這樣,您的代碼將生成編譯錯誤。出于性能原因,禁止檢測系列上的鼠標移動事件。要啟用它,請參閱do*ygen文檔中的CChartSerie::EnableMouseNotifications函數(shù)。使用光標您還可以向控件添加光標。支持兩種類型的光標:“十字線”光標和“拉線”光標。第一個是在鼠標移動的繪圖區(qū)域上顯示的簡單十字,第二個是與特定軸關聯(lián)的水平或垂直線,您可以通過單擊它并使用鼠標移動來拖動。對于每個光標,您可以注冊一個偵聽器,以便在移動光標時通知它。這里是一段代碼,用于創(chuàng)建與底部和左側軸相關聯(lián)的“十字準線”光標以及與底部軸相關聯(lián)的“拉線”光標://Createsacross-haircursorassociatedwiththetwoprimarya*es.CChartCrossHairCursor*pCrossHair=m_ChartCtrl.CreateCrossHairCursor();//Createsadraglinecursorassociatedwiththebottoma*is.CChartDragLineCursor*pDragLine=m_ChartCtrl.CreateDragLineCursor(CChartCtrl::BottomA*is);//Hidesthemousewhenitisovertheplottingarea.m_ChartCtrl.ShowMouseCursor(false);注意到對CChartCtrl::ShowMouseCursor的調(diào)用結束。默認情況下,鼠標總是可見的,但是當您使用十字光標時,當它在繪圖區(qū)域時隱藏有時是需要的。如果希望在光標位置更改時收到通知,則必須實現(xiàn)CChartCursorListener接口,創(chuàng)建其實例并使用光標注冊它:classCCustomCursorListener:publicCChartCursorListener{public:voidOnCursorMoved(CChartCursor*pCursor,double*Value,doubleyValue){TChartStringStreamssTe*t;ssTe*t<<_T("Cursormoved:*Pos=")<<*Value<<_T(",yPos=")<<yValue;//Dosomethingwiththestring...}};CCustomCursorListener*pCursorListener=newCCustomCursorListener;pDragLine->RegisterListener(pCursorListener);OnCursorMoved函數(shù)接收一個*和Y值,但對于拖動光標,只使用這些值中的一個:如果光標與水平軸相關聯(lián),則使用*值,否則使用Y值。使用平移和縮放功能在版本1.1的控件中,縮放和平移功能已被添加到控件。使用鼠標左鍵控制縮放,用鼠標右鍵控制平移。要縮放圖表的特定部分,只需左鍵單擊圖表(這將是縮放矩形的左上角),然后拖動到右下角。將出現(xiàn)一個矩形。一旦松開鼠標按鈕,四個軸將自動調(diào)整到您選擇的區(qū)域。默認情況下啟用縮放,但您可以通過調(diào)用CChartCtrl::SetZoomEnabled(boolbEnabled)來禁用縮放。您還可以通過調(diào)用CChartA*is::SetZoomLimit(doubledLimit)為每個軸指定縮放限制。它指定縮放時軸的最小圍。默認值為0.001。要平移控件,右鍵單擊控件上的*處并移動鼠標。鼠標下的點將“跟隨”鼠標的移動(實際上,軸的最小和最大值將改變)。默認情況下啟用平移,但您可以通過調(diào)用CChartCtrl::SetPanEnabled(boolbEnabled)來禁用它。如果您左鍵單擊圖表(例如開始縮放),但如果您移動到左上角,所有使用縮放和平移功能所做的修改將被取消(控制將處于它的狀態(tài)在使用平移和縮放操作之前)。最后,還有一種方法通過調(diào)用CChartA*is::SetPanZoomEnabled(boolbEnabled)禁用特定軸的平移和縮放功能。利用高速功能線和點系列允許以高速率繪制數(shù)據(jù)。這通常在要繪制來自外部設備(例如,傳感器)的數(shù)據(jù)時完成。這是可能的,因為當您向此類系列添加點時,控件不會完全刷新,只會繪制最后一個點(或最后一個線段),這是非常有效的。但是,如果希望控件能夠足夠快地繪制數(shù)據(jù),則必須考慮幾點。一個重要的事情是,使用自動軸可能會降低很多性能。這是因為如果一個點繪制在軸圍之外,則軸圍將被自動調(diào)整,這意味著控制將被完全刷新。因此,如果您使用自動底部軸線并具有“滾動”軌跡,則每個新點都將位于軸的當前圍之外,并且將對每個點執(zhí)行控制刷新。處理的更好的方法是使用固定軸并且每秒手動地增加軸的圍(或以合理的速率)。另一個重要的點是,你不應該在向一個系列添加一個新點之后調(diào)用RefreshCtrl。這當然會完全地刷新控件,但是應該避免這樣做。最后,如果您需要同時應用幾個修改或添加幾個點到控件,您應該在EnableRefresh(false)和EnableRefresh(true)之間封裝這些調(diào)用(請參閱“自定義外觀”部分)。擴展功能在*些特定情況下,您需要使用新功能擴展控件,例如新的系列類型。目前,您可以自定義四個組件:序列,軸,點標簽和光標。要提供新軸,新標簽或標,您只需繼承基類(CChartA*is,CChartLabel或CChartCursor)并實現(xiàn)所需的虛擬函數(shù)。一旦完成,您可以通過調(diào)用不同函數(shù)的自定義版本(CChartCtrl::AttachCustomA*is,CChartCtrl::AttachCustomLabel或CChartCtrl::AttachCustomCursor)附加您的新對象。CChartLabel類是一個模板類。這個主題有點廣泛,進入了很多細節(jié),但最簡單的方法是看看不同的現(xiàn)有類。如果你想提供新的系列,這有點不同:你首先要考慮你想要在你的系列中操縱的點的類型。如果你只需要使用*和Y值來操作點,則你可以繼承CChart*YSerie,它提供了很多功能來操作這些點。然后,您必須實現(xiàn)所需的虛擬函數(shù)??纯聪旅娴南盗校篊ChartLineSerie,CChartPointSerie,CChartSurfaceSerie和CChartBarSerie具體示例。如果你的系列操縱其他類型的點,則你首先必須為點包含以下方法創(chuàng)建一個結構:doubleGet*(),doubleGet*Min(),doubleGet*Ma*(),doubleGetY(),doubleGetYMin()和doubleGetYMa*()。一旦完成,您必須繼承CChartSerieBase并將此點作為模板參數(shù)。然后,您必須提供所需的虛擬功能。看看下面的系列具體例子:CChartCandlestickSerie和CChartGanttSerie。UpgradingfromVersion1.*toVersion2.0在版本2.0中,對控件進行重構,導致API的更改。主要的可見變化是每個軸類型現(xiàn)在有其單獨的類(CChartStandardA*is,CChartDateTimeA*is和CChartLogarithmicA*is)。這也意味著默認情況下沒有創(chuàng)建軸,并且您必須在向圖表添加系列之前自己創(chuàng)建軸(否則代碼將斷言)。這包括在“操縱軸”部分。另一個變化是添加系列到圖表的方式:AddSerie已經(jīng)在CChartCtrl類中刪除,并已被幫助函數(shù)替代,以創(chuàng)建特定的系列類型(CreateLineSerie,CreatePointsSerie,…)。這些函數(shù)返回確切的系列類型,因此不再需要鑄造。這在“操縱系列”一節(jié)中有詳細描述。UpgradingfromVersion2.*toVersion3.*版本的主要變化是,系列基類現(xiàn)在已經(jīng)作為模板類,模板參數(shù)是系列操作的點類型。如果您沒有通過提供新的系列類型擴展控件,這將不會在您的代碼中有所不同。如果你提供了一個新的系列類型,你的類必須繼承CCharSerieBase并提供它操作的點的類型。如果你的系列使用只有*和Y值的點,你可以簡單地繼承CChart*YSerie。看看現(xiàn)有的系列更多的例子。另一個小的修改是標簽提供程序現(xiàn)在也是模板類(出于同樣的原因)。并且監(jiān)聽系列中的鼠標事件現(xiàn)在從圖表上的鼠標事件中分離出來。這兩點在“在點上添加標簽”部分和“鼠標事件通知”部分中有很好的解釋。最后,CChartA*is::SetAutomatic方法已被標記為已棄用,您應該使用CChartA*is::SetAutomaticMode(已經(jīng)引入了一個額外的自動模式)。例子本節(jié)只是兩個代碼片段,顯示了如何使用控件。第一個片段再現(xiàn)了示波器示例的圖像(參見本文頂部),第二個示例再現(xiàn)了“2008年收入”圖像。代碼是文檔化的,所以它不應該太難理解。Oscilloscopee*ample://Disabletherefreshofthecontrol(avoidmultiplerefresh).m_ChartCtrl.EnableRefresh(false);//Createabottomandlefta*esCChartStandardA*is*pBottomA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::BottomA*is);CChartStandardA*is*pLeftA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::LeftA*is);//Setstheminandma*valuesofthebottomandlefta*isto-15->15pBottomA*is->SetMinMa*(-15,15);pLeftA*is->SetMinMa*(-15,15);//AddanewseriesoftypelinetothecontrolandadddatatoitCChartLineSerie*pLineSeries=m_ChartCtrl.CreateLineSerie();//Specifiesthatthepointsintheseriesarenotordered(neededtobeable//todrawanel.shan*lipse).pLineSeries->SetSeriesOrdering(poNoOrdering);for(inti=0;i<361;i++){double*=10*sin(i/360.0*2*3.141592);doubleY=10*cos((i-60)/360.0*2*3.141592);pLineSeries->AddPoint(*,Y);}//Definesthedifferentcolors(backcolor,a*escolor,...)COLORREFBackColor=RGB(0,50,0);COLORREFGridColor=RGB(0,180,0);COLORREFTe*tColor=RGB(0,180,0);COLORREFSerieColor=RGB(0,255,0);//Specifiesasunkenborderforthecontrolm_ChartCtrl.SetEdgeType(EDGE_SUNKEN);//Setsthecoloroftheborderandthebackcolorm_ChartCtrl.SetBorderColor(Te*tColor);m_ChartCtrl.SetBackColor(BackColor);//Setsthecolorofthedifferentelementsofthebottoma*ism_ChartCtrl.GetBottomA*is()->SetA*isColor(Te*tColor);m_ChartCtrl.GetBottomA*is()->SetTe*tColor(Te*tColor);m_ChartCtrl.GetBottomA*is()->GetGrid()->SetColor(GridColor);//Setsthecolorofthedifferentelementsofthelefta*ism_ChartCtrl.GetLeftA*is()->SetA*isColor(Te*tColor);m_ChartCtrl.GetLeftA*is()->SetTe*tColor(Te*tColor);m_ChartCtrl.GetLeftA*is()->GetGrid()->SetColor(GridColor);//Setsthecolorofthetitle,changethefonttoTimesNewRoman//andaddastringm_ChartCtrl.GetTitle()->SetColor(Te*tColor);m_ChartCtrl.GetTitle()->SetFont(140,_T("TimesNewRoman"));m_ChartCtrl.GetTitle()->AddString(_T("Ane*ampleofoscilloscope"));//ChangethecolorofthelineseriespLineSeries->SetColor(SerieColor);//Finallyre-enabletherefreshofthecontrol.Thiswillrefreshthe//controlifanyrefreshwasstill'pending'.m_ChartCtrl.EnableRefresh(true);“Ineover2008”e*ample:srand((unsignedint)time(NULL));//Disabletherefreshm_ChartCtrl.EnableRefresh(false);COleDateTimeMin(2008,1,1,0,0,0);COleDateTimeMa*(2008,10,1,0,0,0);//Createthebottoma*isandconfigureitproperlyCChartDateTimeA*is*pBottomA*is=m_ChartCtrl.CreateDateTimeA*is(CChartCtrl::BottomA*is);pBottomA*is->SetMinMa*(Min,Ma*);pBottomA*is->SetDiscrete(true);pBottomA*is->SetTickIncrement(false,CChartDateTimeA*is::tiMonth,1);pBottomA*is->SetTickLabelFormat(false,_T("%b"));//Createthelefta*isandconfigureitproperlyCChartStandardA*is*pLeftA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::LeftA*is);pLeftA*is->SetMinMa*(0,100);pLeftA*is->GetLabel()->SetTe*t(_T("Unitssold"));//Createtherighta*isandconfigureitproperlyCChartStandardA*is*pRightA*is=m_ChartCtrl.CreateStandardA*is(CChartCtrl::RightA*is);pRightA*is->SetVisible(true);pRightA*is->GetLabel()->SetTe*t(_T("Ine(kEuros)"));pRightA*is->SetMinMa*(0,200);//Configurethelegendm_ChartCtrl.GetLegend()->SetVisible(true);m_ChartCtrl.GetLegend()->SetHorizontalMode(true);m_ChartCtrl.GetLegend()->UndockLegend(80,50);//Addte*ttothetitleandsetthefont&colorm_ChartCtrl.GetTitle()->AddString(_T("Ineover2008"));CChartFonttitleFont;titleFont.SetFont(_T("ArialBlack"),120,true,false,true);m_ChartCtrl.GetTitle()->SetFont(titleFont);m_ChartCtrl.GetTitle()->SetColor(RGB(0,0,128));//Setsagradientbackgroundm_ChartCtrl.SetBackGradient(RGB(255,255,255),RGB(150,150,255),gtVertical);//CreatetwobarseriesandalineseriesandpopulatethemwithdataCChartBarSerie*pBarSeries1=m_ChartCtrl.CreateBarSerie();CChartBarSerie*pBarSeries2=m_ChartCtrl.CreateBarSerie();CChartLineSerie*pLineSeries=m_ChartCtrl.CreateLineSerie(false,true);intlowInde*=-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);intIne=DesktopVal+LaptopVal*1.5;if(Ine<lowVal){lowVal=Ine;lowInde*=i;}pLineSeries->AddPoint(TimeVal,Ine);}//ConfiguretheseriesproperlypBarSeries1->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("Totaline"));pLineSeries->SetWidth(2);pLineSeries->EnableShadow(true);//Addalabelonthelineseries.TChartStringStreamlabelStream;labelStream<<_T("Minine:")<<lowVal;CChartBalloonLabel<SChart*YPoint>*pLabel=pLineSeries->CreateBalloonLabel(lowInde*,labelStream.str()+_T("kEuros"));CChartFontlabelFont;labelFont.SetFont(_T("MicrosoftSansSerif"),100,false,true,false);pLabel->SetFont(labelFont);//Reenabletherefreshm_ChartCtrl.EnableRefresh(true);FeedbackQuitealotofworkisinvolvedinthedevelopmentofthiscontroland,asanyothersoftwareproject,itmightstillcontainbugsorerrorsinthedocumentation.Ifyouencountersuchaproblem,pleaseletmeknow(evenifyoufi*edityourself)sothatIcanfi*theissueassoonaspossible.Otherusersofthecontrolwillthankyouforthat.Thesameifyouencountererrorsinthedocumentationortyposinthearticle.I’malsomoreorlessconstantlyworkingonthiscontroltoaddnewfeatures.Ifyouhavesomerequirementforanicefeaturethatcouldbeusefulforothers,pleaseletmeknowandI’lladdittomywishlist.However,asI’mworkingonthiscontrolinmysparetime,mytimeisratherlimited.Finally,ifyoulikedthiscontrol,donothesitatetodropmeawordinthediscussionforumortoratethearticle,thisismuchappreciated.Thankyou.History08/05/2006:Releaseofversion1.019/08/2006:Releaseofversion1.1Bugfi*inScreenToValuefunction(CChartA*is)Bugfi*inRemoveAllSeriesfunction(CChartCtrl)AddedsupportformanualzoomAddedsupportformousepanningAbilitytospecifyatickincrementonthea*isAddedsupportforresizingthecontrol09/04/2007:Releaseofversion1.2GDIleakcorrectedInvisibleseriesarenottakeninaccountforautoa*isandlegend(thankstojerminator-jp)Abilitytochangethete*tcolorofthea*isAbilitytochangethecoloroftheborderofthedrawingareaSurfaceseriesadded16/02/2008:Releaseofversion1.3Addeddate/timea*isBugfi*inhowthelogarithmiclabelsaredisplayed(trailing0)AbilitytochangethecolorofthezoomrectangleRemovedpilerwarningsforVC2005Bugfi*inthezoom14/04/2008:Releaseofversion1.4AddedsupportforscrollbarsBarseriesaddedLegendcanbedockedonanysideorfloatingSupportforlegendinhorizontalmodeSupportfortransparentbackgroundonthelegendSupportforshadowforseveralobjectsRemovePointsFromBegin,RemovePointsFromEndandAddPointsintheCChartSeriesclassSupportforgradientbackgroundEnableRefreshandUndoPanZoomfunctionsaddedinCChartCtrlPossibilitytoenable/disablethezoomforaspecifica*isandtosetitslimitSpeedimprovementontheseries(minandma*cached,orderingoftheseries)SeriescanberemovedusingtheirpointersBugfi*forinvisibleseriesinthelegendBugfi*forlogarithmica*is(1digitwasnotdisplayed)Bugfi*whenremovingseriesfromthecontrolBugfi*ifthepenwidthisbiggerthan1forlineseriesBugfi*forautomatica*is20/08/2008:Releaseofversion1.5AddedsupportforUNICODEAddedsupportforprintingAuto-hidescrollbarsBaselineselectionforbarseriesPerformancepatchScrollbarflickeringremoved(seehere)Bugfi*:scrollbarisnowupdatedwhena*isispannedBugfi*:callingAddPointwasnotdrawingthenewpointBugfi*:ticklabelsforloga*iswerenotalwayscorrect(roundingerror)Bugfi*:lastpointofChartPointSeriewasnotdisplayedBugfi*:movingthemouseoutsidethecontroldoesn’tstopthezoomorpanoperation(thebuttoncanbereleasedsidethecontrol)13/04/2009:Releaseofversion2.0Thedifferenta*istypesarenowseparatedintodifferentclassesModifiedthewaytoaddseriestothecontrolforimprovedfle*ibilityAddedcursorsAbilitytodisplaydiscretea*esAbilitytobenotifiedaboutmouseeventsoccurringonthecontrolAddedlabelsonpointsAbilitytodisplayasmoothcurveAddedChartFont:allowsforitalic,boldorunderlinedfontsAddedtheSetReferenceTickfunctionfordate/timea*isAbilitytostoreuserdataforeachpointSeriesnowhaveanIdRemovedtheCChartObjectclassPointsarenowstoredinastandardarrayinsteadofastd::vectorforefficiencyBinarysearchimplementedforfindingthefirstandlastvisiblepoints(forefficiency)ThelineseriesnowusesPolyLineinsteadofMoveTo/LineTo(efficiency)Bugfi*whenusingdate/timea*iswithatickintervalinyearsBugfi*:barseriesweredrawnfromthewronga*is11/06/2009:ReleaseofversionOptimization:thepanfeatureh
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五版牛只運輸車輛駕駛人員培訓與考核合同3篇
- 二零二五年度暖氣設備安裝工程安全生產(chǎn)管理合同3篇
- 二零二五年度農(nóng)業(yè)科技創(chuàng)新農(nóng)副業(yè)承包合同書模板4篇
- 美容院與互聯(lián)網(wǎng)平臺合作開展直播帶貨合同4篇
- 公共管理導論知到智慧樹章節(jié)測試課后答案2024年秋西北大學
- 買賣雙方2024年蔬菜交易合同3篇
- 2025年度木門原材采購合同4篇
- 二零二五寵物醫(yī)院獸醫(yī)職務聘任與培訓合同4篇
- 2025年度南京市二手房買賣合同電子版范本4篇
- 二零二五版農(nóng)業(yè)綜合開發(fā)農(nóng)資采購項目合同4篇
- 基因突變和基因重組(第1課時)高一下學期生物人教版(2019)必修2
- 內(nèi)科學(醫(yī)學高級):風濕性疾病試題及答案(強化練習)
- 音樂劇好看智慧樹知到期末考試答案2024年
- 辦公設備(電腦、一體機、投影機等)采購 投標方案(技術方案)
- 查干淖爾一號井環(huán)評
- 案卷評查培訓課件模板
- 2024年江蘇省樣卷五年級數(shù)學上冊期末試卷及答案
- 人教版初中英語七八九全部單詞(打印版)
- 波浪理論要點圖解完美版
- 金融交易數(shù)據(jù)分析與風險評估項目環(huán)境敏感性分析
- 牛頓環(huán)與劈尖實驗論文
評論
0/150
提交評論