第12章 高级图形设计

    通过第5章的学习,读者已经掌握了使用MATLAB 7.0的高级绘图函数和工具来创建一幅二维或三维图形的方法。而当用户想定制图形,并且要打算对图形的每个可能方面进行控制时,那么句柄图形会为用户提供强有力的工具。另外,在很多场合,用户需要编写一个可以多次反复使用的实用函数,这时候菜单、按钮、文本框作为输入方法就显得更有意义。所以,图形界面的实现对很多用户来讲是必不可少的一部分。

    本章的前3节内容将系统地阐述句柄图形体系、图形对象、属性和操作方法,后面两节叙述图形用户界面地设计原则以及操作步骤,其中包括如何使用编程语言来实现GUI。最后,给出一个实际的例子,帮助读者理解图形界面制作的整个过程。

    12.1 句柄图形

    句柄图形(Handle Graphics)是一种面向对象的绘图系统。该系统提供创建计算机图形所必须的各种软件。它所支持的指令,可以直接创建线、文字、网格、面以及图形用户界面。在第4章所介绍的各种MATLAB 7.0高层(High-level)图形指令(如plot、mesh)都是以句柄图形软件为基础写成的。也正是这个原因,句柄图形也被称为低层(Low-level)图形。

    12.1.1 图形对象、图像句柄和句柄图形树结构

    1.图形对象

    MATLAB 7.0把用于数据可视和界面制作的基本绘图要素称为句柄图形对象(Handle graphics object)。构成MATLAB 7.0句柄图形体系的12个图形对象如图12-1所示。每个图形对象可以被独立地操作。

    在MATLAB 7.0中生成的每个具体图形,由若干不同对象构成。每个具体图形不必包含全部对象,但每个图形必须具备根屏幕和图形窗(简称图)。

    2.句柄

    每个具体对象都有一个惟一的身份(Identifier),即句柄(Handle)。句柄是存取图形对象惟一规范的识别符。不同对象的句柄不可能重复和混淆。

    每台计算机的根对象只有一个,即屏幕。它的句柄总是数字0。而简称为图的图形窗(Figure Windows)的句柄总是正整数,它用来标识图形窗的序号。除了以上两种对象外,其余对象的句柄都是双精度浮点数。


    注意:对根屏幕、图对象来说,数字可直接作为调用对象的句柄。但不要企图通过直接输入浮点数来作为其他对象的句柄;这些对象的句柄只能由相关指令运作得到。


    3.句柄图形的结构

    在句柄图形体系中,各图形对象并非平等,它们之间的关系如图12-1所示。

    alt

    图12-1 句柄图形体系结构图

    · 处于树结构最高层的图形对象是根,它是所有其他图形对象的“父(Parent)”。图对象是根对象的直接“子”对象。理论上,一个根屏幕的独立图形窗数量不限。

    · 图形窗有3各不同类型的“子”,即用户接口对象、轴以及隐式的注释轴。

    · 轴有3种不同类别的“子”,即核心对象、绘图对象以及组对象。而注释轴有一个“子”,即注释对象。

    12.1.2 图形对象种类

    在MATLAB 7.0中,有两种基本的图形对象类型。

    · 核心图形对象(Core graphics objects):用于实现高层图形指令,是组合对象的基本元素,示例图形如图12-2所示;

    · 组合对象(Composite objects):由核心图形对象组成,用来实现特殊的绘图功能。用来构成图形对象的3种子对象。

    下面分别介绍图12-1中示的几种低层的对象。

    · 绘图对象:由基本图形对象组成,支持在本层次上进行对象属性的修改;

    · 注释对象:存在于不同于其他任何图形对象的层上;

    · 组对象:生成一组对象,它们可以完成某种特殊的功能,但在外面看来象单一对象一样;

    · 用户接口对象:主要用来构建用户图形界面接口。

    根对象(root):位于体系结构图中的最顶端是root根对象。根对象相当于计算机的显示屏。一个GUI中仅有一个根对象,其他所有对象都是它的“子”。用户是不能创建根对象的,当用户启动MATLAB 7.0时根对象就已经存在。用户通过设置root的属性值改变图形的显示效果。

    图形窗口(figure):figure对象是位于root显示屏上的单独窗口,MATLAB 7.0在这个窗口中显示图形。MATLAB 7.0对figure对象的数量没有限制,用户以创建任意多个图形窗口。所有的figure对象都是root的子对象,而除了root以外的所有其他对象都是figure的子对象。如果调用绘图函数时图形窗口不存在,所有的绘图函数都能够自动创建一个figure对象。如果在root中有许多个figure对象,其中有一个将被定义为“当前”窗口作为图形输出的目标。

    alt

    图12-2 核心图形对象示例

    核心图形对象(core object):包含了最基本的绘图元素,例如坐标轴对象、图像对象、灯光对象、线条对象、面片对象、矩形对象、曲面对象以及文本对象。

    坐标轴对象在图形窗口中定义了一个区域并使用这个区域为自己的子对象定向。坐标轴对象是figure的子对象,也是图像对象、灯光对象、线条对象、面片对象、矩形对象、曲面对象以及文本对象的父对象。如果创建图形时不存在坐标轴对象,那么所有的绘图函数将会自动创建一个坐标轴对象;如果存在多个坐标轴对象,则指定其中一个为“当前”坐标轴对象。

    图像对象是由一个数据矩阵和一个颜色映射矩阵(可不存在)组成。图像可以分成3种类型,即真彩、索引和灰度,不同图像类型之间的区别在于数据矩阵和像素颜色的关系不同。由于图像是一个严格的二维图像,因而用户仅可以在二维视图中观察图像。

    灯光对象定义光源,该光源对坐标轴中所有的面片对象和曲面对象产生影响。虽然用户看不到光源,但是可以通过设置光源的属性来控制光源。

    线条对象是创建大多数二维和三维绘图的基本图形元素。高级函数plot、plot3和loglog等可以用来创建线条对象。坐标轴对象的坐标系统决定了线条对象的位置和方向。

    面片对象由有边缘的多边形构成。单个的面片可以有许多个小面,每个小面可以独立地使用均匀或插值方法着色。

    矩形对象是一个二维填充区域,其形状可以是矩形或椭圆。

    曲面对象是矩阵数据的三维描述图形,使用矩阵元素作为点到x-y平面的高度,通过调用绘图函数绘制而成。曲面图形由多个四边形组成,四边形到顶点由矩阵数据决定。

    文本对象即字符串,由坐标轴对象的坐标系统来定义字符串的位置。在图12-2中展示了各种核心图形对象。

    12.1.3 图形对象属性概念

    通过修改图形对象的属性可以控制对象外观、行为等许多特征。属性不但包括对象的一般信息,而且包括特殊类型对象独一无二的信息。例如,用户可以从任意给出的figure对象中获得以下信息:窗口中最后一次输入的标识符、指针的位置以及最近一次选择的菜单项。MATLAB 7.0将所有图形信息组织在一个层次表中,并将这些信息存储在相应的属性中。例如,root属性表包括当前图形窗口的句柄和当前的指针位置;figure属性包括其子对象的类型列表,同时实时跟踪窗口中发生的事件;axes属性包含有关其子对象对图形窗口映射表的使用方式以及plot函数所使用的颜色命令。

    用户不但可以查询当前任意对象的任意属性值,而且可以指定大多数属性的取值(某些属性为MATLAB 7.0控制的只读属性)。属性值仅对对象的特定实例起作用,也就是说,修改属性值不会对同类对象、不同实例的属性产生影响。可以通过设置属性的默认值来影响所有此后创建的对象的属性。如果用户既没有定义默认值也没有在创建对象时指定属性值,MATLAB 7.0将使用系统默认值。每个对象创建函数的参考入口都提供一个与图形对象有关的属性完整列表。

    有些属性是所有图形对象都具备的,例如类型(Type)、被选状态(Selected)、是否可见(Visible)和创建回调函数(CreateFcn)、销毁回调函数(DeleteFcn)。而有些属性则是某种对象独有的,例如线条对象的线性属性等。这些独有的属性将在介绍属性设置方法时具体介绍。

    在不引起混淆的前提下,编程时允许使用属性名的缩写。但是,在编写M文件时最好不使用缩写,以防将来MATLAB 7.0系统扩展时导致属性重名现象。

    set和get函数可以指定或获得已存在对象的属性值,如果该属性值有一个取值范围集,这两个函数还能够将该属性所有可能的取值列举出来。设置已存在对象属性的基本语法格式如下:

    alt

    如果希望查询指定对象的当前属性值,可以使用以下语句:

    alt

    12.2 图形对象的操作

    12.2.1 创建图形对象

    典型的图形通常包括许多种相关的图形对象,由这些对象共同生成有具体含义的图形或图片。每一种类型的图形对象都有一个相对应的创建函数(除root对象外),这个创建函数使用户能够创建该对象的一个实例。对象创建函数名与所创建的对象名相同,例如,函数text将创建一个text对象,figure函数将创建一个figure对象。表12-1列出了MATLAB 7.0所有的对象创建函数。

    表12-1      图形对象创建函数及其创建对象描述

    alt

    所有的对象创建函数都有相同的调用格式:

    alt

    用户可以使用属性名和属性值参数对为任意的对象属性指定一个数值(除了只读类型属性以外)。函数将返回创建对象的句柄,在这之后用户可以使用这个句柄查询或修改所创建对象的属性值。下面给出一个创建对象的实例,首先对一个数学函数求值,再使用figure、axes和surface函数创建3个图形对象并设置其属性,其他图形对象的属性使用MATLAB 7.0默认值。

    源程序代码如下:

    alt


    注意:surface函数并不是像surf函数那样采用三维视图。


    程序运行结果如图12-3所示。

    alt

    图12-3 创建图形对象实例

    为了清楚地观察到创建的各个图形对象,可以通过镜头命令或view命令改变视角:

    alt

    改变视角后的图形外观如图12-4所示。

    alt

    图12-4 视图改变后的图形效果

    对象创建函数还能够使用一种较为简单的调用形式,例如下面的示例代码:

    alt

    或者采用如下代码

    alt

    上述两种方式是等效的。


    注意:使用对象创建函数的这种形式有时会导致输出效果与正常调用形式的结果有细微的不同。


    在默认情况下,所有的创建函数都将当前的图形窗口和坐标轴作为其创建对象的父对象。用户也可以指定所创建对象的父对象。例如下面的示例代码:

    alt

    上述代码将在figure_handle指定的图形窗口中创建一个坐标轴。用户可以通过定义parent属性将创建的对象从一个父对象中转移到另一个父对象中,示例代码设置如下:

    alt

    实际上,MATLAB 7.0高级绘图函数(例如plot、surf等)也是通过调用相应的创建函数来绘制图形对象的。高级函数可能会根据坐标轴和图形窗口的NextPlot属性来判断是否清除坐标轴或创建新的图形窗口;而低级对象创建函数则不考虑图形窗口和坐标轴的NextPlot属性,只是创建各自的图形对象并将这些对象放置在当前的父窗口中。例如,第一次调用下面的代码:

    alt

    此时,将使用指定的数值在当前坐标轴中绘制一条红线(如果不存在坐标轴,MATLAB 7.0将创建一个坐标轴:如果不存在图形窗口,MATLAB 7.0也将创建一个图形窗口)。如果用户是第二次调用line函数,MATLAB 7.0将在当前坐标轴中绘制第二条直线,但并不擦除第一条直线。高级绘图函数(例如plot)则与之不同,它们将擦除以前的图形对象并重置所有的坐标轴(除了位置和比例尺)。用户通过使用hold命令或改变NextPlot属性来改变高级绘图函数的这种行为。

    12.2.2 图形对象属性设置

    在创建图形对象的同时,用户可以根据自己的需要来设置相应图形对象的属性。用户还可以通过set函数和创建函数返回的句柄来改变一个已存在对象的属性。例如,将坐标轴的y轴移动到当前图形的右边,示例代码如下:

    alt

    如果句柄参数是一个向量,MATLAB 7.0将对所有由该向量指定的对象进行属性设置。

    可以使用结构体数组或单元数组来定义属性名并设置属性值,这在希望对某些对象使用相同的属性值时非常有用。例如,用户可以定义一个结构体来设置坐标轴的属性,使之符合特定图形的显示要求,示例代码设置如下:

    alt

    为了设置当前坐标轴的这些属性值,可以输入如下语句:

    alt

    还可以使用set函数来显示多个属性的可能取值但不真正设置新的属性值。例如,以下语句将获得line对象属性的可能取值:

    alt

    MATLAB 7.0将返回由obj_handle定义的Marker属性的数值列表:

    alt

    如果希望得到所有可设置的属性,或者希望得到能够接收字符串属性的所有可能取值,使用对象句柄作为set函数的惟一参数即可实现:

    alt

    例如,对于一个曲面对象调用以上语句,MATLAB 7.0将返回如下信息:

    alt

    set函数的输出是一个结构体数组。例如下面的示例代码:

    alt

    上述语句中的返回值a是一个结构体,其域名是对象的属性名,域值是属性的可能取值。如果需要获得坐标轴网线格式的可能取值,可以使用以下的语句:

    alt

    MATLAB 7.0将输出的代码如下:

    alt


    注意:虽然属性名与大小写无关,但结构体域名与大小写有关,如果输入:

    a.GridLineStyle

    MATLAB 7.0将产生一个错误信息。


    12.2.3 属性值查询

    使用get函数来查询一个或多个对象属性的当前取值。例如,使用以下语句检查当前坐标轴PlotBoxAspectRatio属性的取值:

    alt

    执行结果代码如下:

    alt

    虽然MATLAB 7.0能够列举所有属性的取值,但对于包含数据的属性,例如Current和ColorOrder,MATLAB 7.0仅列举该属性值的维数。例如下面的示例代码:

    alt

    用户可以使用指定的属性名单独查询某一属性,从而获得该属性的具体数据:

    alt

    得到的属性为:

    alt

    如果用户将输出指定的某一变量,则MATLAB 7.0将该变量作为一个结构体数组进行赋值,该数组的域名是对象的属性名,域值是属性名对应的当前取值。例如,如果用户希望绘制数据x和y,可以执行下面的指令:

    alt

    还可以通过下面的指令获得由plot函数创建的line对象的所有属性:

    alt

    下面用户就可以通过使用a的域名来访问line属性的数值。以下语句调用text函数将字符串“x and y”作为数据x和y的起点,文本使用与line相匹配的颜色:

    alt

    如果x和y是矩阵,plot函数将为矩阵的每一列绘制一条线。为了标记数据第二列产生的线条,使用以下语句:

    alt

    如果希望查询属性群,可以定义一个属性名单元数组并使用它方便地获取这些属性取值。例如,假设用户希望查询坐标轴camera mode的属性,则定义以下的单元数组:

    alt

    使用这个单元数组作为参数来获得这些属性的当前取值:

    alt

    运行结果代码如下:

    alt

    12.2.4 设置用户属性默认值

    如果用户没有指定属性值,同时又禁止使用默认的属性值,那么MATLAB 7.0将使用系统默认属性值为所有对象定义属性值。用户可以使用以下语句获得所有属性的系统默认属性值:

    alt

    get函数将返回一个结构体数组,该数组的域名由对象类型和属性名联合构成,域值是域名所标识的对象和属性的系统默认值。例如下面的示例代码:

    alt

    标识uimenu对象SelectionHighlight属性的系统默认属性值为on。可以使用以下语句获得单个属性的系统默认属性值:

    alt

    例如下面的示例代码:

    alt

    对于所有对象的属性,MATLAB 7.0系统中都对应有一个默认的属性值。

    在进行绘图或其他需要了解图像属性值的工作时,MATLAB 7.0将从当前对象开始,在继承表中始终向上搜索,直到找到用户定义的默认值或系统,因此总能找到合适的属性值。可以看出,用户定义的默认值越靠近继承表的root对象,MATLAB 7.0的搜索范围越广。如果用户在root级定义line对象的默认值,MATLAB 7.0将对所有的line对象使用这个默认值,如果用户仅在axes级定义line对象的默认值,MATLAB 7.0将对所有的line对象使用这个默认值。如果用户在多级中定义对象的默认值,与该对象最近的父对象级中定义的默认值将被使用,因为该默认值是MATLAB 7.0最先找到的默认值。

    这里需要注意的是默认值的设置仅对那些设置完成后所创建的对象有效,已存在的图形对象不会发生变化。指定默认值时首先要创建一个以Default开头、然后紧跟对象类型、最后是对象属性的字符串。例如,如果希望在当前的图形窗口级指定line对象的LineWidth(线的宽度)属性为1.5个点宽,可以使用以下语句:

    alt

    字符串DefaultLineLineWidth将指明该属性是line对象的LineWidth属性。定义图形窗口的颜色可以使用字符串DefaultFigureColor。这里要注意的是,仅在root级指定图形窗口的属性才是有意义的。

    alt

    使用参数default可以将一个属性设置为MATLAB 7.0第一个搜索到的默认值。例如,以下语句将产生一个绿色的曲面EdgeColor属性(边缘属性):

    alt

    程序执行结果如图12-5所示。

    alt

    图12-5 设置图形属性的默认值

    在figure级存在一个该属性的默认值,MATLAB 7.0将第一个找到这个默认值,于是用它来替换root级定义的默认值。

    指定某个属性的数值为“remove”时将会删除用户定义的默认值:

    alt

    以上语句将root级定义的曲面EdgeColor默认值删除。

    指定一个属性取值为“factory”,则该属性将采用系统默认的属性值。例如,以下语句将曲面的EdgeColor属性设置为系统默认属性值颜色——黑色,而用户设置的默认值将被忽略:

    alt


    注意:在使用对象创建函数时,default、remove和factory必须以反斜线开头说明该字符串为保留字。


    下面给出两个例子来说明以上内容。plot函数在显示多个图形时将循环使用由坐标轴的ColorOrder属性定义的颜色。如果用户为坐标轴的LineStyleOrder属性定义多个属性值,那么MATLAB 7.0将在每一次颜色循环后改变线性的宽度。用户还可以通过定义属性默认值使plot函数使用不同的线性来创建图形。下面给出几个通过修改属性值进行绘图的例子。

    (1)创建一个白底色图形窗口,然后在root级为坐标轴对象设置默认值。

    alt

    无论何时用户调用plot函数,该函数将采用同一种颜色绘制所有数据,这是因为坐标轴的ColorOrder属性仅包括一个颜色。但是函数将循环使用LineStyleOrder定义的不同线型。程序运行结果如图12-6所示。

    alt

    图12-6 root级设置对象默认值示例

    (2)在继承表的多级上设置默认值。

    在一个图形窗口中创建两个坐标轴,分别在figure级和axes级设置坐标轴颜色、线型等属性的默认值,示例代码设置如下:

    alt

    alt

    此时,不同的子图形使用相同的text和line函数会得到不同的显示结果,如图12-7所示。

    alt

    图12-7 使用不同默认值属性值的不同结果

    由于默认的坐标颜色(Color)属性是在图形窗口级(figure)设置的,所以MATLAB 7.0使用相同的灰色背景颜色创建坐标轴。作图的坐标轴点划线是默认线型,在该图中每一次调用line函数都使用这种线型,而右边的坐标轴没有定义默认线型,所以MATLAB 7.0使用系统默认属性值——实线型。右边的坐标轴给文本的Rotation属性定义为90°。该图中所有的文本都使用这种旋转角度,而左边的坐标轴给文本使用固有默认值,不旋转文本。

    如果用户希望无论何时运行MATLAB 7.0都使用用户定义的默认值,可以在startup.m文件中指定这些默认值。


    注意:在调用colordef命令时,MATLAB 7.0可能会自动装载一些外观属性的系统默认值。


    12.3 句柄使用方法

    12.3.1 访问对象句柄

    MATLAB 7.0给所创建的每一个图形对象指定一个句柄。所有的对象创建函数都能够返回被创建对象的句柄。如果用户希望访问对象的属性,那么最好在创建对象时将对象的句柄赋给一个变量,以免以后对句柄进行重复搜索。当然,用户也可以使用findobj函数或通过查询其父对象的Children属性获得已存在对象的句柄。

    除了root对象和figure对象以外,所有图形对象的句柄都是一个浮点数。用户使用这些句柄数据时必须保证这些数据的全部精度。root对象的句柄总是0;figure对象的句柄可以是显示在窗口标题栏中的整数(默认情况下),也可以是一个完全符合MATLAB 7.0内部浮点数精度的数值。

    图像对象的所有操作都是针对当前对象而言的。“当前”是图形对象的一个重要概念,当前窗口是指被指定作为图形输出的窗口;当前坐标轴是创建坐标轴子对象命令的目的坐标轴;当前对象是用户创建的最后一个图形对象或鼠标单击的最后一个对象。

    MATLAB 7.0分别将这些当前对象的句柄保存在其父对象的属性中,如图12-8所示。

    alt

    图12-8 当前对象句柄保存途径

    从图12-8中列出的属性可以获得这些关键对象的句柄,代码设置如下:

    alt

    以上语句中的gcf、gca、gco是get函数的速记符,它们的含义如下:

    · gcf:返回root的CurrentFigure属性值;

    · gca:返回当前图形窗口的CurrentAxes属性值;

    · gco:返回当前图形窗口的CurrentObject属性值。

    用户可以使用这些速记符(也可以称为命令)作为函数的对象句柄参数。例如,用户可以选择一个line对象,然后使用gco作为set函数的参数来指定该line对象的句柄:

    alt

    或者使用以下语句列举出所有当前坐标轴的属性值:

    alt

    下面的语句可以获得当前坐标轴中的图形对象的句柄:

    alt

    通过以下语句确定对象的类型:

    alt

    最后执行结果的代码如下:

    alt

    虽然gcf和gca提供了一个简单地获取当前窗口和坐标轴句柄的方法,但是很少在M文件中使用这两个命令,因为一般设计MATLAB 7.0程序M文件时,不会根据用户行为来获得当前对象。

    MATLAB 7.0还提供了一种通过属性搜索对象的方法,即findobj函数。findobj函数能够快速地形成一个继承表的横截面并获得具有指定属性值的对象句柄。如果用户没有指定一个开始搜索的对象,findobj函数将从root对象开始,始终搜索与用户指定属性名和属性值相符的所有事件。下面举例说明该函数的使用方法。

    在如图12-9所示的sin函数图形中包括用来标注特殊点的文本对象。

    alt

    图12-9 含有文本对象的图形

    假设用户希望将文本字符串sin(t)=0.707从当前位置移动到[3pi/4,sin(3pi/4)]。首先要获得文本对象的句柄,然后通过使用这个句柄修改文本对象的属性Position。

    为了使用函数findobj,选择惟一一个能够表示该文本对象的属性值来搜索当前对象的句柄。在本例中使用文本对象的String属性值。

    alt

    然后重新定义文本的Position属性值,将该对象移动到新的位置:

    alt

    findobj函数也允许用户通过指定多起始点来限制搜索,当继承表中有许多对象时,这样做将会大大提高搜索的速度。在本例中,假设用户知道所需的文本对象位于当前的坐标轴中,可以用以下语句进行快速搜索:

    alt

    12.3.2 使用句柄操作图形对象

    1.对象拷贝

    用户可以使用copyobj函数将一个对象从一个父对象拷贝到另一个父对象中。新对象与旧对象惟一的不同就是其Parent属性和句柄。可以同时将多个对象拷贝到一个新的父对象中,也可以将一个对象拷贝到多个父对象中而不改变当前的父子关系。当用户拷贝一个有子对象的对象时,MATLAB 7.0同时也拷贝其所有的子对象。

    假设用户正在绘制多个数据并且希望标注每个图形点(5π/4,sin(5π/4))。text函数将使用字符串{5π/4,sin(5π/4)}和一个右箭头来标注数据点,并将'HorizontalAlignment'属性修改为right使得数据点位于文本字符串的右边。

    alt

    得到的结果如图12-10所示。

    alt

    图12-10 待拷贝的图形

    为了使用同样的字符串对另外一个点进行标注,首先使用copyobj函数拷贝文本对象。由于以上的语句并没有保存文本对象的句柄,所以用户必须通过findobj函数和String属性获得句柄,代码设置如下:

    alt

    在创建第二个图形后,通过第一个图形中拷贝标签来为第二个图形添加标签:

    alt

    2.对象删除

    用户可以使用delete命令和句柄参数删除一个图形对象。例如,用户可以使用以下语句删除当前的坐标轴(以及其所有子对象):

    alt

    也可以通过findobj命令来获得需要删除的特定对象的句柄。

    例如,假设希望删除图12-10中的点线,首先获得点线的句柄:

    alt

    然后使用这个句柄删除由上述语句获得的线:

    alt

    alt

    图12-11 待删除的点线图形

    如果将以上两个语句并为一个,代码设置如下:

    alt

    删除结果如图12-12所示。

    alt

    图12-12 利用句柄删除点线后的图形

    12.3.3 控制图形输出

    MATLAB 7.0允许在同一次运行过程中打开多个图形窗口,所以当一个MATLAB 7.0程序将创建图形窗口来显示图形用户界面并绘制数据时,有必要对某些图形窗口进行保护,以免成为图形输出的目标,而相应的输出窗口要做好接受新图形的准备。以下内容将讨论如何使用句柄来控制MATLAB 7.0显示图形的目标和方法。

    1.指定输出目标

    默认情况下,MATLAB 7.0图形创建函数在当前的图形窗口和坐标轴中显示图形。用户可以通过在图形创建函数中使用明确的Parent属性直接指定图形的输出位置。例如:

    alt

    在上述代码中,axes handle是目的坐标轴的句柄。

    uicontrol和uimenu函数指定Parents属性的语法比较简单:

    alt

    默认情况下,产生图形输出的函数将在当前的图形窗口中显示该图形而不擦除或重置当前窗口的属性。然而,如果图形对象是坐标轴的子对象,为了显示这些图形将会擦除坐标轴并重置坐标轴的大多数属性。用户可以通过设置图形窗口和坐标轴的NextPlot属性来改变MATLAB 7.0的这种行为。MATLAB 7.0高级图形函数在绘制图形前先要检查NextPlot属性,然后来决定是添加还是擦除重置图形和坐标轴。而低级对象创建函数则不检查NextPlot属性,只是简单地在当前窗口和坐标轴中添加新地图形对象。

    在表12-2中列出了NextPlot属性的可能取值。

    表12-2      NextPlot属性地可能取值

    alt

    hold命令提供访问NextPlot属性的简便方法,以下语句将图形和坐标轴的NextPlot属性都设置为add:

    alt

    以下语句将图形和坐标轴的NextPlot属性都设置为replace:

    alt

    MATLAB 7.0提供newplot函数来简化图形M文件设置NextPlot属性的编写过程。newplot函数首先检查NextPlot属性值,然后根据属性值决定相应的行为。用户应该在所有调用图形创建函数的M文件的开头定义newplot函数。当用户调用newplot函数时,有可能发生以下行为。

    (1)检查当前图形窗口的NextPlot属性

    · 如果不存在图形窗口,则创建一个窗口并设该窗口为当前窗口;

    · 如果NextPlot值为add,将该窗口设置为当前窗口;

    · 如果NextPlot值为replacechildren,删除窗口的子对象并设置该窗口为当前窗口;

    · 如果NextPlot值为replace,删除窗口的子对象,重置窗口属性为默认值,并设置该窗口为当前窗口。

    (2)检查当前坐标轴的NextPlot属性

    · 如果不存在坐标轴,则创建一个坐标轴并设该坐标轴为当前坐标轴;

    · 如果NextPlot值为add,将该坐标轴设置为当前坐标轴;

    · 如果NextPlot值为replacechildren,删除坐标轴的子对象,并设置该坐标轴为当前坐标轴;

    · 如果NextPlot值为replace,删除坐标轴的子对象,重置坐标轴属性为默认值,并设置该坐标轴为当前坐标轴。

    默认情况下,图形窗口的NextPlot值为add,坐标轴的NextPlot值为replace。

    为了说明newplot的使用方法,下面给出一个类似与plot的绘图函数my_plot,该函数在绘制多个图形时将循环使用不同的线型,而不是使用不同的颜色,具体代码设置如下:

    alt

    函数my_plot使用低级函数line语法来绘制数据,虽然line函数并不检查图形窗口和坐标轴的NextPlot属性值,但是newplot的调用使得函数my_plot与高级函数plot执行相同的操作,即每一次用户调用该函数时,函数都对坐标轴进行清除和重置。my_plot函数是使用newplot函数返回的句柄来访问图形窗口和坐标轴。该函数还设置了坐标轴的字体属性并禁止使用图形窗口的菜单。调用my_plot函数的绘图结果如图12-13所示。

    alt

    alt

    图12-13 my_plot函数的典型输出结果

    2.保护图形和坐标轴

    有些情况下需要对图形窗口和坐标轴进行保护以免其成为图形输出的目标。用户可以将特定窗口或坐标轴的句柄从句柄列表中删除,使newplot和其他返回或参考句柄的函数(gca,gco,cla,clf,close和findobj)无法找到该句柄,从而保证该窗口不会成为其他程序的输出目标。HandleVisibility和ShowHiddenHandles两个属性可以用来控制保护对象句柄的可见与否。

    HandleVisibility是一个所有对象都具备的属性,该属性值可以为以下取值:

    · on:对象句柄可以被任意函数获得,这是该属性的默认值;

    · callback:对象句柄对所有在命令行中执行的函数隐藏,而对所有回调函数(用户行为处理函数)总是可见的。这种可见程度将保证用户在命令行中键入的命令不会影响被保护对象;

    · off:句柄对所有函数,无论是命令行中执行的还是回调的函数,都是隐藏的。

    例如,如果一个用户图形界面以文本字符串形式接受用户的输入,并在回调函数中对这个字符串进行处理,那么如果不对窗口进行保护,字符串“close all”有可能会导致用户图形界面的销毁。为了防止这种情况,用户可以将该窗口关键对象的Handle Visibility数值暂时设置为off:

    alt

    如果一个被保护的图形窗口是屏幕最顶层的窗口,而且在它之下存在未保护的窗口,那么使用gcf将返回最高层的未被保护的窗口,gca的情况与之相同。如果不存在未保护的窗口或坐标轴,MATLAB 7.0将创建一个窗口并返回它的句柄。

    root对象的ShowHiddenHandles属性是句柄可视的使能控制属性。ShowHiddenHandles的默认值为off。当该属性值为on时,句柄对所有函数都是可视的。当用户希望访问某一特定时刻存在的所有对象时,该属性将十分有用。这里需要说明的一点是,close函数可以通过使用hidden选项来访问不可见的窗口,例如下面的示例代码:

    alt

    即使该窗口是被保护的,该语句也将关闭屏幕最顶端的窗口。使用以下语句将关闭所有窗口:

    alt

    有些情况下需要改变坐标轴的可视外观来适应新的图形对象。为了与MATLAB 7.0的高级函数保持一致,在改变坐标轴和图形窗口之前,最好先测试一下hold属性是否为on,当hold属性为on时,坐标轴和图形窗口的NextPlot数值均为add。以下的my_plot3函数将接收三维数据并使用ishold来检查hole属性的状态,以此来决定是否更改视图。

    alt

    alt

    如果hold属性为on,调用my_plot3时将不改变视图,否则有3个输入参数,MATLAB 7.0将视图由二维变为三维。

    3.关闭请求

    定义CloseRequestFcn属性使用户能够组织或延迟图形窗口的关闭以及MATLAB 7.0运行的终止。发生以下几种情况时,MATLAB 7.0将执行由图形窗口的CloseRequestFcn属性定义的回调函数(称为关闭请求函数):

    · 在图形窗口中调用close命令;

    · 用户退出MATLAB 7.0时还存在可见的图形窗口(如果一个窗口的Visible属性值为off,则退出MATLAB 7.0时并不执行关闭请求函数,而是删除该图形窗口);

    · 使用窗口系统的关闭菜单或按钮来关闭图形窗口。

    默认的关闭请求函数保存在一个名为closereq.m的M文件中,该函数包括以下语句:

    alt

    这个关闭请求函数通过设置root对象的ShowHiddenHandles属性使得Handle Visibility属性的设置无效,即将所有图形窗口的句柄变成可见。这样,当用户退出MATLAB 7.0时,当前图形窗口的关闭请求函数将被调用,在关闭请求函数中删除该图形窗口,然后root对象children属性列表中的下一个窗口将变成当前窗口,执行它的关闭请求函数,如此重复该过程直至所有的窗口都被删除。此时,如果MATLAB 7.0不删除图形窗口,MATLAB 7.0不能退出。

    如果在视图关闭窗口时关闭请求函数发生错误,那么MATLAB 7.0将终止退出操作。但是如果使用quit或exit命令退出MATLAB 7.0,即使关闭请求函数发生错误,MATLAB 7.0也会无条件地关闭窗口。delete函数也总是无视CloseRequestFcn属性的取值而无条件地关闭指定的窗口,例如下面的示例代码:

    alt

    上面的语句将删除所有句柄可见的窗口(即所有HandleVisibility属性为on的窗口)。如果用户希望删除所有窗口(不论句柄是否隐藏),那么可以通过设置root对象的ShowHiddenHandles属性为on来实现。例如下面的语句:

    alt

    将无条件地删除所有图形窗口。

    12.3.4 在M文件中保存句柄

    所有句柄无论可见与否都能够保持其有效性,如果用户确实知道某对象的句柄,无论该句柄可见与否,用户都可以使用句柄进行对象属性的设置或查询。

    图形M文件经常使用句柄来访问属性值,并通过句柄直接定义图形输出的目标。MATLAB 7.0提供一些有用的函数来获得图形关键对象(例如当前窗口和坐标轴)的句柄,然而在M文件中,使用这些函数并不是获得句柄最好的方法。这是因为,在MATLAB 7.0中查询对象句柄或其他信息的执行效率并不高,最好还是将句柄直接保存在一个变量中进行引用。另外,由于当前的坐标轴、图形窗口或对象有可能因为用户的交互而发生变化,查询方式难以确保句柄完全正确,而使用句柄变量则可以保证正确地反映对象发生的变化。

    为了保存句柄信息,通常用户在M文件的开始处保存MATLAB 7.0相关的状态信息。例如,用户可以使用以下语句作为M文件的开头:

    alt

    这样就无需在每次需要这些信息时都重新进行查询。如果用户在M文件中暂时改变了保持状态,用户应当将NextPlot的当前属性值保存下来,以便以后重新设置。

    alt

    有一些内置的函数可以修改坐标轴的属性以实现某种特定的效果,这些函数可能会对用户的M文件产生一定的影响。

    表12-3列出了一些MATLAB 7.0的内置函数以及它们所修改的属性。注意这些属性仅在hold设置为off时才会发生变化。

    表12-3      某些MATLAB 7.0的内置函数修改的属性

    alt

    12.4 GUI设计向导

    一个可发布的应用程序通常都需要具备一个友好的图形界面,本章就来介绍创建图形用户界面(GUI)的具体方法。本章首先对GUI的基本概念作简单的介绍,然后说明GUI开发环境GUIDE及其组成部分的用途和使用方法,最后说明创建一个完整GUI的详细步骤。

    12.4.1 图形用户界面概述

    通常在开发一个实际的应用程序时都会尽量做到界面友好,最为常用的方法就是使用图形界面。提供图形用户界面的应用程序能够使用户的学习和使用更为方便容易。用户不需要知道应用程序究竟怎样执行各种命令的,而只需要了解可见界面组件的使用方法;用户也不需要知道命令是怎样执行的,只要通过与界面交互就可以使指定的行为得以正确执行。

    在MATLAB 7.0中,图形用户界面是一种包含多种对象的图形窗口。用户必须对每一个对象进行界面布局和编程,从而使用户在激活GUI每个对象时都能够执行相应的行为。另外,用户必须保存和发布所创建的GUI,使得GUI能够真正地得到应用。

    MATLAB为用户开发图形界面提供了一个方便高效的集成开发环境,MATLAB图形用户界面开发环境GUIDE(MATLAB Graphical User Interface Development Environment)。上述所有工作都能够使用GUIDE方便地实现。GUIDE主要是一个界面设计工具集,MATLAB 7.0将所有GUI支持的用户控件都集成在这个环境中并提供界面外观、属性和行为响应方式的设置方法。GUIDE将用户保存设计好的GUI界面保存在一个FIG资源文件中,同时还能够生成包含GUI初始化和组件界面布局控制代码的M文件。这个M文件为实现回调函数(当用户激活GUI某一组件时执行的函数)提供了一个参考框架。虽然使用户自己编写的包含GUI所有发布命令的M文件也能够实现一个GUI,但是使用GUIDE执行效率更高。使用GUIDE不但能够交互式地进行组件界面布局,而且能够生成两个用来保存和发布GUI的文件。

    · FIG文件:该文件包括GUI图形窗口及其所有后裔的完全描述,包括所有对象的属性值。FIG文件式一个二进制文件,调用hgsave命令或界面设计编辑器的File菜单Save选项保存图形窗口时将生成该文件。FIG文件包含序列化的图形窗口对象,在用户打开GUI时,MATLAB 7.0能够通过读取FIG文件重新构造图形窗口及其所有后裔。所有对象的属性都被设置为图形窗口创建时保存的属性。默认情况下,即使用户使用hgsave和hgload命令保存系统默认的图形工具条和菜单,FIG文件也不保存这些默认信息。FIG文件的作用之一就是对象句柄的保存和引用。可以使用open、openfig和hgload命令类来打开一个后缀为.fig的文件;

    · M文件:该文件包括GUI设计、控制函数以及定义为子函数的用户控件回调函数,主要用于控制GUI展开时的各种特征。该M文件可分为GUI初始化和回调函数两个部分,用户控件的回调函数根据用户与GUI的具体交互行为分别调用。这里将GUI的M文件称为应用程序M文件。应用程序M文件使用openfig命令来显示GUI。注意应用程序M文件并不包括用户界面设计的任何代码,这些代码将完全由FIG文件保存。

    GUIDE可以根据用户GUI的版面设计过程直接自动生成M文件框架,这样就简化了GUI应用程序的创建工作,用户可以直接使用这个框架来编写自己的函数代码。这样的编写方法具有以下优点:

    · 应用程序M文件已经包含实现一些有用的函数代码,无需用户自行编写;

    · 可以使用该M文件生成的有效方法来管理图形对象句柄并执行回调函数子程序;

    · 提供管理全局数据的途径;

    · 文件支持自动插入回调函数原型,确保当前GUI与未来发布版本的兼容性。

    用户也可以选择由GUIDE生成FIG文件、自己编写应用程序M文件的GUI创建方式。编写M文件时要注意,应用程序M文件中不能包含用户控件的创建命令,所有的界面设计信息都保存在由界面设计编辑器生成的FIG文件中。

    实现一个GUI主要包括GUI界面设计和GUI组件编程两项工作。整个GUI的实现过程可以分为以下几步:

    · 通过设置GUIDE应用程序的选项来进行GUIDE组态;

    · 使用界面设计编辑器进行GUI界面设计;

    · 理解应用程序M文件中所使用的编程技术;

    · 编写用户GUI组件行为响应控制(即回调函数)代码。

    12.4.2 启动GUIDE

    在MATLAB 7.0中,GUIDE提供了多种设计模板以方便用户轻松地定制属于自己的GUI。这些模板均包括了相关的回调函数,用户可以打开它所对应的M文件,看到它们的工作方式,或修改相应的函数,从而实现自己需要的功能。

    在MATLAB 7.0中,可以通过如下两种方法来访问模板:

    · 直接输入GUIDE命令,打开如图12-14所示的界面;

    · 如果GUIDE已经打开,通过File菜单中的New选项也可以打开GUI模板的设置界面。

    alt

    图12-14 GUI模板设置界面

    在模板设计界面中,用户可以选择创建新的GUI或者打开原有的GUI。在创建新的GUI中,MATLAB 7.0提供了空白模板、带有控制按钮的模板、带有坐标轴和菜单的模板以及问答式对话框4种模板。其中空白模板如图12-15所示。

    alt

    图12-15 空白界面设计编辑器外观

    12.4.3 GUIDE提供的用户控件

    在空白模板中GUIDE提供了用户界面控件以及界面设计工具集来实现用户界面的创建工作。用户界面控件分布在界面设计编辑器的左侧,如图12-16所示。

    alt

    图12-16 各种控件图示

    下面简单介绍各种控件的概念和特点。

    · 按钮:通过鼠标单击按钮可以实现某种行为(按钮下陷和弹起等)并调用相应的回调子函数;

    · 滚动条:使用户能够通过移动滚动条来改变指定范围内的数值输入,滚动条的位置代表用户输入的数值;

    · 单选按钮:单选按钮与按钮的执行方式没有本质上的区别,但是单选按钮通常以组为单位,一组单选按钮之间是一种互相排斥的关系,也就是说任何时候一组单选按钮中只能有一个有效;

    · 复选框:复选框与单选按钮类似,只是多个复选框可以同时有效。复选框为用户提供一些可以独立选择的选项进行设置程序模式,例如显示工具条与否以及生成回调函数原型与否等;

    · 编辑框:编辑框是控制用户编辑或修改字符串的文本域,其String属性包含用户输入的文本信息。如果一个编辑框有输入焦点,在UNIX系统中,单击图形窗口的菜单栏,编辑框回调函数就会被调用,而在Windows系统下,单击图形窗口不会导致编辑框回调函数的执行。这个区别是处于操作平台方便性考虑而定的;

    · 静态文本:静态文本通常作为其他控件的标签使用,用户不能采用交互方式修改静态文本或调用响应的回调函数;

    · 弹出式菜单:弹出式菜单将打开并显示一个由其String属性定义的选项列表。当用户希望提供一些相互排斥的选项,但不希望使用一系列占用有限空间的单选按钮时,文本菜单将非常有用;

    · 列表框:列表框显示由其String属性定义的一系列列表项,并使用户能够选择其中的一项或多项;

    · 拴牢按钮:拴牢能够产生一个二进制状态的行动(on或off)。单击该按钮将使按钮的外观保持下陷状态,同时调用响应的回调函数。再次单击该按钮将使按钮弹起,同时也要调用回调函数。拴牢按钮的回调函数首先要对按钮的状态进行查询,然后才能决定相应的行为;

    · 坐标轴:坐标轴使用户的GUI可以显示图片,像所有的图像对象一样,坐标轴可以设置许多关于外观和行为的参数;

    · 组合框:组合框是图形窗口中的一个封闭区域,它把相关联的控件(例如一组单选按钮)组合在一起,使得用户界面更容易理解;组合框可以有自己的标题以及各种边框;

    · 按钮组:按钮组类似于组合框,但是它可以响应关于单选按钮以及拴牢按钮的高级属性,例如单选按钮之间的互斥属性。


    注意:后边两种为MATLAB 7.0中新增的控件。


    12.4.4 界面设计工具集

    GUIDE提供了一个界面设计工具集来实现图形用户界面的创建工作,这些界面设计工具包括:

    · 界面设计编辑器:添加并排列图形窗口中的组件对象;

    · 属性检查器:检查并设置组件的属性值;

    · 图像浏览器:观察此次MATLAB 7.0运行过程中图形对象的句柄集成关系表;

    · 菜单编辑器:创建窗口菜单和文本菜单。

    1.界面设计编辑器

    界面设计编辑器使用户能够从组件面板种选择GUI组件并将它们排列在图形窗口中。界面设计编辑器由组件面板、工具栏、菜单栏和界面区域4个部分组成。组件面板包含用户界面可获得的所有组件(用户控件对象);工具栏和菜单栏可以用来启动其他界面设计工具,例如菜单编辑器;界面区域实际上就是激活后的GUI图形窗口。

    (1)组件面板

    在GUI界面中放置组件,首先单击组件面板重的按钮,选择需要放置的组件类型,光标变为十字形后使用十字形光标的中心点来确定组件左上角的位置,或者通过在界面区域内单击并拖动鼠标来确定组件的大小。图12-17给出了一个添加组件的示例。

    GUI组件布置工作完成后,用户可以使用激活按钮或选择Tools菜单的ActivateFigure选项来观察GUIDE的设计结果。激活图形窗口将发生以下事件:首先保存FIG文件和M文件,如果用户尚未保存该设计结果,GUIDE将打开“保存:对话框使用户选择将要创建的M文件名;然后GUIDE保存与M文件同名的FIG文件(扩展名为.fig),如果存在一个同名的M文件,GUIDE将会显示一个提示对话框,如果用户选择提示对话框中的Replace按钮,原来的M文件将被替换;如果选择:Append”,则GUIDE将向原有的M文件中插入未保存的新组件回调函数并根据应用程序选项对话框设置的变化来修改原有代码。

    alt

    图12-17 在界面中添加组件的示例


    注意:如果FIG文件和M文件已经存在,那么当用户激活GUI时,GUIDE将自动保存应用程序M文件和FIG文件,因此不要随意激活GUI以免有用的组件被替换。


    (2)文本菜单

    使用界面设计编辑器进行界面设计时,可以先选择一个对象,然后单击鼠标右键来显示与所选对象相连的文本菜单。图12-18表示了一个与图形窗口对象联系在一起的文本菜单,所有已定义的回调子函数都列举在该菜单的下方。

    alt

    图12-18 图形窗口的文本菜单

    图12-19描述了一个与按钮相联系的文本菜单,同样,所有定义的回调函数都列举在该菜单的下方。

    alt

    图12-19 GUI用户控制文本菜单

    (3)排列工具

    用户可以在界面区域内通过选择并拖动任意组件或组件群进行组件排列。另外,界面设计编辑器还提供一些更为精确的组件排列方法。

    · 排列工具:排列并分布组件群;

    · 栅格和标尺:指定任意位置的水平和竖直标线;

    · 指引线:指定任意位置的水平和竖直标线;

    · 拉前推后:控制组件的前后顺序。

    排列工具使用户能够根据其他组件的位置调整被选择组件的间隔或放置某个组件。上述的排列方法可以通过排列工具栏获得,当用户按下“Apply”按钮时,指定的排列操作将应用于所有被选择的组件。图12-20显示了排列工具栏的外观。

    alt

    图12-20 排列工具栏外观

    (4)网线和标线

    界面区域可以使用网格和标线辅助完成组件设计工作。用户可以将网格线的间隔设置在10~200个像素之间,默认情况下以50个像素为间隔。如果用户选择了“snap-to-grip”选项,那么对于任何一个在网格线周围9像素范围内移动或重画的对象,系统都会自动将该对象放置在该网格线上。无论网格是否可见,snap-to-grip选项都是有效的。

    网格和标线对话框外观如图12-21所示。

    alt

    图12-21 网格和标线对话框外观

    该对话框可以完成以下工作。

    · 控制标线、网格和指引线的可见与否;

    · 设置网格间距;

    · 使能snap-to-grip。

    界面设置编辑器有水平和竖直两种指引线。当用户希望在界面设计编辑器的任意位置建立一个组件排列参考标准时,指引线将非常有用。单击标线的左边或顶端并将其拖放到界面区域中所需位置处就会生成一条指引线。

    2.属性检查器

    属性检查器提供一个所有可设置属性的列表并显示当前的属性值,使用户能够设置界面中各组件的属性。列表中的每个属性都对应于一个相应于该属性的属性值选择范围。属性检查器的外观如图12-22所示。

    alt

    图12-22 属性检查器外观

    用户可以通过多种方式来启动属性检查器。双击界面设计编辑器中的组件;选择Tools菜单下的“Property Inspector”选项;选择Edit菜单下的“Inspect Property”选项;从组件的文本菜单中选择“Property Inspector”选项。

    通过对GUI中各个用户控制对象的属性设置可以实现用户所需的控件外观和行为特征。

    3.对象浏览器

    对象浏览器显示图形窗口中所有对象的继承关系。图12-23所为对象浏览器的示例,从图中可以看出,第一个创建的是用户控件组合框,然后是4个单选按钮,最后是坐标轴。

    alt

    图12-23 对象浏览器示例

    4.菜单编辑器

    菜单编辑器的外观如图12-24所示。

    alt

    图12-24 菜单编辑器外观

    GUIDE能够创建两种类型的菜单。在图形窗口菜单栏中显示的菜单栏菜单;当用户在图形对象上单击鼠标右键时弹出的文本菜单。可以使用菜单编辑器来创建这两种类型的菜单。

    (1)定义菜单栏菜单

    创建菜单的第一步是使用New Menu工具栏创建一个菜单,然后来指定菜单的属性。用户单击创建的菜单项将会显示如图12-25所示的一个文本域,在该文本域中可以设置菜单的标签、分隔符、选中模式以及回调函数字符串。

    alt

    图12-25 菜单的相关设置

    菜单创建成功后,MATLAB 7.0将该菜单添加到图形窗口的菜单中。

    下面将创建菜单项。使用New Menu Item工具来添加菜单项,每一个菜单项也可以有级联的子菜单项。图12-26显示了为窗口菜单栏定义了3个菜单的菜单编辑器外观。

    alt

    图12-26 菜单项创建示例

    如果用户激活图形窗口,这3个菜单将会出现在窗口中,如图12-27所示。

    alt

    图12-27 菜单创建结果

    (2)定义文本菜单

    定义了文本菜单的对象后,当用户单击鼠标右键时,文本菜单随之出现。菜单编辑器能够定义文本菜单并将菜单与对象联系起来。

    文本菜单的所有菜单项都是文本菜单的子对象,这些菜单项并不显示在窗口菜单栏中。选择菜单编辑器工具条中的“New Context Menu”来创建父菜单并为之定义一个标签(名称)。注意在定义文本菜单之前要选择菜单编辑器的Context Menus标签界面。使用菜单编辑器工具条中的“New Menu Item”按钮来创建文本菜单项,然后给该菜单项添加一个标签并定义回调字符串。

    在界面编辑器中选择需要定义文本菜单的对象,使用属性检查器将该对象的UIContextMenu属性设置为所需文本菜单的标签名。在应用程序M文件中给每个文本菜单项添加一个回调子函数,当用户选择特定的文本菜单项时,这个回调子函数将被调用。

    12.4.5 GUI组态

    调用GUIDE命令来显示一个空的界面设计编辑器和一个无标题的图形窗口。在为该图形窗口添加组件时,首先应该使用GUIDE应用程序选项对话框来进行GUI组态。选择界面设计编辑器的Tools菜单下的“Application Options”选项打开选项对话框。GUIDE应用程序选项对话框的外观如图12-28所示。

    alt

    图12-28 GUIDE应用程序选项对话框

    该对话框能够设置的选项包括以下几项:

    · 窗口重画行为(Resize behavior);

    · 命令行访问(Command-Line Accessibility);

    · 生成FIG文件和M文件(Generate FIG-File and M-File);

    · 生成回调函数原型(Generate Callback Function Prototypes);

    · 同一时刻仅允许运行一个应用程序实例(GUI Allows Only One Instance to Run(Singleton));

    · 使用系统背景颜色设置(Using the System Background Colors);

    · 仅生成FIG文件(Generate FIG-File Only)。

    1.重画行为(Resize behavior)

    通过选项对话框可以决定用户是否可以重画GUI所在的图形窗口以及MATLAB 7.0将如何管理重画过程。GUIDE提供以下3种选择:

    · Non-resizable:用户不能改变窗口大小(默认选项);

    · Proportional:允许MATLAB 7.0按照新的图形窗口尺寸自动按比例重新绘制GUI组件。注意重画过程中将不改变组件标签字体的大小,如果图像窗口过大,那么组件的标签将不可读。对于在设置过程中始终不关闭的简单GUI工具和对话框来说,这种方式非常实用;

    · User-specified:通过编程使重画过程中GUI按照用户指定的方式变化。该选项需要用户编写一个ResizeFcn属性定义的回调函数,该函数根据新的图形窗口尺寸重新计算组件的大小和位置。

    2.命令行访问(Command-Line Accessibility)

    在MATLAB 7.0中创建一幅图形时,该图形的图形窗口和坐标轴将包括在其父对象的子对象列表中,它们的句柄可以使用诸如findobj、set和get之类的函数来获得。创建GUI时也要创建一个图形窗口,由于绘图命令会直接将结果输出到当前的GUI的图形窗口中,所以通常情况下用户不需要额外的GUI图形窗口访问,就可以直接将图形输出到窗口中。但是,如果用户需要创建一个包含坐标轴等绘图工具的GUI,那么访问图形窗口则是必要的。这种情况下GUI要支持命令行的访问。

    GUIDE应用程序选项对话框提供3种用户访问权限。

    · off:禁止命令行对GUI图形窗口的访问;在这种方式下GUI图形句柄是隐藏的,这就意味着用户不能够使用findobj函数来定位GUI中的用户控件句柄。应用程序M文件将创建一个对象句柄结构体来保存GUI中的所有用户控件句柄并将该结构体传递给子函数保证GUI中句柄的使用无误;

    · on:允许命令行对GUI图形窗口进行访问;

    · User-specified:GUI使用用户设置Handle Visibility和IntergerHandle这两个图形窗口属性值,它们决定句柄是否能被命令行获得。Handle Visibility属性决定图形窗口的句柄对视图访问当前图形窗口的命令是否可见;该属性设置为off将导致图形窗口的句柄从根对象的子对象列表中删除,使该图形窗口不再是当前图形窗口。但是该图形窗口的句柄仍然有效,明确使用该句柄的函数仍然能够工作。IntergerHandle决定图形窗口的句柄是一个整数还是浮点数。如果该属性是off,MATLAB 7.0将使用一个不可重复使用的浮点数来代替整数,这将大大减小用户对图形窗口的误操作几率。

    3.生成FIG文件和M文件(Generate FIG-File and M-File)

    如果用户希望GUIDE同时创建FIG文件和应用程序M文件,在GUIDE应用程序选项对话框中选择“Generate FIG-File and M-File”选项。一旦用户选择了这个选项,用户就可以选择以下任何一项M文件的组态项目。

    · 生成回调函数原型(Generate Callback Function Prototypes):当用户在GUIDE用程序选项对话框中选择该选项时,GUIDE将在应用程序M文件中为每一个组件添加一个回调函数(注意组合框和静态文本组件不使用自身的Callback属性)。用户必须为回调函数编写代码。GUIDE还为用户添加了一个与弹出式(右键鼠标出现)文本菜单选项对应的回调子函数,用户无论何时选择菜单项都调用该函数;

    · 同一时刻仅允许运行一个应用程序实例(GUI Allows Only One Instance to Run(Singleton)):该选项允许用户选择图形窗口的两种行为。允许MATLAB 7.0的一次运行过程中仅有GUI一个实例;允许MATLAB 7.0显示GUI的多个实例。如果用户允许运行一个实例,那么无论何时执行一个发布GUI的命令,MATLAB 7.0都将重新使用已存在的GUI图形窗口。如果GUI已存在,MATLAB 7.0将该GUI带到前台而不是重新创建一个新的窗口。如果允许MATLAB 7.0显示GUI的多个实例,则每一个GUI调用命令都将创建一个新的窗口。GUIDE通过在应用程序M文件中生成使用openfig命令的代码来实现这个特征,而通过reuse或new字符来指定GUI的一个或多个实例;

    · 使用系统背景颜色设置(Using the System Background Colors):GUI组件使用的颜色与计算机系统有关。选项使图形窗口的背景颜色与用户控件默认背景颜色(与系统有关)相匹配。图12-29中左边的图形窗口未选择该选项,而右边的图形窗口选则了该选项,可以看到二者的效果差异。

    alt

    图12-29 背景颜色使用系统颜色设置与否的比较

    如果用户不希望GUIDE生成应用程序M文件,在GUIDE应用程序选项对话框中选择Generate FIG-File Only选项。当用户在界面设计编辑器中保存GUI时,GUIDE将仅仅创建能够使用open和hgload命令重新显示的FIG文件。当用户选择这个选项时,必须将每一个GUI组件的Callback属性设置为一个MATLAB 7.0能够理解并执行指定操作的字符串,这个字符串可以是一个表达式也可以是一个M文件。如果用户希望生成一个与应用程序M文件完全不同的程序范例时可以选择这个选项。

    12.4.6 GUI界面设计

    用户可以使用openfig、open或hgload命令来显示一个GUI图形窗口,这些命令将相应的FIG文件装载到MATLAB 7.0的工作平台中,然后用户就可以对该GUI进行重新设计。用户可以使用界面设计编辑器File菜单中的“Open”选项或以下命令来装载一个已存在的GUI进行编辑:

    alt

    GUI界面设计是通过使用界面设计编辑器进行的。基本的编辑方法在前面已经做了详细介绍,这里就不再赘述。

    GUI的创建过程中经常会要求用户定义组件的Tag属性值和callback子函数名。GUIDE要求用户为GUI界面中的每个组件指定一个Tag属性值,然后根据该字符串来命名回调函数。在第一次激活或保存GUI之前最好为组件选择一个Tag名和文件名。

    使用界面设计编辑器File菜单的Save as选项可以给应用程序M文件重命名并重新设置callback属性以保证回调函数的正常运行。注意由于GUIDE使用Tag属性来命令函数和结构体的域名,所以用户选择的Tag必须为一个有效的MATLAB 7.0变量名。可以使用isvarname函数来确定用户指定的字符串是否有效。如果用户在GUIDE中创建M文件和FIG文件后,对GUI进行了修改,那么必须确保用户的代码能够兼容这些修改。

    GUIDE自动给每一个用户控件的Tag属性分配一个字符串并使用该字符串构造生成的回调子函数名,并为句柄结构体添加一个域名。

    如果用户在GUIDE生成回调子函数后对Tag属性进行了修改,GUIDE将不会生成一个新的子函数。然而,由于句柄结构体是实时创建的,所以GUIDE将在句柄结构体中使用新的Tag名。避免产生问题最好的方法就是在添加组件的同时设置Tag属性值。如果一定要在应用程序M文件创建后修改Tag并希望重新命名回调子函数来保持GUIDE所使用的名称一致,那么用户需要修改句柄结构体所有输出数据参考和修改回调子函数名称。当用户保存或激活GUI时,GUIDE将使用一个在应用程序M文件中执行回调子函数的字符串来替换每一个取值为automatic的Callback属性。当第一次将一个按钮插入界面中时,其Callback属性如图12-30所示。

    alt

    图12-30 按钮组件的Callback属性

    alt

    图12-31 添加第一个按钮的字符串

    用户保存或激活图形窗口时,GUIDE将修改Callback属性为执行回调子函数字符串,图12-31表明了添加了第一个按钮的字符串。

    如果用户希望修改回调子函数的名称,那么必须同时修改用户控件的Callback属性字符串。图12-32为回调子函数重命名为Closebutton_Callback后的外观。

    alt

    图12-32 回调子函数重命名后的外观

    GUI的FIG文件和相关的应用程序M文件的文件名相同,只是文件扩展名不同。当用户执行M文件以发布GUI时,使用mfilename命令从M文件名来确定FIG文件名:

    alt

    如果FIG文件名与M文件名不同,以上语句将不会被成功调用。

    对于GUI实现的第3个步骤——回调函数编程将在下一节向读者介绍。

    完成GUI的创建工作后,使用File菜单下的“Save”或“Save as”选项将用户界面保存为一个FIG文件。当用户保存或激活图形窗口时,GUIDE将自动创建应用程序M文件。执行M文件来显示GUI。对于那些用户尚未实现,而GUIDE在M文件中保存原型的回调函数,运行MATLAB 7.0时将返回一个消息说明这些函数尚未实现。

    12.5 编程设计GUI

    在前面的基础上,本节将主要介绍GUI的深入编程方法。首先将说明应用程序M文件系统生成代码的具体含义以及利用句柄结构体管理GUI数据的方法,然后介绍GUI组件回调函数的类型和回调函数的中断方法,最后说明如何控制GUI图形窗口的行为。

    12.5.1 M文件以及GUI数据管理

    1.应用程序M文件理解

    GUI包含许多可以使软件与用户中断进行交互的用户界面组件,GUI的实现任务之一就是控制这些组件如何响应用户的行为。对应用程序M文件代码进行详细分析的目的就是要通过了解GUIDE创建应用程序M文件的功能,从而实现GUI的规划。

    MATLAB 7.0通过创建应用程序M文件为GUI控制程序提供一个框架。这个框架孕育着一种高效而坚固的编程方法,即所有代码(包括回调函数)都包含在应用程序M文件中,这就使得M文件仅有一个入口可以初始化GUI或调用相应的回调函数以及GUI中希望使用的任意帮助子程序。无论用户是否使用GUIDE来创建应用程序M文件,这里所说的编程技术对用户进行GUI编程都是有用的。

    (1)回调函数自动命名

    GUIDE给添加到应用程序M文件中的回调子函数自动命名。GUIDE还将Callback属性值设置为一个字符串使用户激活控件时该子函数能够被调用。

    首先说明GUIDE如何为回调子函数命名。当用户在GUI界面中添加一个组件时,GUIDE为该组件的Tag属性指定一个用来生成回调函数名称的值。例如,假设用户添加到界面中的第一个按钮被称为pushbutton1,当用户保存或激活图形窗口时,GUIDE在应用程序M文件中添加一个名为pushbutton1_Callback的回调子函数。如果用户为该按钮定义了ButtonDownFcn属性,则相应的回调子函数的名称为pushbutton1_ButtonDownFcn。

    GUIDE通过指定回调字符串为回调子函数命名。当用户第一次为GUI界面添加一个组件时,其Callback属性被设置为字符串<automatic>,当用户保存或激活GUI时,该字符串将通知GUIDE使应用程序M文件中相应的回调子函数名来替换该字符串。

    (2)应用程序M文件的执行路径

    应用程序M文件根据GUI调用文件时所传递的参数类型来决定有待执行的行为。例如,如果不向M文件传递任何参数,则调用M文件将会发布该GUI(如果此时用户指定了一个M文件的输出参数,那么M文件将返回GUI图形窗口的句柄);如果使用一个子函数名作为传递给M文件的第一个参数,那么调用M文件将会执行指定的子函数(通常是回调子函数)。

    应用程序M文件包含一个调度函数,该函数可以使GUI能够根据调用方式决定执行路径。应用程序M文件调度函数的功能是通过在if语句中使用feval函数实现的。在调用M文件时,feval函数将执行字符串参数所指定的子函数。feval函数在一个try/catch调试语句块中执行,这是因为当GUI视图调用不存在的子函数(找不到与传递参数名称相同的子函数)或调用发生错误时能够得到正确的处理。以下是GUIDE生成的调度函数功能代码(用户不能够修改):

    alt

    任何由子函数返回的输出参数都将通过该函数返回GUI。虽然GUIDE生成的回调子函数的参数是明确的,但是参数列表的长度是变化的,这是由于输入参数varargin其实可以是多个参数,用户可以在调用M文件时通过该参数给被调用的子函数赋予任意多个用户所需的参数。用户可以通过编辑Callback属性字符串来传递额外的参数。

    (3)GUI初始化

    首先,应用程序M文件使用openfig命令来装载GUI图形窗口,格式如下:

    alt

    一定要注意这个语句打开的FIG文件名是源于应用程序M文件的(mfilename命令返回当前执行的M文件名),如果用户使用由GUIDE创建的应用程序M文件,那么用户必须保证FIG文件与M文件同名。参数reuse决定任何时候都只能由一个GUI实例在运行。

    应用程序M文件自动包含一些管理GUI的有效技术。

    · 单个/多个实例控制。当设计GUI时,用户必须明确选择是否允许GUI图形窗口的多个实例窗口存在。如果选择允许,也就是说同一时刻只能有一个实例存在,那么以后视图创建另一个GUI的操作将仅仅导致已存在的GUI出现在其他窗口之上。很多信息对话框(尤其是模态对话框)只能同时存在一个实例,因为对于用户的某种特定行为,对话框只能提示一次。GUIDE界面设计编辑器实际上是一个允许多个实例存在的GUI,设计这个GUI的目的是使用户能够同时打开多个界面;

    · GUI图形窗口在屏幕中的位置不受目标计算机屏幕和分辩率的影响。应用程序M文件使用movegui命令来确保GUI图形在目标计算机的屏幕上可见,使GUI不受计算机屏幕的尺寸和分辨率的影响。如果指定的窗口位置将导致GUI窗口位于屏幕之外,那么movegui命令将窗口移动到屏幕中离指定位置最近的地方。movegui语句格式如下:movegui(fig, 'onscreen')。其中fig是由openfig命令返回的GUI图形窗口句柄;

    · 自动创建GUI组件句柄结构体;

    · 自动命名Tag属性、生成子函数原型并指定回调属性字符串。当用户发布GUI时,应用程序M文件创建一个包含所有GUI组件句柄的结构体,然后将该结构体保存在图形窗口的应用程序数据中以备将来的使用。句柄结构体的域名与相应对象的Tag属性值一致。

    用户可以使用相同的方法访问隐藏的图形窗口句柄,例如假设窗口的Tag属性为figure1,则handles.figure1就是图形窗口的句柄。

    应用程序M文件使用guihandles和guidata来创建并存储句柄结构体:

    alt

    注意只有那些Tag属性值为有效字符串的对象的句柄才能够保存在句柄结构中。可以使用isvarname来确定字符串是否有效。

    句柄结构体是传递给所有回调函数的参数之一,因而用户可以使用这个结构体来保存数据并在子函数之间传递。

    · 单个M文件同时包含GUI初始化和回调函数执行代码。

    2.GUI数据管理

    GUIDE使用保存在GUI图形窗口中的应用程序来定义和实现数据的存储和获取机制。GUIDE使用这个机制来存储一个包含所有GUI组件对象句柄的结构体。由于这个结构体将传递每一个对象的回调函数,所以该结构体也可以用来保存其他数据。

    12.5.2 回调函数的使用方法

    1.回调函数类型

    实现一个GUI的首要机制就是构成用户界面的用户控件的回调函数进行编程。除了用户控件的Callback属性,还可以使用其他一些属性来定义回调函数。

    (1)所有图形对象的回调函数属性

    所有图形对象都有3个能够定义回调函数的属性:

    · ButtonDownFcn:当用户将鼠标放置在某个对象或对象相邻的5个像素范围内时,如果单击鼠标左键,MATLAB 7.0将会执行回调函数;

    · CreatFcn:MATLAB 7.0将在创建对象时调用回调函数;

    · DeleteFcn:MATLAB 7.0在删除对象之前调用回调函数。

    (2)图形窗口的回调属性

    图形窗口有如下所述的几种额外的用来执行相应用户行为的属性。

    · CloseRequestFcn:当请求关闭图形窗口时MATLAB 7.0将执行这个回调函数;

    · KeyPressFcn:当用户在图形窗口内按下鼠标键时MATLAB 7.0将执行这个回调函数;

    · ResizeFcn:当用户重画图形窗口时MATLAB 7.0将执行这个回调函数;

    · WindowButtonDownFcn:一旦用户在图形窗口内无控件的地方按下鼠标键时,MATLAB 7.0就会执行这个回调函数;

    · WindowButtonMotionFcn:当用户在图形窗口中移动鼠标时,MATLAB 7.0将执行这个回调函数;

    · WindowButtonUpFcn:当用户在图形窗口中释放鼠标键时,MATLAB 7.0将执行这个回调函数。

    MATLAB 7.0将根据用户的行为来判断究竟执行哪个回调函数。单击一个有效的用户控件将会阻碍任何ButtonDownFcn和WindowButtonDownFcn回调函数的执行,而如果用户单击一个无效用户控件、图形窗口或其他定义了回调函数的图形对象时,MATLAB 7.0将首先执行图形窗口的WindowButtonDownFcn函数,然后再执行鼠标单击对象的ButtonDownFcn函数。

    2.回调函数执行中断

    默认情况下MATLAB 7.0允许正在执行的回调函数被后来调用的回调函数中断。例如,假设用户创建了一个在装载数据时能够显示一个进展条的对话框,这个对话框包含一个取消按钮可以组织数据的装载操作,那么取消按钮的回调函数将会中断正在执行的数据装载子函数。某些情况下用户可能不希望正在执行的回调函数被用户的行为中断,例如,在重新显示一幅图形之前,可能会需要使用一个数据分析工具进行数据流长度计算。假设用户行为可以中断回调函数的执行,此时如果用户无意中单击鼠标使回调函数执行中断,那么就可能导致MATLAB 7.0在返回原来的回调函数之前发生状态改变,引起执行错误。

    (1)可中断设置

    所有图形对象都有一个控制其回调函数能否被中断的属性Interruptible,该属性的默认值为on,表示回调函数可以中断。然而MATLAB 7.0只有在遇到一些特定的命令(drawnow、figure、getfreame、pause和waitfor)时才会执行中断,转而查询事件序列,否则将会继续执行正在执行的回调函数。在回调函数中出现的计算或指定属性值的MATLAB 7.0命令将会被立即执行,而影响图形窗口状态的命令将被放置在事件序列中。事件可以由被任何导致图形窗口重画的命令或用户行为引发,例如定义了回调函数的鼠标移动行为。仅仅当回调函数执行完毕或回调函数包含drawnow、figure、getframe、pause和waitfor命令时,MATLAB 7.0才进行事件序列的处理。

    如果在回调函数的执行过程中遇到上述某个命令,MATLAB 7.0将先执行的程序挂起,然后处理事件序列中的事件。MATLAB 7.0控制事件的方式依赖于事件类型和回调函数对Interruptible属性的设置。只有在当前回调对象的Interrupotible属性值为on的情况下,导致其他回调函数执行的事件才可以真正执行回调函数;导致图形窗口重画的事件将无视回调函数的Interruptible属性值而无条件地执行重画任务;对象的DeleteFcn属性和CreatFcn属性或图形窗口的CloseRequestFcn属性或ResizeFcn属性定义的回调函数将无视对象的Interruptible属性而中断正在执行的回调函数。

    所有对象都具有一个BusyAction属性,该属性决定了对于在不允许中断的回调函数执行期间发生的事件的处理方式。BusyAction有两种可能的取值。

    · queue:将事件保存在事件序列中并等待不可中断回调函数执行完毕后处理;

    · cancel:放弃该事件并将事件从序列中删除。

    (2)回调函数执行期间的事件处理

    以下几种情况描述了MATLAB 7.0在一个回调函数的执行期间是如何处理事件的。

    · 如果遇到了drawnow、figure、getframe、pause、waitfor命令中的一个命令,那么MATLAB 7.0将该回调函数挂起并开始处理事件序列;

    · 如果事件序列的顶端事件要求重画图形窗口,MATLAB 7.0将执行重画并继续处理事件序列中的下一个事件;

    · 如果事件序列的顶端事件将会导致一个回调函数的执行,MATLAB 7.0将判断回调函数被挂起的对象是否可中断。如果回调函数可中断,MATLAB 7.0执行与中断事件相关的回调函数;如果该回调函数包含drawnow、figure、getframe、pause、waitfor命令之一,那么MATLAB 7.0将重复以上步骤;如果回调函数不可中断,MATLAB 7.0将检查事件生成对象的BusyAction属性;如果该属性值为queue,MATLAB 7.0将事件保留在事件序列中;如果cancel则放弃该事件;

    · 当所有事件都被处理后,MATLAB 7.0恢复被中断函数的执行。

    这些步骤都一直持续到回调函数执行完毕为止。当MATLAB 7.0返回命令窗口时,所有残余的事件都已经被处理了。当然,由于序列中事件的类型不同,以上步骤有可能不一一执行。

    12.5.3 图形窗口的行为控制

    在设计GUI时,需要考虑显示GUI时图形窗口将怎样展开,以及一个GUI图形窗口的行为是用户所需要的。考虑以下几种情况:

    · 一个实现图形注释的工具GUI通常设计成其他MATLAB 7.0执行任务可访问的GUI,这个工具可能一次只能对一幅图形进行注释,所以每一幅图形都需要一个新的工具实例;

    · 一个对话框,能向用户发出询问并阻止MATLAB 7.0运行直至用户作出回答。但是用户可能需要通过观察其他的MATLAB 7.0窗口获得信息后才能够对该对话框作出回答。

    · 一个警告用户其指定的操作将会破坏文件的对话框,该对话框能够执行用户所需的操作前强迫用户作出回答。此时的图形窗口既被阻止又是模态的(用户不可观察其他窗口)。

    以下3种技术能够有效地实现以上GUI的设计要求:

    · 允许单个或多个GUI实例同时运行;

    · 在显示GUI时阻止MATLAB 7.0的运行;

    · 使用模态图形窗口使用户只能与当前执行的GUI进行交互。

    模态图形窗口能够捕捉在MATLAB 7.0窗口任何可见位置处发生的键盘和鼠标事件。这就意味着一个模态图形窗口能够处理用户与任何组件的交互操作,但是不允许用户访问其他的MATLAB 7.0窗口(包括命令窗口)。另外,除非删除该模态窗口,否则窗口将始终位于窗口堆栈的最上方。如果用户希望在进行MATLAB 7.0其他操作之前必须先响应GUI,那么最好使用模态图形窗口。

    下面说明如何使一个GUI窗口模态化。事实上设置图形窗口的WindowStyle属性为modal就可将图形窗口模态化。用户可以使用属性检查器,也可以通过在应用程序M文件的初始化阶段调用以下语句进行属性设置:

    alt

    在这个语句中,set函数使用由openfig函数返回的句柄实现设置工作。

    如果要释放模态窗口的控制权,可以在模态窗口GUI的回调函数中使用以下方法之一:

    · 删除窗口:delete(figure_handle)

    · 使窗口不可见:set(figure_handle, 'Visible', 'off');

    · 修改窗口的WindowStyle属性值为normal:set(figure_handle, 'WindowStyle', 'normal')。

    用户还可以在一个模态窗口按ctrl+C键将该窗口转换为普通窗口。

    无论使用哪一种方法释放模态窗口控制权都需要获得该窗口的句柄。由于大多数的GUI将图形窗口句柄隐藏以避免无意识的访问,所以在回调函数中使用gcbf命令能够最有效地获取窗口句柄。gcbf返回当前回调对象所在窗口的句柄,这个句柄就是释放控制权所需的句柄。例如,假设用户对话框包括一个用来关闭对话框的按钮,其回调函数末尾处将调用delete来删除对话框,示例代码如下:

    alt

    12.6 图形用户界面设计实例

    实现一个用来显示名字和电话号码的GUI,该GUI中输入的名字和电话号码都保存在一个MAT文件中。要求该GUI可以添加新条目并将该条目保存在同一个MAT文件或一个新的MAT文件中。

    12.6.1 图形界面的实现

    要实现图形界面这个问题要用到以下几种GUI编程技术:

    · 使用打开和保存对话框的方法为使用者提供寻址和打开地址簿MAT文件、保存MAT文件修改、创建新地址簿的方法;

    · 定义GUI菜单回调函数;

    · 使用GUI句柄结构体保存和传递全局数据(名字和电话号码);

    · 使用GUI图形窗口重画函数实现重新显示工作。

    这里仅仅介绍GUI组态和界面设计工作,关于回调函数的编程方法将在下一节介绍。下面就介绍其具体解决方法。

    步骤一:GUI组态。打开GUI应用程序选项对话框,进行如下设置:

    · 重画行为:User-spcified;

    · 命令行可访问性:Off;

    · 同时生成FIG文件和M文件;

    生成回调函数原型:Generate callback fuction prototypes;

    同时只允许一个实例运行:Application allows only one instance to run。

    步骤二:进行界面设计。在命令行中键入guide,选择空白模板。

    可以按照用户的喜好任意设计GUI界面并排列各组件,例如如图12-33所示的GUI界面。使用“Prev”和“Next”按钮在地址簿中的条目向前或向后翻页。

    alt

    图12-33 地址簿的GUI界面

    步骤三:设计菜单。地址簿GUI包含一个File菜单,其菜单项为Open,GUI用该菜单项来装载地址簿MAT文件。当用户对地址簿进行修改时,用户需要保存被修改的MAT文件或将这些修改保存为一个新的MAT文件,使用File菜单的“Save”和“Save as”菜单项使用户能够实现这些要求。

    步骤四:对各个组件的属性进行设置。在界面设计编辑器中使用菜单命令打开属性检查器,根据用户的界面需要设置组件的Tag、callback等属性。必须设置“Prev”和“Next”按钮的Callback属性以及Save和Save as菜单项的回调函数,使它们分别能够调用同一个回调函数Save_Callback、Prev_Next_Callback。另外,将窗口的ResizeFcn属性设置为下面的格式:

    alt

    步骤五:保存GUI。激活GUI界面,确保界面符合用户要求。设计满意后保存GUI。

    步骤六:执行GUI。运行地址簿GUI的应用程序M文件。可以对该M文件进行反复调试使之符合用户的要求。

    12.6.2 行为控制的实现

    前一节已经介绍了地址簿界面的实现,下面来讲如何实现地址簿GUI的行为控制。在GUI中最重要的技术之一就是保证信息跟踪正确并使不同的子函数能够获得该信息。这里所指的信息包括当前MAT文件名,MAT文件名中的名字和电话号码;一个表明当前名字和电话号码,一个表明当前姓名和电话号码的索引指针(用户在地址簿中翻页时该指针必须刷新),图形窗口位置和大小,所有GUI组件的句柄。为了能够正确地管理全局数据,地址簿GUI的行为控制主要实现以下几个方面:

    · 发布GUI:由于GUI需要在其他MATLAB 7.0任务运行的同时执行,所以GUI需要设置成不可中断、非模态的;

    · 将一个地址簿装入阅读器中:有两种方式可以将地址簿MAT文件装入GUI中:发布GUI时使用参数指定MAT文件,如果用户不使用参数,则MATLAB 7.0装入默认的地址簿文件addrbook.mat;用户使用File菜单的Open选项来装载其他MAT文件;

    · 管理菜单回调行为:选择Open菜单项后将显示一个对话框(uigetfile),使用户能够浏览文件。对话框将返回文件名及其路径,并将返回值传递给fullfile,从而确保路径对于任意的操作系统而言是合理构建的;Save和Save as菜单用来保存对MAT文件的修改;Creat New菜单将简单地清除Contact Name和Contact Phone#编辑框的内容以便添加新的名字和电话号码。在添加新条目后,用户使用Save或Save as菜单项来保存地址簿。

    · 管理按钮组件回调行为:Contact Name编辑框显示地址簿条目的姓名,如果用户键入新的姓名,若当前地址簿中已存在该姓名,相应的电话将被显示;如果该姓名不存在,则显示一个询问对话框(questdlg)确认是否创建一个新的条目,如果不创建则返回到先前显示的姓名;Contact Phone#编辑框显示与Contact Name编辑框条目相匹配的电话号码。如果键入一个新的数字并回车,回调函数将发布一个询问对话框确认是否要对已存在的号码进行修改。按钮控件Prev和Next用来实现地址簿条目间的翻页。

    · 地址簿将自定义重画函数,允许用户增大窗口的宽度以适应长姓名和电话号码,但是不允许缩小窗口宽度或改变窗口高度(这一限制不会限制GUI的使用,并且简化了必须保持窗口尺寸与组件大小相匹配的重画函数代码)。用户重画窗口并释放鼠标后将执行重画函数,重画后的窗口尺寸将被保存下来。

    下面具体介绍解决方法:

    步骤一:发布GUI。用户可以不使用参数来调用应用程序M文件,在这种情况下GUI使用默认的地址簿MAT文件。用户也可以在参数中指定一个MAT文件,这就需要修改默认的应用程序M文件GUI初始化代码。修改后的初始化代码如下:

    alt

    alt

    步骤二:将一个地址簿装入阅读器中。首先要确认MAT文件。作为一个有效的地址簿文件,MAT文件必须包含一个称作Addresses的结构体,该结构体有两个域,分别称为Name和Phone。Check_And_Load子函数将按照以下步骤来确认并装载这些数据。装载指定的或默认的文件;判断MAT文件是否为一个有效的地址簿文件,如果有效则显示数据,否则显示错误对话框;对于有效的MAT文件,返回1,否则返回0(该返回值由Open菜单项回调函数使用);在句柄结构中保存条目MAT文件名和Addresses结构体;指示当前所显示的名字和地址的索引指针。

    Check_And_Load设置的代码如下:

    alt

    alt

    步骤三:控制菜单回调行为。

    首先看Open菜单项回调函数Check_And_Load,该函数将确认并装载新的地址簿。Open菜单项的回调函数代码如下:

    alt

    再来看Save和Save as回调函数,该回调函数使用菜单项的Tag属性来判断究竟时Save还是Save as为回调对象,也就是说哪一个对象的句柄作为回调函数的第一个参数。如果用户选择的是Save菜单项,则调用save命令来保存MAT文件的新名字和电话号码;如果选择的是Save as,则显示一个指定保存目标文件名的对话框,用户可以选择一个已有的文件,也可以输入新的文件名,对话框将返回文件名和路径。

    · 使用fullfile来创建一个与平台武官的路径名;

    · 调用save来保存MAT文件中的新数据;

    · 刷新句柄结构体以包含新的MAT文件名;

    · 调用guidata保存句柄结构体。

    Save_Callback回调函数代码如下:

    alt

    alt

    第三步设置Creat New菜单。Creat New菜单的回调函数主要将编辑框的String属性设置为空。其代码如下:

    alt

    步骤四:控制组件回调行为。

    首先看Contact Name编辑框回调函数。回调函数使用句柄结构体来访问地址簿的内容并获得索引指针,这样回调函数就能够判断用户修改之前编辑框中显示的是什么姓名。索引指针表明当前显示的姓名位置,在发布GUI时由Check_And_Load函数来添加地址簿和索引指针。如果用户添加了一个新的条目,回调函数将新的姓名添加到地址簿中并刷新索引指针来显示新的数值。刷新后的地址簿和索引指针再一次被保存在句柄结构体中。Contact Name编辑框的回调函数代码如下:

    alt

    alt

    alt

    再来看Contact Phone#回调函数。和Contact Name编辑框类似,这个回调函数使用索引指针来刷新地址簿中的新数值或恢复为上一次的显示内容。所有当前地址簿和索引指针都保存在句柄结构体中,因而其他回调函数可以获得这些数据。以下是Contact Phone#编辑框的回调函数代码:

    alt

    alt

    最后看“Prev”和“Next”按钮的回调函数。回调函数定义一个额外的参数str来表明被单击的按钮是“Prev”还是“Next”。“Prev”按钮的回调字符串以“Prev”为最后一个参数,而“Next”按钮则以“Next”为最后一个参数。在case语句中用str的数值来区分并实现每一个按钮的功能。Prev_Next_Callback回调函数将从句柄结构体中获取当前的指针和地址,并且根据被选择的按钮来增加或减小索引指针的数值,并显示相应的名字和电话号码,最后将在句柄结构体中存储新的索引指针数值并使用guidata来保存刷新后的结构体。

    Prev_Next_Callback回调函数代码如下:

    alt

    alt

    步骤五:自定义重画函数。重画函数将对以下几种可能发生的情况进行处理。

    · 改变宽度:如果新的宽度大于固有宽度,图形窗口将被设置为新的宽度。Contact Name编辑框的尺寸随着窗口宽度的变化而变化。改变编辑框的宽度需要修改编辑框的Units为normalized,重新设置编辑框的宽度为窗口宽度的78.9%,将Units重新设置为characters。如果新的宽度小于固有宽度,窗口被设置为固有宽度;

    · 改变高度:如果用户视图修改窗口高度,高度始终被设置为固有高度。但是由于用户释放鼠标时会调用重画函数,所以重画函数不能总判断GUI在屏幕中的固有位置。因此,重画函数对窗口垂直分量作鼠标释放时的垂直位置+鼠标释放时的高度-原始高度的设置。

    当窗口从底部被重画时,窗口将保持原位;从顶部重画时,窗口将移动到鼠标释放处的位置。

    重画函数调用movegui函数来确保无论用户在何处释放鼠标,重画后的窗口始终保持在屏幕中。第一次发布GUI时,GUI窗口的大小和位置由Position属性决定,用户可以使用属性检查器来设置这个属性。

    重画函数代码如下:

    alt

    alt