技术文章和通讯

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

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


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

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

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

本文提供了一个逐步的指南,具有设计模式和最佳实践,以使用此框架创建和实现自定义图表。该步骤用包含最佳配合线的示例散点图进行说明。主题包括:

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

图表的例子

几种图表在MATLAB中可用,包括热线图图,它可视化地显示了覆盖在彩色网格正方形上的矩阵值Geobbble.图,它提供了在地图上绘制离散数据点的快速方法(图1)。

图1所示。的热线图Geobbble.图表。

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

图2.可在文件交换上下载的自定义图表。

创建2D散点图

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

RNG(“默认”)X = RANDN(1000,1);Y = 2 * x + 1 + RANDN(尺寸(x));s =散射(x,y,6,“填充”,“markerfacealpha”,0.5);M = fitlm(x, y);持有绘图(x,m.fited,“linewidth”,2)

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

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

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

图4。更改后,最佳拟合行不更新XData散点图的一部分。

  • 分散对象年代发出警告,如果其数据属性中的任一个(XData或者YData)设置为比当前数组长或短的数组。
    s、 扩展数据=s.XData(1:500);

我们可以通过设计一个图表来解决这些挑战和其他挑战散点图

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

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

功能散射(varargin)%确保2或3个输入。Narginchk(2,3)%我们支持金宝app调用语法scatterfit(x, y)或% scatterfit(f, x, y),其中f是父图形。转变输入参数个数情况下2f=gcf;x=varargin{1};y=varargin{2};否则% case 3 f = varargin{1};x =变长度输入宗量{2};y =变长度输入宗量{3};结束% switch / case%创建图表轴和散点图。斧头=轴(“父母”f);离散(ax, x, y, 6,“填充”%计算并创建最佳拟合线。M = fitlm(x, y);(ax,“上”)绘图(斧头,x,m.fited,“行宽”,2)持有(斧头,“离开”结束%staptlefit函数

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

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

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

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

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

作为一个类实现图表具有代码封装和可重用性的所有优势,即函数提供的同时也让您在不需要重建图表的情况下修改图表。

选择图表超类:ChartContainer或者ComponentContainer.

图表必须实现为处理类,以便可以在适当的地方修改它。为了与MATLAB图形对象保持一致,图表应该支持金宝app收到/除标准点表示法外,属性的语法。两个都ChartContainerComponentContainer.句柄类和提供支持金宝app收到/语法,这意味着您可以从这些超类之一派生自定义图表。

classdefScatterFit < matlab.ui.componentcontainer.ComponentContainer

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

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

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

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

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

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

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

写图表设置更新方法

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

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

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

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

让我们看一下设置方法。这是类定义中初始化图表的函数。开始的一个好地方是从scatterfit函数到设置方法。然后,我们进行以下修改以支持所需的图表行为:金宝app

  • 父图形。与上面描述的方法不同scatterfit函数,如果否如果指定了输入,则我们不会自动为图表创建一个输入。请注意,这种行为与方便函数不同,例如情节分散,显示自动授权。在设置方法时,我们创建主图形对象(例如轴、面板或布局),并将其父属性指派给父类提供的顶级图形对象。的getLayout的方法ChartContainer超类返回对顶层平铺布局的引用。对于ComponentContainer.在图表中,我们可以简单地将图形父属性指定给对象本身。

    obj.Axes=轴(“父对象”,obj.getLayout());%ChartContainer obj.Axes=轴(“父对象”,obj);%ComponentContainer

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

  • 图表图形。我们创建并存储图表所需的任何图形对象。大多数图表将需要一个轴对象和一些轴内容,如线或补丁对象。在里面散点图图表,我们需要一个分散对象和目的。

    obj.ScatterSeries=散射(obj.Axes,NaN,NaN);obj.BestFitLine=直线(obj.Axes,NaN,NaN);

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

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

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

基本图形功能 高级图形功能
线 情节
表面 冲浪
修补

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

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

封装图表数据和图形

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

特性(访问=私人)XData属性的内部存储。XData_ =双。空(0,1)ydata属性的%内部存储。YData_ =双。空(0,1)%指定是否需要计算的逻辑标量。ComputationRequired=false()结束%属性(Access = private)

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

特性(Access = private, Transient, NonCopyable)%的图表坐标轴。轴(1,1)matlab.graphics.Axis.axes(x,y)数据的%散射系列。ScatterSeries matlab.graphics.chart.primitive.Scatter (1,1)% Line对象的最佳拟合线。BestFitLine matlab.graphics.primitive.Line (1,1)结束%属性(Access =私有,暂行,不可能)

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

  • 私有属性限制了底层图形的可见性,隐藏了实现细节并减少了图表API中的视觉混乱。
  • 限制对低级图形的访问,减少绕过API的可能性。
  • 图表数据可以轻松同步(例如,我们需要XDataYData特性散点图相关)。

对于内部图形属性,最好指定瞬态NonCopyable属性。这些属性确保图表对象在保存到MAT文件或复制到MAT文件时行为正确。为了增强健壮性,以及在图表类中工作时启用图形属性的制表符完成,我们还实现了财产验证

提供可视化API

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

图5。散点图图表的API。

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

我们使用依靠的属性。一个依靠的属性是其中值未明确存储的属性,而是源自类中的其他属性。在图表中,依靠的属性取决于私有属性,例如低级图形或内部数据属性。

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

特性(依情况而定)% x数据图表。扩展数据(:,1)双{mustBeReal}%图表数据。YData(:, 1) double {mustBeReal}结束%属性(依赖)

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

功能value=get.XData(obj)value=obj.XData;结束%。XData

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

散点图在图表中,我们支持对金宝app数据属性进行动态修改(包括长度更改)(XDataYData)。当用户设置(公共)时XData在图表中,我们填充或截断了相反的(私有)数据属性YData_,这取决于新数据向量分别比现有数据长还是短方法将在构造时调用,如果用户已指定XData当创建图表时。

功能集。XData (obj,值)将图表标记为更新。obj.需要计算= true();决定如何修改图表数据。nX =元素个数(价值);纽约=元素个数(obj.YData_);如果nx %如果新的x数据太短,则截断图表的y数据。obj。YData_ = obj.YData_ (1: nX);其他的%否则,如果nX >= nY,则填充y-data。对象YData_u2;(end+1:nX,1)=NaN;结束%如果%设置内部x-data。obj.XData=值;结束%set.xdata.

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

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

在里面散点图图表,呢更新方法包含设置新数据所需的代码分散对象,重新计算最佳拟合线并在相应的目的。

功能更新(obj)如果obj.需要计算%使用新数据更新散布序列。设置(obj。分散Series,“xdata”obj。XData_,“ydata”obj.YData_)获得新的最佳拟合直线。m = fitlm (obj。XData_,obj。YData_);%更新最佳拟合线图形。[~,posMin]=min(对象扩展数据);[~,posMax]=max(对象扩展数据);set(对象最佳拟合线,“xdata”obj。XData_([posMin, posMax]),“ydata”m.Fitted ([posMin posMax]))把这张图表划干净。obj.ComputationRequired=false();结束%如果刷新图表的装饰属性。设置(obj。分散Series,“CData”obj。CData,“SizeData”obj.SizeData)结束%更新

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

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

添加图表注释方法

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

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

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

xl =包含(科幻小说,“x数据”);

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

功能varargout=xlabel(obj,varargin)[varargout{1:nargout}]=xlabel(obj.Axes,varargin{:});结束%包含

在图表中包括交互式控件

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

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

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

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

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

功能togglelinevisibility(obj,s,〜)%togglelinevisibility切换最佳拟合线的可见性。obj.BestFitLine.Visible = s.Value;结束% toggleLineVisibility

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

与最佳拟合行可见性对应的代码如下所示。为了确保属性值和复选框值之间的兼容性,我们将属性转换为matlab.lang.OnOffSwitchState类型。此类型支持表示的任何金宝app兼容语法真的值,如10,以及“上”“离开”

特性(依情况而定)%最佳拟合线的能见度。LineVisible(1,1)matlab.lang.OnOffSwitchState结束%属性(依赖)功能value = get.linevisible(obj)值= obj.bestfitline.visible;结束%。行看得见的功能集。LineVisible (obj,值)%更新属性。obj.BestFitLine.Visible =价值;%更新复选框。obj.BestFitLineCheckBox.Value =价值;结束%设置。行看得见的

将图表与App Designer集成

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

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

总结

在本文中,我们描述了定制图表实现的设计模式和最佳实践,使用散点图以图表为例。许多常见的可视化任务,尤其是需要动态图形的任务,可以使用适当的图表来执行。设计和创建图表需要前期开发时间和精力,但图表可以大大简化许多可视化工作流。

2021年出版的

下载188bet金宝搏产品使用

了解更多

    查看相关功能的文章