6.1.2 GUI编程要素——控件、消息与回调函数

6.1.2 GUI编程要素——控件、消息与回调函数

(1)GUI程序运行流程

MATLAB的GUI程序包含两部分:由GUIDE编辑后生成的.fig文件,以及一个同名的.m文件。前者是一个图形(图6-2),由一个窗口和程序界面所需的各种控件如按钮、输入框、绘图区、滑动条等组成(事实上,装载这些控件的窗口本身也是一个控件)。后者与前者配套,主要包括窗口的生成函数和各控制消息的回调函数。

图6-2 MATLAB GUI程序的.fig文件

(a)程序界面;(b).fig文件

图6-3 GUI程序运行流程

GUI程序运行的流程如图6-3所示。①程序首先生成一个窗口;②等待并接收消息;③在接收到消息后,寻找并执行与该消息对应的回调函数;重复②、③两步直到窗口关闭。GUI程序的.m文件只列出了窗口生成函数和消息回调函数,而消息检测与响应等内核部分由系统自动完成,无须用户参与,因此并没有体现出来。开发者理解GUI程序的运行流程,对于开发GUI程序是很有帮助的。

如前所述,GUIDE可以帮助程序开发者完成窗口生成、消息检测等工作,因此需要程序开发者完成的工作其实很简单,即在窗口上设计控件、定制控件的消息,以及编制与消息对应的回调函数。下面分两部分叙述GUI界面控件设计及消息与回调函数的设计。

(2)GUI界面控件设计

在命令栏中输入“guide ”,选择“Blank GUI (Deafult)”,进入MATLAB GUI编辑界面(图6-4)。用鼠标拖动左侧控件容器中的一个控件,将其放置在右侧布局区,则初步完成了一个控件的设计。重复上述操作,可以完成界面设计。

控件容器中包含了14种GUI编程所需的控件。用鼠标在每个控制上停留片刻,会显示该控件的名称。初学者也可将控件拖到布局区放大,对于一个常操作Windows程序的用户来说,只需看到每个控件的样子,基本就了解了该控件的用途和功能。也可以通过设置“File”→“Preference”,勾选“Show names in component palette”,使GUI的控件选择区显示每个控件的名称,如图6-5所示。

图6-4 GUI编辑界面

图6-5 GUI的控件选择区显示控件名称

仅靠鼠标拖动控件所设计完成的界面可能很乱,为了使程序界面更美观,还需要对各控制的大小、位置、色彩、字体等进行精确控制。在布局区通过鼠标拖动和使用控件调整工具排布可以进行控件大小和位置的控制,更精确的控制还可以用设置控件的属性来完成。

将选中的控件拖入控件布局区后,双击控件,即弹出控件属性查看器,如图6-6所示。

图6-6 属性查看器

通过修改控件的属性,可以实现对控件的精细控制。例如,通过设置pushbutton的String、FontSize、FontNane等属性,可以修改button显示的文字、字体及文字大小等,如图6-7 (a)所示。甚至还可以通过设置button的“CDATA”属性让button显示一个图案,而不是文字,如图6-7 (b)所示。

图6-7 通过属性对button进行修改

(a)修改button属性;(b)让button显示图案

Tag属性是一个控件的“姓名”(要和string相区分,string只是控件显示的内容),编程时,在任何时候调用一个控件时,都要通过Tag属性来定义控件的“姓名”。在默认情况下,GUIDE会给控件赋一个通用的按序号排列的Tag,如“pushbutton9”。这样命名虽然不影响应用,但在编程时会引起一些不方便。与编程时变量命名一样,虽然“Data 1 ”“Data2”等变量命名方式也没问题,但如果命名为“name ”“age”等,则显然更方便一些。因此,建议编程时将控件的Tag属性修改为有意义的形式,如修改Push button控件的Tag属性为“ Button Read”就比“push_Button9”等属性更好一些。在修改完Tag属性后,GUIDE会自动将回调函数的名称修改为

function Button_Read_ Call back(hObject,eventdata,handles)

在有多个控件的程序中,这种命名方式大大提高了程序的可读性,便于程序的编写和后期维护。

(3)GUI消息与回调函数

GUIDE自动地为控件添加若干消息,如当鼠标单击一个push_button时,会向窗口发出一个特定的消息。当“程序热点”位于该button时,按Enter键会向窗口发出同样的消息。开发人员还可以定义一些快捷键等,让控件发出与鼠标单击时的同样的消息。对于GUI程序来说,上述不同操作是等效的。

回调函数是控件的灵魂,一个控件如果没有回调函数,就不能对操作做出反应,就是没有用处的“空架子”。设计回调函数分为两步:第一步是建立消息同回调函数之间的联系,即建立二者之间的映射;第二步是编写回调函数的内容,让回调函数执行应有的操作。

在GUIDE中,建立回调函数比较简单:选择PushButton控件,然后单击右键,选择“view Callbacks”→“Call back”,即可生成回调函数,并自动进入回调函数编辑状态。其代码如下所示。

建立好回调函数的映射后,就可进入.m文件进行编程,按需求编写相应的回调函数的功能。编写回调函数时,所有的编程规则、方法与技巧和之前的过程化编程相同。

(4)GUI程序中的数据获取及传递

如前所述,GUI程序的.m文件由一个一个独立的函数构成,与其他编程语言一样,变量的有效范围仅存在于一个函数之内,在函数之外是无效的。因此,从一个函数内获取函数之外的数据,或者想将数据传递到另一个函数,需要一些特殊的方法或技巧。

在GUI编程中,最常遇到的是对控件属性的获取及修改。例如,在一个用于计算的push_button的回调函数中,需要知道一个输入框控件中的文字,以便转换成数字后进行计算,再将结果输入一个文本显示控件中。这就要求能够获取该控件的句柄,以便对该控件进行操作。在GUI程序的回调函数中都会传递一个参数—handles,该参数包括了窗口中所有控件的句柄,是一个“句柄包”(因此称为handles)。有了该参数,调用任何控件的属性都是很容易的。例如:

handles. button_Calcul ate→string

就表示一个“姓名”(tag属性)为button_Calculate的push_button的string属性的句柄。

GUI编程中,有时还需要进行数据的传递,例如,将计算的中间结果传递到另一个回调函数中继续进行计算等。用全局变量可完成这一操作。在GUI程序中,也可以定义全局变量,但是对于结构化编程来说,全局变量会给程序设计和维护带来很多问题,因此推荐用另一种方法完成数据的传递。

GUI所有控件的属性中都有一项“userdata”。这个属性是系统专门留给用户来使用的。程序设计时,通过set和get函数可以在这个属性中存取任何格式的数据。虽然这个属性中只能存取一个变量,但从前面的内容可知,MATLAB可以将多个不同类型的变量组合在一个Cell类型的变量中,因此,理论上讲,通过一个控件的“userdata”属性可以传递任意多个不同类型的变量。