第21章 创建图形用户界面 用户界面是人,即用户与计算机或计算机程序的接触点或交互方式,是用户与计算机进行信息交流的方式。计算机在屏幕显示图形和文本,若有扬声器还可产生声音。用户通过输入设备,如:键盘、鼠标、跟踪球、绘制板或麦克风,与计算机通讯。用户界面设定了如何观看和如何感知计算机、操作系统或应用程序。通常,多是根据悦目的结构和用户界面功能的有效性来选择计算机或程序。 图形用户界面或GUI是包含图形对象,如:窗口、图标、菜单和文本的用户界面。以某种方式选择或激活这些对象,通常引起动作或发生变化。最常见的激活方法是用鼠标或其它点击设备去控制屏幕上的鼠标指针的运动。按下鼠标按钮,标志着对象的选择或其它动作。 与上一章讨论MATLAB句柄图形功能的相同方式,它让用户按规定设计MATLAB显示信息的方法,本章所描述的图形用户界面的功能,它让用户定制用户与MATLAB的交互方式。命令窗口不是唯一与MATLAB的交互方式。 本章将说明图形句柄uicontrol 和uimenu对象的使用,把图形界面加到MATLAB的函数和M文件。uimenu对象能在图形窗口中产生下拉式菜单和子菜单。uicontrol对象能建立如按钮,滚动条,弹出式菜单以及文本框等对象。 MATLAB在demo命令中包含了GUI功能的极好例子。 >> demo 研究该命令,以了解uimenu和uicontrol如何给MATLAB函数提供交互输入。 21.1 谁创建图形界面GUI?为什么? 在运行了demo例子后,很可能会问“为什么要在MATLAB中建立一个GUI?”这是一个很好的问题,简单的回答是可能并不需要.使用MATLAB来分析数据,求解问题,绘制结果的绝大多数的人,并不会发现GUI工具很有用。 但另一方面,GUI可以在MATLAB中生成非常有效的工具和应用程序,或是建立演示工作的交互式界面。 生成用户图形界面的最常见的理由:  编写一个需多次反复使用的实用函数,菜单、按钮、文本框作为输入方法具有意义;或  编写函数或开发应用程序供别人使用;或  创建一个过程、技术或分析方法的交互式示例;或  认为GUI的简洁,性能良好,并且想实践一下。 许多基于GUI的工具函数包含在精通MATLAB工具箱中,将在后续章节进行讨论。其它由MATLAB用户编制的工具和实用程序装入MATLAB的GUI函数。工具的大多数可在Mathworks 匿名FTP节点和其它资源中获得。 在我们开始讨论之前,记住对“句柄图形”的理解是设计和实现GUI的先决条件,如果你跳过了前一章,现在应重新回去阅读。 21.2 GUI对象层次结构 正如我们在上一章所展示的那样,由图形命令生成的每一事物是一个图形对象。图形对象不仅包括uimenu和uicontrol对象,而且还包括图形、坐标轴和他们的子对象。让我们从另一个角度来看这一层次结构。计算机的屏幕本身是根结点,图形是根对象的子对象,坐标轴,uimenu ,uicontrol是图形的子对象。 根可以包括多个图形,每个图形含有一组或多组坐标轴以及其子对象,每个图形也可以有一个或多个与坐标轴无关的uimenu和uicontrol。虽然uicontrol对象无子对象结点,但他们确实具有多种类型。uimenu对象常将其它的uimenu对象作为其子对象。  图21.1 GUI对象层次结构图 运行MATLAB的不同型号的计算机或平台上,产生不同的图形显示。Unix工作站使用不同的X Window系统,具有几个窗口程序,如mwn或twm以控制显示的布局。PC机靠Microsoft Windows或Windows NT进行窗口管理,Macintosh计算机用Macintosh工具箱程序作窗口。虽然在各种平台上,显示看起来有很大的不同,但在很多的情况下,句柄图形的编码是一致的。MATLAB在内部处理平台和窗口系统的差别。体现句柄图形例程的函数,包括应用uimenu和uicontrol对象的函数,通常运行在所有平台。存在已知差异的地方将在本章后面给出。 21.3 菜单 在每一个窗口系统中使用菜单让用户选择命令和选项。通常在显示屏或窗口的顶部有一菜单条。移动鼠标指针到菜单标志上按下鼠标按键,顶层菜单就被选中,一列菜单项就从菜单标志拉下来。这种款式菜单就叫下拉式菜单。按下鼠标将指针移动至菜单项并松开鼠标,则完成菜单项的选择。MS-Windows 和一些X Window系统平台还提供另一种选择菜单的方法。在顶层菜单上按下并松开鼠标,或称单击鼠标,则打开下拉菜单。然后,移动鼠标指针至下拉菜单项再次单击鼠标,就选择菜单项。在下拉菜单中选择一项就引起动作的发生。 一个菜单项还可用自己的菜单项列表而作为子菜单。子菜单项在子菜单的标志右边显示小三角或箭头以表示菜单还有更多子菜单项可供选择。如果子菜单的菜单项被选择,另一个具有更多菜单项的菜单显示在此菜单的右边的下拉菜单中。有时这种菜单称之行走菜单。选中其中一个菜单项也引起某些动作的产生。 子菜单可以嵌套,但层次的数目受到窗口系统及有用资源的限制。 菜单的布置 Macintosh在显示屏的顶部使用包含下拉菜单标题的菜单条。选择图形窗口时,菜单条变化以反映激活的图形窗中可利用的选项。标准的Macintosh菜单标题包括Apple、 File、 Edit、 Window 和Help 或Balloon Help。由uimenu所加的菜单标题放在Window和Help之间。如果想从菜单条中删去File、 Edit、和 Window菜单标题,可以使用Set命令。 >> set(Hf_fig1, ‘ Menubar ‘ , ‘ none ‘ ) Apple和Help不可从菜单条中删除。同样,标准的菜单是用以下的命令恢复: >> set(Hf_fig1, ‘ Menubar ‘ , ‘ figure ‘ ) 在Microsoft窗口系统下,菜单条位于图形窗口的顶部。每个图形窗口有自己的菜单条,它包含File,Edit,Window和Help标题。由uimenu所加的菜单标题放在Help之后。可以使用与上面相同的Set命令从菜单条中删去或恢复所有的标准菜单。 在X Window系统工作站上没有MATLAB标准图形窗口菜单的标题。窗口管理器可将菜单条放置在屏幕上每一个窗口上端,但这些菜单条与MATLAB菜单无关。当建立第一个uimenu对象时,MATLAB 在图形窗口的顶部边缘生成自己的菜单条。 建立菜单和子菜单 我们采用函数uimenu建立菜单项。uimenu的句法与其他对象创建函数相似。如, >> Hm_1=uimenu(Hx_parent, ‘ PropertyName ‘ ,PropertyValue,...) 其中Hm_1是由uimenu生成的菜单项的句柄,通过设定uimenu对象的属性值 ‘ PropertyName ' ,PropertyValue这对命令定义了菜单特性;Hx_parent是缺省的父辈对象的句柄,必须是图形和uimenu对象。 uimenu对象中最重要的属性是' Label ' 和 ' Callback ' 。 ' Label ' 属性值是菜单条和下拉菜单项上的文本字符串,以确认菜单项。 ' Callback ' 属性值是MATLAB字符串,当选中菜单项时,它传给eval,用以执行。 菜单举例 下面的例子用函数uimenu将简单菜单加到当前的图形窗口中。这里提出的例子说明如何只用几个MATLAB命令来建立工作菜单。后面的例子将详细地讨论uimenu的命令和属性。 下例用两个下拉菜单将菜单条加到当前窗口中。首先,建立名为Example的顶部菜单输入。 >> Hm_ex=uimenu(gcf, ‘ Label ‘ , ‘ Example ‘ ); 在此菜单下有两个菜单项。第一项标志为Grid,切换坐标轴格栅的状态。 >> Hm_exgrid=uimenu(Hm_ex, ‘ Label ‘ , ‘ Grid ‘ , ‘ Callback ‘ , ‘ Grid ‘ ); 注意,句柄Hm_ex是用于与上层菜单相关联的。这项uimenu按eval的要求出现在上层菜单之下。还需注意的是属性 ‘ Callback ‘ 的值,它是一个带引号的字符串。 Example下的第二项标志为View,并带有子菜单。 >> Hm_exview=uimenu(Hm_ex, ‘ Label ‘ , ‘ View ‘ ); View菜单有两项选择2-D和3-D视图。 >> Hm_ex2d=uimenu(Hm_exview, ‘ Label ‘ , ‘ 2-D ‘ , ‘ Callback ‘ , ‘ view(2) ‘ ); >> Hm_ex3d=uimenu(Hm_exview, ‘ Label ‘ , ‘ 3-D ‘ , ‘ Callback ‘ , ‘ View(3) ‘ ); 注意以上这些是View的子菜单,因为它们指定Hm_exview作为其父对象。 现在,将第二个顶层菜单加到标题为Close的菜单条中。 >> Hm_ex=uimenu(gcf, ‘ Label ‘ , ‘ Close ‘ ); 由该顶层菜单Close加入了两个菜单项。第一项关闭图形窗口,第二项使图形窗口打开,但去掉用户菜单。 >> Hm_clfig=uimenu(Hm_close, ‘ Label ‘ , ‘ Close Figure ‘ , ‘ Callback ‘ , ‘ Close ‘ ); >> Hm_clmenu=uimenu(Hm_close, ‘ Label ‘ , ‘ Remove Menu ‘ ,... ‘ Callback ‘ , ‘ delete(Hm_ex); delete(Hm_ex); drawnow ‘ ); 在精通MATLAB工具箱的脚本文件mmenu1.m中含有上面的例子。所以你可以运行mmenu1.m来验证上例。 菜单属性 如上所示,同句柄图形函数一样,在建立图形对象时可定义uimenu属性,或用set改变属性。所有可设定的属性,包括标题、菜单颜色、甚至回调字符串都可以用set来改变。这种功能十分便于迅速地定制菜单和属性。 表21.1列出了MATLAB 4.2版本中的uimenu对象的属性及其属性值。带有*的属性是非文件式的,使用时需加小心。在括号{}内的属性值是缺省值。 表21.1 Uimenu 对象的属性  Accelerator 指定菜单项等价的按键或快捷键。对于X-windows,按键顺序是Control- 字符;Macintosh系统,按键顺序是Command - 字符或# - 字符  BackgroundColor uimenu背景色,是一个3元素的RGB向量或MATLAB预先定义的颜色名称。缺省的背景色是亮灰色  Callback MATLAB回调字符串,选择菜单项时,回调串传给函数eval;初始值为空矩阵  Checked on: {off}: 被选项的校验标记 校验标记出现在所选项的旁边 校验标记不显示  Enable {on}: off: 菜单使能状态 菜单项使能。选择菜单项能将Callback字符串传给eval 菜单项不使能,菜单标志变灰。选择菜单项不起任何作用。  ForegroundColor uimenu前景(文本)色,是一个三元素的RGB向量或MATLAB预 先定义的颜色名称。缺省的前景色是黑色  Label 含有菜单项标志的文本串。在PC系统中,标记中前面有 ‘ & ‘ ,定 义了快捷键,它由Alt - 字符激活  Position uimenu对象的相对位置。顶层菜单从左到右编号,子菜单从上至 下编号  Separator on: {off}: 分割符 - 线模式 分割线在菜单项之上 不画分割线  *Visible {on}: off: uimenu对象的可视性 uimenu对象在屏幕上可见 uimenu对象不可见  ButtonDownFcn 当对象被选择时,MATLAB的回调串传给函数eval。初始值为空矩 阵。  Children 其它uimenu对象的句柄。  Clipping {on}: off: 限幅模式 对uimenu对象无效果 对uimenu对象无效果  DestroyFcn 仅用于Macintosh 4.2 版本。没有文本说明。  Interrruptible {no}: yes: 指明ButtonDownFcn和CallBack串可否中断 回调不可中断 回调串可中断  Parent 父对象的句柄;如果uimenu对象是顶层菜单,则为图形对象;若 uimenu是子菜单,则为父的uimenu对象句柄  *Select 值为[on|off]  *Tag 文本串  Type 只读对象辩识串,通常为uimenu  UserData 用户指定的数据。可以是矩阵,字符串等等  Visible {on}: off: uimenu对象的可视性 uimenu对象在屏幕上可见 uimenu对象不可见   属性值仅仅定义了uimenu对象的性质并且控制菜单如何显示;它们也决定了选择菜单项所引起的动作。其中某些性质将在下面更详细地讨论。 菜单快捷键 ' Label ' 属性义定了出现在菜单或菜单项中的标志。它也可以用来定义Microsoft Windows系统的快捷键:标志字符串中,在所需字符前加上&,例如: >> Hm_top=uimenu( ‘ Label ‘ , ‘ Example ‘ ); >> uimenu(Hm_top, ‘ Label ‘ , ‘ &Grid ‘ , ‘ CallBack ‘ , ‘ grid ‘ ); 它定义了键盘上G为快捷键。菜单项标志将以Grid形式出现在菜单上。为激活快捷键,在选择图形窗口时按Alt键并按下G键。快捷键不一定是字符串的第一字符。下例中R为快捷键: >> uimenu(Hm_top, ‘ Label ‘ , ‘ G & rid ‘ , ‘ CallBack ‘ , ‘ grid ‘ ); 则标志以Grid形式出现在菜单上。 Macintosh平台用 ‘ Accelerator ‘ 属性而不是 ‘ Label ‘ 来定义快捷键。在Macintosh >> uimenu(Hm_top, ‘ Label ‘ , ‘ Grid ‘ , ‘ Accelerator ‘ , ‘ G ‘ , ‘ CallBack ‘ , ‘ grid ‘ ); 定义G为快捷键,菜单标志以Grid#G的形式出现。为激活快捷键,选择图形窗口时,按Command键或#键并按下G键。 在Macintosh上不可为顶层菜单定义快捷键。另外,已经定义在标准Macintosh上的快捷键,如Command C或# C不撤除标准Macintosh菜单,就不能复制。 在X-Window 系统中定义和使用快捷键与Macintosh相似,但仍存在某些差异。同Macintosh一样,用 ' Accelerator ' 属性而不是 ' Label ' 属性来定义快捷键。但快捷键在菜单上显示不同。例如, >> uimenu(Hm_top, ‘ Label ‘ , ‘ Grid ‘ , ‘ Accelerator ‘ , ‘ G ‘ , ‘ CallBack ‘ , ‘ grid ‘ ); 定义字母G为快捷键。菜单标志常以Grid<Ctrl>-G出现在菜单上。未使用快捷键,需按Control键并按下G键。同Macintosh一样,不可为顶层菜单定义快捷键。 虽然我们没有对此验证,但MATLAB用户手册指出当给定相同命令时,用X的某些工作站动作不一样。如果顶层菜单标志中有带下划线的字符,就可按Meta键并按下下划字符键来选择菜单。如果子菜单项标志中含有带下划线的字符,就按下该字符键来选择菜单项。请参阅键盘使用说明,以为系统确定合适的Meta键。 菜单的外观 影响菜单的布置和外观的三个属性为 ' Position ' , ' Checked ' ,和 ' Separator ' .uimenu对象的 ' Position 属性值是一个整数,它定义了相对于其它菜单和菜单项的位置。在生成菜单时,设定 ' Position ' 属性。菜单条的最左端的菜单条和下拉菜单中的上端菜单项处在位置1。 设置 ‘ Position ' 属性可以重新排列菜单位置。考虑下面的例子: >> Hm_1=uimenu( ‘ Label ‘ , ‘ first ‘ ); % Create two menus >> Hm_2=uimenu( ‘ Label ‘ , ‘ Second ‘ ); >> get(Hm_1, ‘ Position ‘ ) % Check the locations ans= 1 >> get(Hm_2, ‘ Position ‘ ) ans= 2 >> set(Hm_2, ‘ Position ‘ ,1) % Change menu order >> get(Hm_1, ‘ Position ‘ ) % check the locations ans= 2 >> get(Hm_2, ‘ Position ‘ ) ans= 1 注意,当一个uimenu的 ' Position ' 属性改变,就将移动其它uimenu以适应此变化,它们的 ' Position ' 属性均更新。子菜单中菜单选项的编号以同样的方式重新排列。 属性 ' Checked ' 的值使校验标记出现在菜单项标志的左边。缺省值为 ' off ' 。命令 >> set(Hm_item, ‘ Checked ‘ , ‘ on ‘ ) 使校验标记出现在Hm_item uimenu标志的旁边。对于创建代表属性的菜单项,该命令十分有用。例如, >>Hm_top=uimenu( ‘ Label ‘ , ‘ Example ‘ ); >>Hm_box=uimenu(Hm_top, ‘ Label ‘ , ‘ Axis Box ‘ ,... ‘ CallBack ‘ ,[... ‘ if strcmp(get(fca, ‘ ‘ Box ‘ ‘ ), ‘ ‘ on ‘ ‘ ), ‘ ,... ‘ seet(gca, ‘ ‘ Box ‘ ‘ , ‘ ‘ off ‘ ‘ ), ‘ ,... set(Hm_box, ‘ ‘ Checked ‘ ‘ ,...off ‘ ‘ ), ‘ ,... ‘ else, ‘ ,... ‘ set(gca, ‘ ‘ Box ‘ ‘ , ‘ ‘ on ‘ ‘ ), ‘ ,... ‘ set(Hm_box, ‘ ‘ Checked ‘ ‘ , ‘ ‘ on ‘ ‘ ), ‘ ,... ‘ end ‘ ]); 建立了以Axis Box为标志的下拉菜单项。当选中该项时,就运行回调字符串所表示的命令。回调字符串确定了当前坐标轴的 ' Box ' 属性值,并适当地设定坐标轴的 ‘ Box ' 属性及uimenu的 ' Checked ' 属性。该例子可从精通MATLAB工具箱的M脚本文件mmenu2.m中得到。 可以通过改变uimenu的 ' Label ' 属性以反映菜单项当前的状态。下面的例子(在mmenu3.m中)改变了菜单项标志本身,而不是加一个校验标记: >> Hm_top=uimenu( ‘ Label ‘ , ‘ Example ‘ ); >> Hm_box+uimenu(Hm_top, ‘ Label ‘ , ‘ Axis Box ‘ ,... ‘ CallBack ‘ ,[... ‘ if strcmp(get(gca, ‘ ‘ Box ‘ ‘ ), ‘ ‘ on ‘ ‘ ), ‘ ,... ‘ set(gca, ‘ ‘ Box ‘ ‘ , ‘ ‘ off ‘ ‘ ), ‘ ,... set(Hm_box, ‘ ‘ Label ‘ ‘ , ‘ ‘ Set Box On ‘ ‘ ), ‘ ,... ‘ else, ‘ ,... ‘ set(gca, ‘ ‘ Box ‘ ‘ , ‘ ‘ on ‘ ‘ ), ‘ ,... ‘ set(Hm_box, ‘ ‘ Label ‘ ‘ , ‘ ‘ Set box Off ‘ ‘ ), ‘ ,... ‘ end ‘ ]); 使用 ' Separator ' 属性可将下拉菜单分成局部组。如果一个uimenu项的 ' Separator ' 属性是 ' on ' ,则在下拉菜单中显示此项时,此项的上端有一条水平线将其与前面的菜单项隔开。缺省值是 ' off ' ,但在菜单生成时可以改变, >> Hm_box=uimenu(Hm_top, ‘ Label ‘ , ‘ Box ‘ , ‘ Seperator ‘ , ‘ on ‘ ); 或是在以后使用set命令 >> set(Hm_box, ‘ Separator ‘ , ‘ on ‘ ); 顶层菜单忽略 ' Seprator '的属性值。 使用 ' Seperator ' 属性可将下拉菜单项分成若干逻辑组。如果对uimenu项 ' Seperator ' 属性设置为' on ' ,用在一条其上面的水平线来表现该项,将其与前面的菜单项形象地区分开。缺省值为 ' off ' ,但在生成菜单时可以改变, >> Hm_box=uimenu(Hm_top, ‘ Label ‘ , ‘ Box ‘ , ‘ Seperator ‘ . ‘ on ‘ ) 或是在以后使用set命令: >> set(Hm_box. ‘ Seperator ‘ , ‘ on ‘ ) 顶层菜单忽略 ' Seperator ' 属性值。