技术文章及通讯

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

作者:Ken Deeley和David Sampson, MathWorks


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

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

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

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

  • 编写标准图表模板
  • 写图表设置而且更新方法
  • 封装数据和图形
  • 为最终用户提供高级API
  • 包括交互控制

图表的例子

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

图1。热图和地球气泡图。

图1。的的热图而且geobubble图表。

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

图2。自定义图表可从文件交换下载。

图2。自定义图表可从文件交换下载。

创建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", "MarkerFaceAlpha", 0.5);M = fitlm(x, y);按住plot(x, m. fitting,“LineWidth”,2)
图3。最佳拟合线和底层分散数据。

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

上面的代码足以实现静态可视化。但是,如果应用程序要求数据是可动态修改的,那么我们就会遇到几个挑战:

  • 如果我们替换XDataYData使用与当前数组长度相同的新数组XData时,不动态更新最佳拟合行(图4)。
    s.XData = s.XData + 4;
图4。在更改散点图的XData后,不更新最佳拟合线。

图4。属性后,不更新最佳拟合行XData散点图。

  • 散射对象年代发出警告,并且不执行图形更新,如果其数据属性(XDataYData)被设置为一个比当前数组长或短的数组。
    s.XData = s.XData(1:500);

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

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

函数将代码封装为可重用单元,允许您在不复制代码的情况下创建多个图表。

函数scatterfit(变长度输入宗量)保证2或3个输入。narginchk(2、3)我们支持调金宝app用语法scatterfit(x, y)或% scatterfit(f, x, y),其中f是父图形。开关输入参数个数情况下2 f = gcf;X = varargin{1};Y = varargin{2};否则% case 3 f = varargin{1};X = varargin{2};Y = varargin{3};结束% switch / case创建图表轴和散点图。Ax =轴(“父”f);散射(ax, x, y, 6,“填充”计算并创建最合适的直线。。M = fitlm(x, y);(ax,“上”) plot(ax, x, m.拟合,“线宽”, 2) hold(ax,“关闭”结束%散射拟合函数

注意,这个函数需要两个数据输入(x而且y).您可以指定图形父f(例如,一个图形)作为第一个输入参数。

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

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

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

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

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

选择图表超类:ChartContainerComponentContainer?

图表必须实现为处理类,以便可以对其进行适当的修改。为了与MATLAB图形对象的一致性,图表应支持金宝app得到/标准点表示法之外的属性语法。这两个ChartContainer而且ComponentContainer处理类和提供支持的金宝app得到/语法,这意味着您可以从这些超类之一派生自定义图表。

classdefScatterFit < matlab.ui.componentcontainer.ComponentContainer

因此,对于任何属性,都自动支持表1中所示的语法。金宝app

语法类型 访问 修改
点符号 x = SF.XData; 科幻小说。XData = x;
获取/设置 x = get(SF, "XData"); set(SF, "XData", x)

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

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

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

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

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

编写图表设置而且更新方法

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

  • 设置:在创建图表时自动调用
  • 更新:当用户修改某些图表属性时自动调用

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

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

我们来看看设置方法。这是我们在类定义中初始化图表的函数。方法中复制代码是一个很好的开始scatterfit函数设置方法。然后我们进行以下修改以支持所需的图表行为:金宝app

  • 父图形。方法中所描述的方法不同scatterfit函数,如果没有如果指定了输入,则不会自动为图表创建输入。注意,这种行为不同于方便函数,例如情节而且散射,它们具有自动养育功能。在设置方法后,我们创建主图形对象(如轴、面板或布局),并将其父属性分配给超类提供的顶级图形对象。的getLayout方法ChartContainer父类返回对顶级平铺布局的引用。为ComponentContainer图表,我们可以简单地将图形父属性分配给对象本身。

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

    如果被指定为输入参数,那么它将由超类自动设置,以及在图表创建期间提供的任何其他名称-值对。超类将用户指定的父类赋值为顶级图形对象的父类。

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

    obj。散射系列=散射(obj.)轴,NaN, NaN);obj。BestFitLine = line(obj.)轴,NaN, NaN);

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

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

