技术文章和通讯

用MATLAB面向对象编程创建专门的图表

肯·迪利(Ken Deeley)和大卫·桑普森(David Sampson), MathWorks出版社


发展先进MATLAB®可视化通常涉及管理多个低级图形对象。对于包含动态更新的图形的应用程序来说尤其如此。这样的应用程序可能需要耗时的编程。

一个图表对象提供了用于创建自定义可视化的高级应用程序编程接口(API)。图表不仅为最终用户提供了方便的可视化API,还消除了用户实现低级图形编程的需要。

MATLAB包含一个面向对象的框架,通过以下容器超类来开发自定义图表:

本文提供了使用此框架创建和实现自定义图表的一步一步的指导,包括设计模式和最佳实践。这些步骤用包含最佳拟合线的示例散点图来说明。主题包括:

  • 编写标准图表模板
  • 写表的设置使现代化方法
  • 封装数据和图形
  • 为最终用户提供高级API
  • 包括交互式控件

图表的例子

几种图表在MATLAB中可用,包括的热图图表,它可视化了叠加在彩色网格正方形上的矩阵值,以及geobubble图,它提供了在地图上绘制离散数据点的快速方法(图1)。

图1。这个的热图geobubble图表。

此外,我们还创建了几个特定于应用程序的图表(图2)。您可以从下面下载这些图表,以及本文中使用的MATLAB代码文件交换

图2。自定义图表可在File Exchange上下载。

创建2D散点图

假设我们想要创建一个2D散点图,其中包含对应的最佳拟合线(图3)散射函数以可视化离散对象(x,y)数据点和fitlm函数从统计学和机器学习工具箱™中计算最佳拟合线。

Rng ("default") x = randn(1000, 1);Y = 2*x + 1 + randn(size(x));s = scatter(x, y, 6, "filled", " markfacealpha ", 0.5);M = fitlm(x, y);hold on plot(x, m.Fitted, "LineWidth", 2)

图3。最佳拟合线和底层散点数据。

上面的代码对于静态可视化来说已经足够了。然而,如果应用程序要求数据是动态修改的,那么我们会遇到几个挑战:

  • 如果我们替换XData伊达塔使用与当前数组相同长度的新数组XData,则不会动态更新最佳拟合行(图4)。
    s.XData = s.XData + 4;

图4.更改后,最佳拟合线不会更新XData散点图。

  • 散射对象年代如果它的数据属性(XData伊达塔)设置为比当前数组长或短的数组。
    s.XData = s.XData (1:50 0);

我们可以通过设计一个我们命名的图表来解决这些挑战和其他挑战ScatterFit

构造图表代码:函数还是类?

函数将代码封装为可重用单元,并允许您创建多个图表,而无需复制代码。

