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

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

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

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

本文使用包含最佳拟合线的散点图作为主要示例,逐步指导使用MATLAB面向对象编程创建和实现自定义图表。主题包括:

  • 编写标准图表模板
  • 写一个构造方法
  • 封装图表数据和图形使用私人性质
  • 使用。创建高级可视化API依赖性质
  • 管理图表生命周期
  • 使用继承来简化其他图表的开发

更新:从R2019b开始,MATLAB提供了一个用于开发自定义图表类的面向对象框架。这个框架补充了本文中描述的方法,并简化了一些低级编程细节。看到图发展概述了解更多。

图表的例子

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

图1热图土气泡图表。

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

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

创建2D散点图:功能还是图表?

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

RNG默认x = randn(1000,1);y = x + 1 + 2 * randn(大小(x));s =散射(x, y 6‘满’,‘MarkerFaceAlpha’,0.5);m = fitlm (x, y);抓住情节(x, m.Fitted“线宽”,2)

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

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

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

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

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

我们可以通过设计一个图表来解决这些挑战,散乱

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

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

函数scatterfit(变长度输入宗量)%确保2或3个输入。narginchk(2,3)%我们支持金宝app调用语法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=轴(“父”散射(ax,x,y,6,“填充”%计算并创建最佳拟合直线。m = fitlm (x, y);(ax,“上”)情节(ax, x, m。合身,“线宽”2)持有(ax,“关闭”结束%散射拟合函数

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

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

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

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

  • 创建图表后,不能修改数据。
  • 要更改图表数据,需要再次调用函数重新创建图表,并指定不同的数据输入。
  • 最终用户很难定位可配置的图表参数(如注释和散点/线属性)。

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

定义一个图表

我们将把这个图表作为一个整体来实现处理类,以便与MATLAB图形对象保持一致,从而可以在适当的位置修改图表。我们支持点表示法和金宝app获取/设置图表属性的语法。为了达到这个目的,我们推导散乱从预定义的matlab.mixin.SetGet类,它本身就是处理

classdefScatterFit < matlab.mixin.SetGet

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

语法类型

点符号

获取/设置

访问

x = SF.XData;

x=get(SF,'XData');

修改

SF.XData=x;

集(科幻,XData, x)

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

编写图表的构造函数方法

构造函数方法是创建图表对象的类定义中的函数。开始的一个好地方是从scatterfit函数到我们的图表构造器。然后我们做以下修改,以支持所需的图表行为:金宝app

  • 输入参数。我们支金宝app持对所有图表输入参数使用名称-值对,使用瓦拉金.这意味着不需要指定输入参数,所有输入都是可选的。
    函数obj = ScatterFit(变长度输入宗量)
  • 父图形.不像功能性方法,如果没有父母亲已指定图表构造的输入,则我们不会自动为图表创建输入。相反,我们会创建一个斧头具有空父母亲
    obj。轴= axes('Parent',[]);

    请注意,此行为不同于便利功能,例如情节散射这体现了父母的育儿方式。如果用户已指定父母亲作为输入参数,之后将在构造函数中设置。

  • 图表图形。我们创建并存储图表所需的任何图形对象。大多数图表都需要一个轴对象以及一些轴内容,如线条或面片对象。在散乱图表,我们需要散射对象和一个对象。
    obj.ScatterSeries=散射(obj.轴,NaN,NaN);obj.BestFitLine=直线(obj.轴,NaN,NaN);
  • 图形配置。我们通过设置任何必需的属性来配置图表图形。例如,我们可以创建标签或标题等注释,设置坐标轴的特定视图,添加网格,或调整线条的颜色、样式或宽度。
  • 指定的输入。我们设置最终用户提供的任何名称-值对参数。由于图表是由matlab.mixin.SetGet,这特别容易做到:
    如果~ isempty(变长度输入宗量)(obj,变长度输入宗量{:});结束

    这是数据属性(XData伊达塔),前提是用户已将这些作为名称-值对输入参数提供。我们还注意到,这种编码实践确保了在指定名称-值对时由用户引起的任何错误都将由图表的属性捕获和处理方法(稍后讨论)。

只要可行,我们就使用原语对象来创建图表图形,因为高级方便函数在调用时将重置许多现有的轴属性(表2)散乱,我们使用散射函数来创建散射图形对象,因为它支持对单个标记的大小和颜色的后续更改。金宝app

原始图形函数


表面
色斑

高级图形函数

情节
冲浪
填满

表2.基本和高级图形功能示例。

封装图表数据和图形

在大多数图表中,基础图形包含至少一个轴对象及其内容(例如,线或面对象)或轴对等对象(例如,图例或颜色条)。图表还保留了内部数据属性,以确保公共属性彼此一致。我们将底层图形和内部数据存储为私有图表属性。例如,散乱图保留下列私人物业:

性质(Access = private) XData_ YData_ Axes ScatterSeries BestFitLine Legend结束

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

使用私人物业有三个主要用途:

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

提供可视化API

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

图5。散乱图表API。

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

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

定义一个依赖属性时,我们首先在性质块的属性依赖.这表明属性的值依赖于类中的其他属性。

性质(依赖)XData YData结束

我们还需要通过编写相应的类属性来指定该属性如何依赖于其他类属性得到方法。该方法返回单个输出参数—即依赖财产。在散乱图表XData属性(图表公共界面的一部分)只是基础属性扩展数据_属性,它在内部存储为私人图表的属性。

函数x = getxdata (obj) x = obj. xdata_;结束

我们写了一篇文章方法用于每个可配置图表属性。此方法将用户指定的值分配给正确的内部图表属性,在必要时触发图形更新。

对于散乱图中,我们支持对数据金宝app属性(XData伊达塔).当用户设置(public)XData在图表中,我们填充或截断了相反的(私有)数据属性伊达塔_,这取决于新数据向量比现有数据长还是短。回想一下方法将由构造函数调用,如果用户已指定XData当创建图表时。

函数set.XData(obj,x)%执行基本验证。validateattributes(x,{'double'},{'real','vector'},'ScatterFit/set.XData','x-data')决定如何修改图表数据。nX =元素个数(x);纽约=元素个数(obj.YData_);如果nX如果新的x数据太短…%……然后截断图表的y数据。obj.YData=obj.YData(1:nX);其他的%否则,如果nX >= nY,则填充y-data。obj.YData_ (+ 1: nX) =南;结束%如果%设置内部x-data。obj。XData_ = x (:);%更新图表图形。更新(obj);结束%设置。XData

我们通过调用一个单独的使现代化方法。方法中包含设置新数据所需的代码散射对象,重新计算最佳拟合线,并在相应的对象。

函数更新(obj)%用新数据更新散点序列。集(obj.ScatterSeries, XData obj.XData_,…YData, obj.YData_);获得新的最佳拟合直线。m=fitlm(对象扩展数据,对象扩展数据);%更新最适合的直线图形。[~, posMin] = min(obj.XData_);[~, posMax] = max(obj.XData_);集(obj.BestFitLine, XData obj。XData_ ([posMin posMax]),……YData, m。安装([posMin posMax]));结束%更新

我们执行方法伊达塔以同样的方式,切换X / YData属性。的使现代化方法也可以从方法伊达塔

为了创建适合最终用户的富API,我们实现了一组广泛的依赖属性。对于每个图表,我们建议至少包含表3中所示的属性。

财产

父母亲

意图

在图形、面板或其他容器图形之间移动图表


位置
单位
OuterPosition
ActivePositionProperty

调整图表


可见

切换屏幕图表可见性

表3。推荐依赖属性。

请注意,在大多数情况下,这些属性直接映射到基础图表轴得到研究方法父母亲属性映射图表对象的父母亲轴的父母亲

函数value = get.Parent(obj) value = obj. axes . parent;结束函数set.Parent(obj,value)obj.Axes.Parent=值;结束

我们通过定义额外的公共接口属性来实现可视化设置的控制,每个属性映射到图表维护的特定低级图形对象。在这个类别中散乱Chart支金宝app持各种与行相关的属性,例如线型线宽,LineColor与最佳拟合直线有关的。例如,图表对象的LineColor属性映射到线对象的颜色财产。

函数value = get.LineColor(obj) value = obj. bestfitline . color;结束函数set.LineColor(obj,value) obj. bestfitline . color = value;结束

此类别中的典型图表属性包括:

  • 视图相关属性—例如,轴看法XLim,YLim
  • 注释,例如,轴的包含伊拉贝尔,标题
  • 化妆品属性——例如,颜色、线宽和样式;网格;透明度;和照明

管理图表生命周期

散乱Chart与它的底层轴对象密切相关,后者被存储为图表的一个轴对象私人属性。为了正确管理图表的生命周期,我们需要保证两种行为:

  • 删除坐标轴(例如,关闭主图形窗口)将删除图表。如果我们不能保证这一点,那么修改图表的数据属性将导致MATLAB尝试对已删除的图形对象进行更新,从而导致错误。
  • 删除图表(例如,当图表超出范围或显式删除其句柄时)将删除轴。如果我们不保证这一点,那么在删除图表时,我们会留下静态的图形残余。

MATLAB中的每个图形对象都有一个名为DeleteFcn,当图形对象超出范围时自动调用的回调函数。因此,我们可以通过设置轴的DeleteFcn在图表构造器中。

obj.Axes.DeleteFcn=@obj.onAxesDeleted;

在这里onAxesDeleted是一个私人类方法,它只是图表析构函数方法的包装器。我们记得每一个处理类附带了一个可自定义的析构函数方法。当对象超出范围时调用析构函数方法。

函数onAxesDeleted (obj, ~, ~)删除(obj);结束

我们通过编写自定义图表析构函数来确保第二个要求。在图表销毁时,我们删除图表的轴。

函数删除(obj)删除(obj.Axes);结束

实现这两个需求将使图表对象具有与其底层轴相同的生命周期(图6)。

图6。管理图表和坐标轴生命周期。

简化额外图表的开发

一旦我们写了几个图表,就很容易识别代码的相似和重复部分。我们可以通过将通用代码集中在超类中来加快编写附加图表的过程。每个新图表都可以从这个超类派生出来,使我们能够专注于该特定图表的实现细节,并减少重复编码的需要。

我们的超类(命名图表)其结构如下:

  • 图表来自matlab.mixin.SetGet
  • 图表实现六核依赖性质父母亲位置单位OuterPositionActivePositionProperty,可见
  • 图表有一个受保护的财产(底层图形peer)。
  • 图表构造函数创建对等轴对象并设置轴'DeleteFcn随着受保护的方法onAxesDeleted.接着,该方法删除图表对象。
  • 图表析构函数删除轴对象。

注意使用超类图表可能不适用于所有的图表。例如,维护多个轴的图表需要对上面描述的体系结构进行一些更改。我们可以使用uipanel而不是将轴对象作为图表的基础图形对等对象,并在面板内创建多个轴。

总结

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

2018年出版


下载188bet金宝搏产品使用

了解更多

    查看相关功能的文章