在实际情况下,我们使用原语对象(表2)来创建图表图形,因为高级方便函数在调用时将重置许多现有的axis属性。然而,这一原则也有例外:在内部ScatterFit,我们使用非原语函数scatter来创建图形对象,因为它支持对单个标记大小和颜色的后续更改(而单行对象则不支持)。金宝app

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

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

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

封装图表数据和图形

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

属性(Access = private)% XData属性的内部存储。XData_ = double。空(0,1)% YData属性的内部存储。YData_ = double。空(0,1)指定是否需要计算的逻辑标量。ComputationRequired = false()结束% properties (Access = private)

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

属性(访问=私有,瞬时,不可复制)%图表轴。matlab.graphics.axis.Axes (1,1% (x, y)数据的散射序列。散射系列(1,1)matlab.graphics.chart.primitive.Scatter最适合的行对象。BestFitLine(1,1) matlab.graphics.primitive.Line结束%属性(访问=私有,瞬态,不可复制)

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

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

对于内部图形属性,最好指定瞬态而且NonCopyable属性。这确保图表对象在保存到mat文件或复制时行为正确。为了获得额外的健壮性,并在图表类中工作时在图形属性上启用选项卡补全,我们还实现了属性的验证

提供可视化API

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

图5。ScatterFit图表API。

图5。ScatterFit图表的API。

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

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

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

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

我们还需要通过编写相应的get方法来指定该属性如何依赖于其他类属性。方法的值返回一个输出参数依赖财产。在ScatterFit图表,XData图表的属性(图表公共接口的一部分)只是底层的XData_属性,该属性在内部存储为图表的私有属性。

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

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

ScatterFit图表中,我们支持动态金宝app修改数据属性(包括长度更改)(XData而且YData).当用户设置(public)XData对于图表,我们可以填充或截断相反的(私有)数据属性YData_,取决于新数据向量是否分别比现有数据长或短。回想一下方法将在构造时调用XData当创建图表时。

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

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

更改时不需要新的计算的公共API属性不需要得到方法。方法的末尾刷新相应的内部对象更新方法。通常,公共API属性包括图表的装饰和修饰方面,如颜色、线宽和样式,更新这些属性的成本很低。

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

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

我们实现方法YData以同样的方式,切换的角色X / YData属性。

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

添加图表标注方法

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

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

如果由金宝app装饰函数支持,还可以使用输出调用注释方法,以返回对图形对象的引用,以便进一步定制。例如,包含函数返回一个文本对象。

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

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

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

在图表中包含交互式控件

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

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

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

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

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

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

函数toggleLineVisibility(obj, s, ~)TOGGLELINEVISIBILITY切换最合适行的可见性。obj.BestFitLine.Visible = s.Value;结束% toggleLineVisibility

每个控件的值必须与相应的图表属性同步。为实现这一点,我们为chart属性配备依赖属性,然后实现它得到而且方法。注意,除了更新内部图形对象之外,方法还必须更新控件对象的值。

与最佳拟合行可见性对应的代码如下所示。为确保属性值和复选框值之间的兼容性,将属性转换为matlab.lang.OnOffSwitchState类型。此类型支持用于表示的金宝app任何兼容语法真正的而且值,例如1而且0,以及“上”而且“关闭”

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

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

截至MATLAB R2021a,图表开发使用ComponentContainer超类可以是与应用程序设计器集成在App Designer中,你可以通过创建元数据与最终用户共享图表。安装的图表将出现在用户的App Designer Component Library中,在那里它可以像任何其他组件一样在画布中交互使用。

图7。自定义图表集成的应用程序设计器。

图7。自定义图表集成的应用程序设计器。

总结

方法描述了实现自定义图表的设计模式和最佳实践ScatterFit以图表为例。许多常见的可视化任务,特别是那些需要动态图形的任务,都可以使用适当的图表来执行。设计和创建图表需要预先的开发时间和精力,但图表可以大大简化许多可视化工作流程。

2021年出版的

下载188bet金宝搏产品使用

了解更多

    查看相关功能的文章