函数scatterfit(变长度输入宗量)%确保2或3个输入。narginchk(2、3)%我们支持万博1manbetx调用语法scatterfit(x, y)或% scatterfit(f, x, y),其中f是父图形。开关函数输入变量数案例2 f = gcf;x =变长度输入宗量{1};y =变长度输入宗量{2};否则% case 3 f = varargin{1};x =变长度输入宗量{2};y =变长度输入宗量{3};结束%开关/机箱%创建图表轴和散点图。ax =轴(“父”,f);散射(ax,x,y,6,“填充”%计算并创建最佳拟合直线。M = fitlm(x, y);(ax,“关于”) plot(ax, x, m.Fitted,“线宽”2)持有(ax,“关闭”结束% scatterfit函数

注意,该函数需要两个数据输入(xy).您可以指定图形的父类f(例如,一个图形)作为第一个输入参数。

  • scatterfit (x, y)指定两个数据输入。
  • 散射拟合(f,x,y)指定父图形和数据。

在第一种情况下,函数显示为autoparenting行为-也就是说,图表的图形将自动创建。

使用函数创建图表有一些缺点:

  • 创建图表后,不能修改数据。
  • 要更改图表数据,需要再次调用函数以重新创建图表。
  • 最终用户很难找到可配置的图表参数(例如标签和装饰性图形属性,如颜色、线条样式等)。

将图表实现为类具有该函数提供的代码封装和可重用性的所有好处,同时还允许您修改图表而不需要重新创建它。

选择图表超类:ChartContainerComponentContainer

图表必须实现为处理类,以便可以对其进行就地修改。为了与MATLAB图形对象保持一致,图表应支持万博1manbetx得到/除了标准点表示法之外的属性语法。这两个ChartContainerComponentContainer句柄类和提供支持万博1manbetx得到/语法,这意味着您可以从这些超类之一派生自定义图表。

classdefScatterFit < matlab.ui.componentcontainer.ComponentContainer

因此,对于任何属性,都会自动支持表1所示的语法。万博1manbetx

语法类型 访问 修改
点符号 x = SF.XData; SF.XData=x;
获取/设置 x = get(SF, "XData"); 设置(SF,“扩展数据”,x)

表1。图表属性的访问和修改语法。

根据图表的要求选择一个超类。如果图表不需要交互的用户界面控件,如按钮、下拉菜单和复选框,那么派生图表ChartContainer;否则,使用ComponentContainer.这是因为图表容器超类提供了平铺的布局作为顶级图形对象,该对象可以包含坐标轴,但不包含用户控件。与组件容器关联的顶级图形对象是面板类对象,它同时支持轴和用户控件。万博1manbetx

注意,框架的超类会自动管理图表的生命周期,保证以下行为:

  • 当图表图形被删除时(例如,通过关闭主图形窗口),图表对象将被删除。
  • 当图表对象被删除时(例如,当它超出范围或当它的句柄被删除时),图表图形将被删除。

框架超类支持对所有图表输入参数使用名称-值对。万博1manbetx这意味着在创建图表时不需要指定输入参数,所有输入都是可选的。

写作的图表设置使现代化方法

现在我们需要实现两个特殊的方法,它们都是框架超类所需要的:

  • 设置:创建图表时自动调用
  • 使现代化:当用户修改某些图表属性时自动调用

这些方法必须在图表类中具有保护访问权限,因为它们在超类中具有此属性。

方法(访问=保护)函数设置(obj)结束%设置函数更新(obj)结束%更新结束%方法(访问=受保护)

让我们看看设置方法。这是类定义中初始化图表的函数。开始的一个好地方是从散乱函数设置方法。然后我们做以下修改,以支持所需的图表行为:万博1manbetx

  • 父图形。不同于上面描述的方法散乱函数,如果没有父母亲如果指定了输入,那么我们不会自动为图表创建一个输入。请注意,此行为不同于方便函数,例如情节散射这体现了父母的育儿方式。在设置方法时,我们创建主图形对象(例如轴、面板或布局),并将其父属性指派给父类提供的顶级图形对象。的getLayout方法ChartContainer超类返回对顶层平铺布局的引用。为ComponentContainer我们可以简单地将图形父属性赋给对象本身。

    obj。一个xes = axes("Parent", obj.getLayout()); % ChartContainer obj.Axes = axes("Parent", obj); % ComponentContainer

    如果父母亲指定为输入参数,然后超类将与创建图表期间提供的任何其他名称-值对一起自动设置该参数。超类将指定用户指定的父对象作为顶级图形对象的父对象。

  • 图表图形。我们创建并存储图表所需的任何图形对象。大多数图表都需要一个轴对象以及一些轴内容,如线条或面片对象。在ScatterFit图表,我们需要散射对象和一个对象。

    obj。ScatterSeries =散射(obj。轴,南南);obj。BestFitLine =线(obj。轴,南南);

    注意,我们初始化这些图形时将其数据属性设置为. 如果用户已指定XData和/或伊达塔在构建时,我们延迟更新散点图和最佳拟合线到相应的位置方法(稍后讨论)。这种编码实践确保在指定名称-值对时由用户引起的任何错误都将被捕获并单独处理。

  • 图形配置。我们通过设置任何必需的属性来配置图表图形。例如,我们可以创建标签或标题等注释,设置坐标轴的特定视图,添加网格,或调整线条的颜色、样式或宽度。

只要可行,我们就使用原语对象(表2)来创建图表图形,因为高级方便函数在调用时将重置许多现有的轴属性。然而,这一原则也有例外:内部ScatterFit,我们使用非基本函数“散布”来创建图形对象,因为它支持对单个标记大小和颜色的后续更改(而单线对象不支持)。万博1manbetx

原始图形函数 高级图形函数
情节
表面 冲浪
补丁 填满

表2。基本图形函数和高级图形函数的示例。

稍后我们将回到图表的更新方法。

封装图表数据和图形

在大多数图表中,基础图形包含至少一个轴对象及其内容(例如,线或面对象)或轴对等对象(例如,图例或颜色条)。图表还维护内部数据属性,以确保公共属性正确地呈现给最终用户。我们将底层图形和内部数据存储为私有图表属性。例如,ScatterFitChart维护以下私有财产。

属性(访问=私人)XData属性的内部存储。XData_ =双。空(0,1)% YData属性的内部存储空间。YData=双精度。空(0,1)%指定是否需要计算的逻辑标量。ComputationRequired = false ()结束%属性(访问=私有)

我们使用命名约定扩展数据_指示这是图表数据的私有内部版本。将命名用户可见的相应公共数据属性XData

属性(Access = private, Transient, NonCopyable)%图表轴。轴(1)matlab.graphics.axis.Axes% (x, y)数据的散点序列。ScatterSeries(1,1)matlab.graphics.chart.primitive.Scatter%用于最佳拟合线的线对象。BestFitLine matlab.graphics.primitive.Line (1,1)结束%属性(Access = private, Transient, NonCopyable)

使用私人内部图表数据和图形的属性有三个主要目的。

  • 私有属性限制了底层图形的可见性,隐藏了实现细节并减少了图表API中的视觉混乱。
  • 对低级图形的访问受到限制,减少了绕过API的机会。
  • 图表数据可以很容易地同步(例如,我们需要XData伊达塔的属性ScatterFit有关)。

对于内部图形属性,最好指定瞬态NonCopyable属性。这确保图表对象在被保存到mat文件或复制时能够正确地运行。为了增加健壮性,以及在图表类中工作时在图形属性上启用选项卡补全,我们还实现了属性的验证

提供可视化API

设计图表的主要原因之一是提供方便和直观的API。我们装备ScatterFit图表具有易于识别的属性,使用与现有图形对象属性一致的名称(图5)。

图5。ScatterFit图表API。

用户可以使用表1中的示例语法访问或修改这些属性。关联的图表图形会动态更新以响应属性修改。例如,改变线宽属性更新图表的线宽最适合的直线。

我们使用依赖财产。A.依赖属性的值不是显式存储的,而是派生自类中的其他属性的。在图表中依赖属性依赖于私有属性,如低级图形或内部数据属性。

定义一个依赖属性,我们首先在属性块中用attribute声明它的名称依赖.这表明属性的值依赖于类中的其他属性。

属性(依赖)% x数据图表。XData(:, 1) double {mustBeReal}%图表y-数据。YData(:,1)双{mustBeReal}结束%属性(依赖)

我们还需要通过编写相应的get方法来指定属性如何依赖于其他类属性依赖所有物在ScatterFit图表,XData属性(图表公共接口的一部分)只是底层扩展数据_属性,该属性在内部存储为图表的私有属性。

函数value = get.XData(obj) value = obj. xdata_;结束%。XData

每个数据属性还需要方法。这会将用户指定的值分配给正确的内部图表属性,并触发任何必要的图形更新。

对于ScatterFit图中,我们支持对数据万博1manbetx属性(XData伊达塔).当用户设置(public)XData在图表中,我们填充或截断了相反的(私有)数据属性伊达塔_,这取决于新数据向量比现有数据分别长还是短。回想一下,这如果用户指定了XData当创建图表时。

函数集。XData (obj,值)将图表标记为更新。obj.ComputationRequired=true();决定如何修改图表数据。nX=numel(值);nY=numel(对象数据);如果nX <纽约%如果新的x数据太短,则截断图表的y数据。obj.YData=obj.YData(1:nX);其他的%否则,如果nX >= nY,则填充y-data。obj。ydata_1 (end+1:nX, 1) = NaN;结束%如果%设置内部x-data。obj。XData_ =价值;结束%设置。XData

注意这个图表使现代化方法在用户设置公共属性时自动调用。为了避免不必要和耗时的计算,我们使用一个私有的内部逻辑属性ComputationRequired记录,在方法,无论是否需要完全更新。

不需要新计算的公共API属性在更改时不需要得到相反,我们只需在使现代化方法。通常,公共API属性包括图表的装饰和装饰方面,如颜色、线宽和样式,更新这些属性的成本不高。

ScatterFit图表,使现代化方法中设置新数据所需的代码散射对象,重新计算最佳拟合线并在相应的对象。

函数更新(obj)如果obj。ComputationRequired%用新数据更新散点序列。设置(obj。散射Series,“XData”obj。扩展数据_,“YData”,obj.YData)获得新的最佳拟合直线。m = fitlm (obj。XData_,obj。伊达塔_);%更新最适合的直线图形。[~, posMin] = min(obj.XData_);[~, posMax] = max(obj.XData_);设置(obj。BestFitLine,“XData”obj。扩展数据_([posMin, posMax]),“YData”m.Fitted ([posMin posMax]))把这张图表划干净。obj。ComputationRequired = false ();结束%如果%刷新图表的装饰属性。设置(obj。散射Series,“CData”obj。CData,“SizeData”obj.SizeData)结束%更新

我们执行方法伊达塔以相同的方式,切换X / YData属性。

为了创建适合最终用户的富API,我们实现了一组广泛的公共属性。请注意,标准属性,例如父母亲位置单位,可见都是从超类继承的,不需要在图表中进行额外的实现。

添加图表注释方法

在API中,我们提供了熟悉且易于使用的注释图表的方法。这些注释的方法过载(具有同名)相应的高级图形装饰功能。要使用这些方法,用户提供对图表的引用作为第一个输入参数,然后是装饰函数的输入。

包含(科幻小说,“x数据”“字形大小”, 12)

如果装万博1manbetx饰功能支持,还可以使用输出调用注释方法,以返回对图形对象的引用,以便进一步自定义。例如包含函数返回一个文本对象。

xl=xlabel(SF,“x数据”);

为了支万博1manbetx持名称-值对和输出参数,可以方便地使用单元格数组变长度输入宗量varargout.的语法变长度输入宗量{:}生成以逗号分隔的输入参数列表。我们使用调用者来确定输出的数量nargout.为了处理可变数量的输出参数(对于这些方法,通常是0或1),我们使用语法[varargout{1:nargout}]调用装饰函数时。典型的标注方法结构如下:

函数Varargout = xlabel(obj, varargin) [Varargout {1:nargout}] = xlabel(obj. Varargout, varargin)轴,变长度输入宗量{:});结束%xlabel

在图表中包括交互式控件

除了图表的API之外,我们还可以包括为最终用户提供图表交互和修改选项的控件(图6)。

图6。交互式图表控件的例子。

我们在图表中初始化这些控件设置方法,使用用于应用构建的组件.每个控件有一个回调函数,实现为私有方法。这个方法有三个输入参数:

  • 图表对象。
  • 提及来源对象(负责触发回调的对象)——在本例中,源对象是相应的用户控件。
  • 事件数据。当用户与控件交互时,这个对象会被MATLAB自动传递给回调函数。事件数据对象包含有关事件的附加信息。

例如,考虑控制最佳拟合行可见性的复选框的回调函数。此函数根据复选框的值切换底层行对象的可见性。

函数切换线条可见性(obj,s,~)切换最佳匹配线的可见性。obj.BestFitLine.Visible = s.Value;结束% toggleLineVisibility

每个控件的值必须与相应的图表属性同步。为了实现这一点,我们用依赖属性,然后实现其得到方法。请注意,除了更新内部图形对象外方法也必须更新控件对象的值。

与最佳匹配行可见性相对应的代码如下所示。为确保属性和复选框值之间的兼容性,我们将属性转换为matlab.lang.OnOffSwitchState类型。此类型支持表示的任何万博1manbetx兼容语法真正的值,例如10,以及“关于”“关闭”

属性(依赖)%最佳拟合线的可见性。LineVisible matlab.lang.OnOffSwitchState (1,1)结束%属性(依赖)函数value = get.LineVisible(obj) value = obj. bestfitline . visible;结束%获取.LineVisible函数集。LineVisible (obj,值)%更新属性。obj.BestFitLine.Visible =价值;%更新复选框。obj.BestFitLineCheckBox.Value =价值;结束%设置。行可见

将图表与应用程序设计器集成

截至MATLAB R2021a,图表开发使用ComponentContainer超类可以与App Designer集成(图7)使用App Designer,你可以通过创建元数据与最终用户分享图表。安装的图表将出现在用户的应用设计器组件库中,它可以像其他组件一样在画布中交互使用。

图7。与App Designer集成的自定义图表。

总结

在本文中,我们描述了定制图表实现的设计模式和最佳实践,使用ScatterFit以图表为例。许多常见的可视化任务,特别是那些需要动态图形的任务,可以使用适当的图表来执行。设计和创建图表需要预先投入开发时间和精力,但图表可以从本质上简化许多可视化工作流程。

2021年出版的

s manbetx 845使用的产品

了解更多

    查看相关功能的文章