開發高級MATLAB可視化效果通常需要管理多個低層的圖形對象,包含動態更新圖形的應用程序更是如此。這種應用程序可能需要非常耗時的編程。Chart 對象可提供高級應用程序編程接口(API),實現可視化的自定義創建。圖表可為最終用戶提供方便的可視化 API,無需用戶執行低層圖形編程。
本文以包含最佳擬合線的散點圖為主要示例,通過分步指導展示如何使用 MATLAB 面向對象的編程創建和實現自定義圖表,包括:
編寫標準圖表模板
編寫構造方法
使用 private 屬性封裝圖表數據和圖形
使用 Dependent 屬性創建高級可視化 API
管理圖表生命周期
使用繼承簡化附加圖表的開發
圖表示例
MATLAB 提供幾種圖表,包括 heatmap 圖和 geobubble 圖,前者顯示疊放在彩色網格上的矩陣值,后者可快速在地圖上繪制離散數據點(圖1)。
圖1 heatmap圖和 geobubble 圖
此外,我們還創建幾種特定于應用程序的圖表(圖2)。您可以從File Exchange 中隨本文使用的 MATLAB 代碼一起,下載這些圖表。
圖2 可在 File Exchange 中下載自定義圖表:
https://ww2.mathworks.cn/matlabcentral/fileexchange/65857-creating-specialized-charts-with-matlab-object-oriented-programming
創建二維散點圖:函數或圖表?
假設我們要創建包含對應最佳擬合線的二維散點圖(圖 3)。我們可以使用 scatter 函數顯示離散 (x,y) 數據點,并使用 Statisticsand Machine Learning Toolbox 中的 fitlm 函數計算最佳擬合線。
圖3 最佳擬合線和底層的分散數據
以上代碼可滿足靜態可視化的需求。但是,如果應用程序要求對數據進行動態修改,我們會遇到幾個難題:
如果使用長度與當前 XData 相同的新數組替換 XData 或 Ydata,最佳擬合線不會動態更新(圖 4)。
圖4 最佳擬合線在更改散點圖的 XData 后未更新
如果 Scatter 對象 s 的任一數據屬性(XData 或 YData)設置為長度比當前數組長或短的數組,該對象會發出警告且不會執行圖形更新。
我們可以通過設計一個圖表 ScatterFit 來解決這些難題。
構建圖表代碼:函數或類?
函數將代碼封裝為可重用單元,用戶無需重復代碼即可創建多個圖表。
請注意,此函數需要輸入兩個數據(x 和 y)。您可以指定圖形父項 f(例如,圖形)作為第一個輸入參數。
scatterfit(x,y) 指定輸入的兩個數據
scatterfit(f,x,y) 指定圖形父項和數據
在第一種情況下,該函數展示自動生成的行為,即將自動創建圖表的圖形。
使用函數創建圖表具有某些缺點:
圖表創建后數據無法修改
要更改圖表數據,指定不同的數據輸入后,需要再次調用函數重新創建圖表
最終用戶很難查找可配置的圖表參數(如注釋和分散/線屬性)
用類來實現圖表具有代碼封裝及方法可重用性等好處,同時支持對圖表進行修改。
定義圖表類
為了與 MATLAB 圖形對象保持一致,我們將圖表實現為 handle 類,以便在適當位置修改圖表。我們支持在圖表屬性中使用點記法和 get/set 語法。要實現這一目標,我們從預定義 matlab.mixin.SetGet 類(本身是 handle 類)中派生出 ScatterFit 圖表。
因此,任何屬性都將自動支持表 1 中顯示的語法。
表1圖表屬性的訪問和修改語法
編寫圖表類的構造方法
構造方法是類定義中的一個函數,用于構造圖表對象。首先,將代碼從 scatterfit 函數復制到我們的圖表構造方法之中。隨后,進行以下修改支持所需圖表行為:
輸入參數。我們使用 varargin 支持在所有圖表輸入參數中使用名稱值對。這意味著不需要指定輸入參數,所有輸入值均可選。
父圖形。不同于函數,如果已指定圖形構造中沒有輸入 Parent,則無法為圖表自動創建該輸入。我們使用空 Parent 創建 axes 對象。
請注意,此行為與 plot 和 scatter 等便捷函數的行為不同,后兩者可以自動生成輸入。如果用戶將 Parent 指定為輸入參數,則隨后將在構造方法中對其進行設置。
圖表圖形。我們可創建和存儲圖形所需的任何圖形對象。大多數圖表需要 axes 對象以及線或貼片對象等 axes 內容。在 ScatterFit 圖表中,我們需要 Scatter 對象和 Line 對象。
圖形配置。我們可通過設置任何所需屬性配置圖表圖形。例如,我創建標簽或標題等注釋、設置 axes 的特定視圖、添加網格,或者調整顏色、樣式或線寬。
用戶指定的輸入。我們可設置最終用戶提供的任何名稱值對參數。由于圖表派生自 matlab.mixin.SetGet,實現這一點非常容易:
如果用戶已作為名稱值對輸入參數提供數據屬性,在此處設置這些數據(XData 和 YData)。我們還注意到,此編碼方法可確保用戶在指定名稱值對時出現的任何錯誤都將被圖表的屬性 set 方法發現和解決(稍后討論)。
如可行,我們將使用基元對象創建圖表圖形,因為高級便捷函數在調用時將重置多個現有 axes 屬性(圖2)。但是,這條原則存在例外情況:在 ScatterFit 內部,我們將使用 scatter 函數創建 Scatter 圖形對象,因為它支持對個別標記的尺寸和顏色進行后續更改。
表2 基元和高級圖形函數的示例
封裝圖表數據和圖形
在大多數圖表中,底層圖形包含至少一個 axes 對象及其內容(例如線或面對象)或多個對等的 axes 對象(例如,圖例或彩條)。這些圖表還包含內部數據屬性,以確保公共屬性之間保持一致。我們存儲底層圖形和內部數據為專有圖表屬性。例如,ScatterFit 圖表會維護以下專有屬性:
我們使用命名約定 XData_ 指示該版本是圖表數據的專有內部版本。用戶可見的對應公共數據屬性將命名為 XData。
使用 private 屬性主要有三個目的:
限制底層圖形的可見性,隱藏實現細節,減少 API 中的視覺混亂
限制對底層圖形的訪問,減少繞過 API 的幾率
輕松同步圖表數據(例如,我們需要對 ScatterFit 的 XData 和 YData 屬性進行關聯)
提供可視化API
設計圖表的一個主要原因是提供方便、直觀的 API。我們使用與現有圖形對象屬性一致的名稱為 ScatterFit 圖表提供容易識別的屬性(圖 5)。
圖5 ScatterFit 圖表API
用戶可使用表 1 中所示的語法訪問或修改這些屬性。關聯的圖表圖形會動態更新,以響應屬性的修改。例如,更改圖表的 LineWidth 屬性會更新最佳擬合線的 LineWidth。
我們使用 Dependent 類屬性實現圖表 API。Dependent 屬性的值未進行顯式存儲,而是獲取自類中的其他屬性。在圖表中,Dependent 屬性依賴于低級別圖形等專有屬性或內部數據屬性。
要定義 Dependent 屬性,我們首先使用屬性 Dependent 在 properties 塊中聲明其名稱。這表明該屬性的值依賴于類中的其他屬性。
通過編寫對應的 get 方法,我們還可以指定依賴于其他屬性的屬性。此方法會返回單個輸出參數,即 Dependent 屬性的值。在 ScatterFit 圖表中,XData 屬性(圖表公共接口的一部分)就是底層 XData_ 屬性,它作為圖表的 private 屬性存儲在內部。
我們為每個可配置的圖表屬性編寫 set 方法。此方法將用戶指定的值分配到正確的內部圖表屬性,以在必要時觸發圖形更新。
對于 ScatterFit 圖表,我們支持對數據屬性(XData 和 YData)進行動態修改(包括長度更改)。當用戶設置圖表的(公共)XData 時,我們將根據比較新數據矢量與現有數據的長短,填充或截斷相對的(專有)數據屬性 YData_。請記住,如果用戶在創建圖表時已指定 XData,此 set 方法將被構造方法調用。
我們通過調用不同的 update 方法刷新圖表圖形。此方法包含在 Scatter 對象中設置新數據、重新計算最佳擬合線,以及在對應的 Line 對象中設置新數據所需的代碼。
我們以相同方式為 YData 實現 set 方法,以切換 X/YData 屬性的角色。還會從 set 方法中為YData調用 update 方法。
要創建適用于最終用戶的豐富 API,我們會實現一組廣泛的 Dependent 屬性。建議在每個圖表中至少包含表 3 中所示的屬性。
表3. 建議的 Dependent 屬性
請注意,在大多數情況下,這些屬性將直接映射到底層圖表 axes。例如,Parent 屬性的 get 和 set 方法將圖表對象的 Parent 映射到 axes 的 Parent。
我們通過定義額外公共接口屬性啟用對可視化設置的控制,其中每個屬性映射到圖表維護的特定低級別圖形對象。在此類別中,ScatterFit 圖表支持各種線相關屬性,如最佳擬合線相關 LineStyle、LineWidth 和 LineColor。例如,圖表對象的 LineColor 屬性會映射到線對象的 Color 屬性。
此類別中的典型圖表屬性包括:
視圖相關屬性——例如,axes 的 View、XLim 和 Ylim
注釋——例如,axes 的 Xlabel、Ylabel 和 Title
裝飾性屬性——例如,顏色、線寬,樣式、網格、透明效果和明暗度
管理圖表的生命周期
ScatterFit 圖表與其底層 axes 對象密切關聯,該對象作為圖表的 private 屬性之一存儲。要正確管理圖表的生命周期,我們需要確保兩種行為:
刪除 axes(例如,通過關閉主圖形窗口)即刪除圖表。如果不能保證這一點,一旦修改圖表的數據屬性,將導致 MATLAB 嘗試更新刪除的圖形對象,從而引發錯誤
刪除圖表(例如,在其超出范圍時或當其句柄顯式刪除時)即刪除 axes。如果做不到這一點,則在刪除圖表后,仍會殘留其靜態圖形
MATLAB 中的每個圖形對象都具有 DeleteFcn 屬性——一種在圖形對象超出范圍后被自動調用的回調函數。因此,在圖表構造方法中設置 axes 的 DeleteFcn 滿足第一個要求。
此處,onAxesDeleted 是 private 類方法,僅充當圖表析構方法周圍的包裝程序。如前所述,每個 handle 類在創建時都包含可自定義析構方法。當對象超過范圍后,析構方法將被調用。
通過編寫自定義圖表類的析構方法,我們可滿足第二個要求。在圖表析構時,我們將刪除圖表的axes。
實現這兩個要求后,圖表對象與其底層 axes 將具有相同的生命周期(圖 6)。
圖6管理圖表和 axes 生命周期
簡化附加表格的開發
在編寫幾個圖表后,我們能夠輕松識別相似性和重復的代碼段。通過在超類中集中放置通用代碼,我們可以加速編寫額外圖表的進程。每個新圖表可派生自此超類,這可使我們專注于實現該特定圖表的細節,減少重復編碼的需求。
我們的超類(即 Chart)具有以下結構:
Chart 派生自 matlab.mixin.SetGet。
Chart 將實現六個核心 Dependent 屬性 Parent、Position、Units、OuterPosition、ActivePositionProperty 和 Visible。
Chart 具有 protected 屬性 Axes(底層對等圖形)。
Chart 構造方法將創建對等的 axes 對象并將 axes 的 DeleteFcn 設置為 protected 方法 onAxesDeleted。此方法進而將刪除圖表對象。
-
數據
+關注
關注
8文章
7241瀏覽量
91013 -
函數
+關注
關注
3文章
4371瀏覽量
64204 -
圖表
+關注
關注
0文章
33瀏覽量
8976
發布評論請先 登錄
[Actor] 通過actor創建控制中心與數據采集工作站來看操作者架構
無刷電機電調的基本原理
基于鴻蒙原生ArkTS語法開發的圖表組件--柱狀圖

SciChart—高性能的JavaScript圖表和圖形庫

Chart FX——金融圖表
Chart FX——圖表導出

Chart FX——打印圖表

Chart FX-使用API傳輸數據 使用Value屬性傳輸數據

Chart FX-圖表渲染尺寸和格式

gitee 支持的編程語言有哪些
Chart FX-選擇圖表輸出

評論