独立的回调函数
建立GUI函数的一个有效方法是编写独立的回调函数,专门执行一个或多个回调。函数使用的对象句柄和其它变量可以作为参量传递,必要时回调函数可返回值。
考虑先前的一个例子,建立一个方位角的滑标,以脚本文件来实现。
% setview.m script file
vw=get(gca,' View ' );
Hc_az=uicontrol(gcf,' Style ',' slider ',...
' Position ',[10 5 140 20],...
' Min ',-90,' Max ',90,' Value ',vw(1),...
' Callback ',[...
' set(Hc_cur,' String ',num2str(get(Hc_az,' Value ' ))),',..
' set(gca,' View ',[get(Hc_az,' Value ' ) vw(2)]) ' ]);
Hc_min=uicontrol(gcf,' style ',' text ',...
' Position ',[10 25 40 20],...
' String ',num2str(get(Hc_az,' Min ' )));
Hc_max=uicontrol(gcf,' Style ',' text ',...
' Position ',[110 25 40 20],...
' String ',num2str(get(Hc_az,' Max ' )));
Hc_cur=uicontrol(gcf,' Style ',' text ',...
' Position ',[60 25 40 20],...
' String ',num2str(get(Hc_az,' Value ' )));
下面是同样的例子。作为一个函数,采用 ' Tag ' 属性来辨别控制框,并使用独立的M文件来执行回调。
funtion setview( )
vw=get(gca,' View ' );
Hc_az=uicontrol(gcf,' Style ',' Slider ',...
' Position ',[10 5 140 20],...
' Min ',-90,' Max ',90,' Value ',vw(1),...
' Tag ',' Azslider ',...
' Callback ',' svcback ' );
Hc_min=uicontrol(gcf,' style ',' text,...
' Position ',[10 25 40 20],...
' String ',num2str(get(Hc_az,' Min ' )));
Hc_max=uicontrol(gcf,' Style ',' text ',...
' Position ',[110 25 40 20],...
' String ',num2str(get(Hc_az,' Max ' )));
Hc_cur=uicontrol(gcf,' Style ',' text ',...
' Position ',[60 25 40 20],...
' Tag ',' Azcur ',...
' String ',num2str(get(Hc_az,' Value ' )));
回调函数本身如下:
function svcback( )
vw = get(gca,' View ' );
Hc_az = findobj(gcf,' Tag ',' AZslider ' );
Hc_cur = findobj(gcf,' Tag ',' AZcur ' );
str = num2str(get(Hc_az,' Value ' ));
newview =[get(Hc_az,' Value ' ) vw(2)];
set(Hc_cur,' String ',str)
set(gca,' View ',newview)
上面的例子并不节省很多代码,但却得到了用函数而不用脚本文件的优点:回调函数可以利用临时变量,而不使命令窗口工作空间拥挤;不需要eval所需的引号和字符串;在回调函数中命令的句法变得十分简单。使用独立回调函数技术,越复杂的回调(函数)越简单。
独立回调函数的缺点是:需要很大数目的M文件以实现一个含有若干控制框和菜单项的GUI函数,所有这些M文件必须在MATLAB路径中可得,且每一个文件又必须要有一个不同的文件名。在对文件名大小有限制且对大小写不敏感的平台上,如MS-windows,文件冲突的机会就增加了。而且回调函数只能被GUI函数调用而不能被用户调用。
递归函数调用
利用单独的M文件并递归地调用该文件,既可以避免多个M文件的复杂性,又可以利用函数的优点。使用开关 switches或if elseif语句,可将回调函数装入调用函数内。通常这样一种函数调用的结构为
function guifunc(switch)。
其中switch确定执行哪一个函数开关的参量,它可以是字符串 ' startup ',' close ',' sectolor ' 等等,也可以是代码或数字。如switch是字符串,则可如下面所示的M文件片段那样将开关编程。
if nargin < 1,switch = ' startup ' ; end;
if ~isstr(switch),error( ' Invalid argument ' ),end;
if strcmp(switch,' startup ' ),
<statement to create controls or menus>
<statements to implement the GUI function>
elseif strcmp(switch,' setcolor ' ),
<statements to perform the Callback associated with setcolor>
elseif strcmp(switch,' close ' ),
<statements to perform the Callback associated with close>
end
如果是代码或字符串,开关也可以相同方式编程。
if nargin < 1,switch = 0; end;
if isstr(switch),error( ' Invalid argument ' ),end;
if switch = = 0,
<statements to create controls or menus>
<statements to implement the GUI function>
elseif switch = =1,
<statements to perform the Callback associated with setcolor>
elseif switch ==2,
<statements to perform the Callback associated with close>
end
下面的例子说明了方位角滑标如何可作为单独的函数M文件来实现:
function setview(switch)
if nargin < 1,switch = ' startup ' ; end;
if ~isstr(switch),error( ' Invalid argument,' ); end;
vw = get(gca,' view ' ); % This information is needed in both sections
if strcmp(switch,' startup ' ) % Define the controls and tag them
Hc_az = uicontrol(gcf,' Style ',' slider ',...
' Position ',[10 5 140 20],...
' Min ',-90,' Max ',90,' Value ' vw(1),...
' Tag ',' AZslider ',...
' Callback ',' setview( ' set ' ) ' );
Hc_min=uicontrol(gcf,' Style ',' text ',...
' Position ',[10 25 40 20],...
' String ',num2str(get(Hc_az,' Min ' )));
Hc_max = uicontrol(gcf,' Style ',' text ',...
' Position ',[110 25 40 20],...
' String ',num2str(get(Hc_az,' Max ' )));
Hc_cur =uicontrol(gcf,' Style ',' text ',...
' Position ',[60 25 40 20],...
' Tag ',' AZcur ',...
' string ',num2str(get(Hc_az,' Value ' )));
elseif strcmp(switch,' set ' ) % Execute the Callback
Hc_az=findobj(gcf,' Tag ',' AZslider ' );
Hc_cur-findobj(gcf,' Tag ',' AZcur ' );
str = num2str(get(Hc_az,' Value ' ));
newview - [get(Hc_az,' Value ' ) vw(2)];
set(Hc_cur,' String ',str)
set(gca,' View ',newview)
end
上述的两个例子均设置了 ' tag ' 属性,利用该属性和函数findobj寻找回调函数所需对象的句柄。另外两种方法将在下章描述。
全局变量
全局变量可用在函数中,使某些变量对GUI函数的所有部分都可用,全局变量是在函数的公共区说明,因此整个函数以及所有对函数的递归调用都可以利用全局变量,下面的例子说明如何利用全局变量将方位角滑标编程。
function setview(switch)
global HC_AZ HC_CUR % Create global variables
if nargin < 1,switch = ' startup ' ; end;
if ~isstr(switch,error( ' Invalid argument,' ); end;
vw = get (gca,' View ' ); % This information is needed in both sections
if strcmp(switch,' startup ' ) % Define the controls
Hc_AZ=uicontrol(gcf,' style ',' slider ',...
' Position ',[10 5 140 20],...
' Min ',-90,' Max ',90,' Value ',vw(1),...
' Callback ',' setview( ' set ' ) ' );
Hc_min=uicontrol(gcf,' Style ',' text ',...
' Position ',[10 25 40 20],...
' String ',num2str(get(Hc-AZ,' Min ',)));
HcMax=uicontrol(gcf,' Style ',' text ',...
' Position ',[110 25 40 20],...
' String ',num2str(get(Hc_AZ,' Max ' )));
Hc_cur=uicontrol(gcf,' style ',' text ',...
' Position ',[60 25 40 20],...
' String ',num2str(get(HC_AZ,' Value ' )));
elseif strcmp(switch,' set ' ) % Execute the Callback
str=num2str(get(HC_AZ,' Value ' ));
newview= [get(HC_AZ,' Value ' ) vw(2)];
set(HC_CUR,' String ',str)
set(gca,' View ',newview)
end
全局变量遵循MATLAB的规定,变量名要大写。不需要 ' tag ' 属性,且不使用它,另外因为对象句柄存在的,不需要用函数findobj去获取,故回调代码比较简单,全局变量通常使一个函数更有效。
不过有一点要注意,尽管一个变量在函数内说明为全局的,变量并不能自动地在命令窗口工作空间中利用,也不能在回调字符串内使用。但是,如果用户发命令:>> clear global,则所有全局变量则都被破坏,包括在函数内定义的那些变量。
当单独的一个图形或有限个变量要被所有的回调(函数)利用时,全局变量使用和递归性函数调用都是有效的技术。对于包含多个图形的更复杂的函数,或用独立对象回调函数实现的情况,' UserData ' 属性更合适。另外,只要可获得对象句柄,对象 ' UserData ' 的属性值在命令窗口工作空间中是存在的。
用户数据属性
同属性 ' Tag ' 一样,' UserData ' 属性可在函数之间或递归函数的不同部分之间传递信息。如果需要多个变量,这些变量可以在一个容易辨识的对象的属性 ' UsetData ' 中传递。如前面所述,对与句柄图形对象在一起的单个数据矩阵' UserData ' 提供了存储。下面的程序利用了当前图形的 ' UserData ' 属性来实现方位角滑标。
function setview(switch)
if nargin < 1,switch = ' startup ' ; end;
vw = get(gca,' View ' ); % This information is needed in both sections
if strcmp(switch,' startup ' ) % Define the controls
Hc_az = uicontrol(gcf,' Style ',' slider ',...
' Position ',[10 5 140 20],...
' Min ',-90,' Max ',90,' Value ',vw(1),...
' Callback ',' setview("set") ' );
Hc_min = uicontrol(gcf,' Style ',' text ',...
' Position ',[10 25 40 20],...
' String ',num2str(get(Hc_az,' Min ' )));
Hc_max = uicontrol(gcf,' Style ',' text ',...
' Position ',[110 25 40 20],...
' String ',num2str(get(Hc_az,' Max ' )));
Hc_cur = uicontrol(gcf,' Style ',' text ',...
' Position ',[60 25 40 20],...
' String ',num2str(get(Hc_az,' Value ' )));
set(gcf,' UserData ',[Hc_az Hc_cur]); % Store the object handles
elseif strcmp(switch,' set ' ) % Execute the Callback
Hc_all = get(gcf,' UserData ' ); % retrieve the object handles
Hc_az = Hc_all(1);
Hc_cur = Hc_all(2);
str = num2str(get(Hc_az,' Value ' ));
newview = [get(Hc_az,' Value ' ) vw(2)];
set(Hc_cur,' String ',str)
set(gca,' View ',newview)
end
句柄存储于 ' startup ' 末端,图形 属性' UserData ' 中,在回调被执行前对此进行检索。如果有许多回调,如下面的程序片断所示,' UserData ' 只需检索一次。
if strcmp(switch,' startup ' ) % Define the controls and tag them
% <The ' startup ' code is here>
set(gcf,' UserData ',[Hc_az Hc_cur]); % Store the object handles
else % This must be a Callback
Hc_all=get(gcf,' UserData ' ); % Retrieve the object handles
Hc_az=Hc_all(1);
Hc_cur=Hc_all(2);
if strcmp(switch,' set ' )
% <The ' set ' Callback code is here>
elseif strcmp (switch,' close ' )
%<The ' close ' Callback code is here>
% <Any other Callback code uses additional elseif clauses>
end
end
调试GUI M文件
回调字符串在命令窗口工作空间中计算并执行的,这个情况对编写和调试GUI函数和脚本文件有某种隐含意义。回调字符串可以很复杂,尤其是在脚本文件中,这为句法错提供了许多机会,记录单引号、逗号、括号是令人头痛的事。如果出现了句法错误,MATLAB给出提示;只要对象的 ' Callback ' 属性值是一个真正的文本串,MATLAB就认可了。只有当对象被激活并将回调字符串传给eval时,才检查回调字符串内部的句法错误。
这样让用户定义回调字符串,它涉及还未曾定义过的对象句柄和变量,这使编写相互参照的程序变得更容易,但是每个回调函数必须分别测试,保证回调字符串是合法的MATLAB命令,并且回调字符中涉及的所有变量可在命令窗口工作空间中是可利用的。
将回调象函数M文件一样编程或象GUI函数本身内的开关一样编程,就可以不运行整个GUI函数而对各个回调进行改变或测试。
因为回调字符串是在命令窗工作空间中而不在函数本身内计算,在函数与各回调之间传递数据就变得十分复杂。例如,函数test包含如下程序:
function test()
tpos1=[20 20 50 20];
tpos2=[20 80 50 20];
Hc_text=uicontrol( ' Style ',' text ',' String ',' Hello ',' Position ',tposl);
Hc-push=uicontrol( ' Style ',' push ',' String ',' Move Text ',...
' Position ',[15 50 100 25],...
' Callback ',' set(Hc-text,"Position",tpos2) ' );
所有语句都是有效的MATLAB命令,且回调字符串也对有效的MATLAB语句估值。文本对象和按钮出现在图形上,但当激活按钮键时,MATLAB就出示错误。
>> test
>>
Undefined functiion or variable Hc_text.
Error while evaluating Callback string.
如果test是个脚本文件,就不会出现这样问题,因为所有变量可在命令窗口工作空间中使用,因为test是个函数,Hc_text和tpos2在命令窗口工作空间中均未定义,回调字符串执行失败。
一种解决方法是使用各个字符串元素来建立回调,该字符串元素由数值而非变量建立,例如,改变回调字符串如下:
' Callback ',[ ' set9 ',...
sprintf( ' &.15g ',Hc_text),...
',' ' Position ' ',',...
sprintf( ' [%.15g %.15g %.15g %.15g] ',tpos2),...
' ) ' ]);
建立了包括Hc_text对象句柄值的一个字符串,该值变换成具有15位精度的字符串,而tpos2变量转换成矩阵表示的字符串。在函数内计算sprinf语句,然后将所得的字符串用在回调中。在命令窗工作空间执行的实际命令如下所示
eval( ' set(87.000244140625,' ' Position ' ',[20 80 50 100]) ' )
将一个对象句柄转换为字符串,必须保持全精度。上例中的变换,使用了小数点后15位的数字的精度。在MATLAB中句柄对象转换应使用这样的精度。
要记住,变量随后的变换不会改变回调字符串。在前面的例子中,在控制框定义之后改变tpos2的值,就无效果。例如,在函数结尾处加命令
tpos2=[20 200 50 20 ]
就无效果,因为在tpos2重新定义之前,通过计算tpos2,回调字符串已经建立。
21.6 指针和鼠标按钮事件
GUI函数利用鼠标箭头的位置和鼠标按钮的状态来控制MATLAB行动。本节讨论指针、对象位置和鼠标按钮动作之间的交互,以及MATLAB如何响应变化或事件,诸如:揿下按钮、松开按钮或箭头移动等。
回调属性,选择区域和堆积顺序
所有句柄图形对象具有一个至今还未阐述过的 ' ButtonDownFcn ' 属性,本节就进行讨论。uimenu和uicontrol均有 ' CallBack ' 属性,这个属性是菜单和控制框的应用核心。另外,图形有 ' KeyPressFcn ' 和 ' ResizeFcn ' 属性以及 ' WindowButtonUpFcn ' 和 ' WindowButtonMotionFcn ' 属性。与这些属性相关的值是回调字符串,即MATLAB字符串,当属性激活时,它传给eval。指针的位置确定事件涉及到哪些回调以及当事件发生时它们被激励的顺序。
前一章讨论过堆积顺序和与讨论相关的对象选择区域。根据图形中3个区域,MATLAB决定哪一个回调将被激励。如果指针是在句柄图形对象内,如同对象的 ' Position ' 属性所确定的那样,则指针就是在对象上。如果指针不在一个对象上,而在对象的选择区域内,则指针靠近对象。如果指针在图形内但既不靠近也不在另一对象之上,则可以认为指针关掉其它对象。如果若干对象及其选择区域相重叠,重叠顺序就决定了选择顺序。
句柄图形的线、曲面、补片、文本和坐标轴对象的选择区域已在上一章讨论过了。uimenu对象没有外部的选择区域,指针要么在uimenu上,要么不在其上。uicontrol对象越过图基位置,在各方向延伸大约5个象素有一个选择区域,指针可以靠近或在控制框上。记住这里讨论的堆积顺序和选择区域是针对4.2c版本的,在以后的MATLAB有某种程度的变化。
按钮点击
按钮点击可以定义为当鼠标指针在同一对象上时,揿下并随后松开鼠标按钮。如果鼠标指针在uicontrol或uimenu项上,按钮点击触发对象 ' Callback ' 属性字符串的执行,按钮揿下使控制框作好准备,并常常在视觉上改变uicontrol或uimenu,松开按钮触发回调。当鼠标指针不在uicontrol或uimeun上,揿下按钮和松开按钮事件,将在后面讨论。
揿下按钮
当鼠标指针位于一个图形窗口内,揿下鼠标按钮,根据指针位置和对句柄图形对象靠近程度将会发生不同的动作。如果一个对象被选择,它就变成了当前的对象,并上升到堆积顺序的最高端。如果没有对象被选择,图形本身就是当前对象,图形的 ' CurrentPoint ' 和 ' SelectionType ' 属性都被更新,然后激发适当的回调。表格21.3列出了指针位置与按钮揿下所激发的回调:
表21.3
指针位置
所激发的属性
在uimenu 或uicontro项上面
准备释放事件
在句柄图形上,或接近控制框
图形的WindowButtonDownFcn属性,然后是对象的ButtonDownFcn属性
在图形内,但不在或接近任何对象
图形的WindowButtonDownFcn属性,然后是图形的ButtonDownFcn属性
注意:按钮揿下事件总是在所选对象的 ' ButtonDownFcn ' 回调之前,引起图形的 ' WindowButtonDownFcn ' 回调,除非指针是在uicontrol或uimenu对象上。如果指针靠近控制框,则在图形的 ' WindouButtonDownFcn ' 回调完毕后,引起控制框的 ' ButtonDownFcn ' 回调而不是 ' Callback ' 属性回调。
按钮松开
当松开鼠标按钮时,图形的 ' CurrentPoint ' 属性就更新。如果没有定义 ' WindowButtonUpFcn ' 回调,则鼠标按钮松开时,属性 ' CurrentPoint ' 不更新。
指针的移动
当指针在图形内移动时,图形的 ' CurrentPoint ' 属性被更新,引起图形的 ' WindowButtonMotionFcn ' 回调;如果没有定义图形的 ' WindowButtonMotionFcn ' 回调,则指针移动时,属性 ' CurrentPoint ' 不更新。
回调的复合能产生许多有趣的效应。试一下包含在MATLAB于demo目录中的函数sigdemo1,sigdemo2,就可解其中的一些效果。另外将要讨论的一个例子是 精通MATLAB工具箱的函数mmdraw。
21.7 中断回调的规则
一旦回调开始执行,通常都在下一个回调事件处理之前运行完毕。将 ' Interruptible ' 属性设置为 ' yes ' 可改变这种缺省行为,从而当正在执行的回调遇到drawnow,figure,getframe或pause命令时,允许处理的回调事件悬挂起来。事件队列执行计算操作或设置对象属性的命令一经发出,MATLAB便进行处理;而涉及图形窗口输入或输出的命令则生存事件。事件包括产生回调的指针移动和鼠标按钮动作,以及重新绘制图形的命令。
回调处理
回调在达到drawnow,getframe,pause或figure命令之前一直执行。注意gcf和gca引起figure命令,而精通MATLAB工具箱中的函数mmgcf和mmgca不引发figure命令。不含有这些特殊命令的回调不会被中断,一旦达到这些特殊命令之一,停止执行回调,将其悬挂起来;并检查事件队列中每一个悬挂的事件。如果产生悬挂回调的对象的Interruptible属性设为 ' yes ',则在被悬挂的回调恢复之前按序处理所有悬挂。如果Interruptible属性设为 ' no ',即缺省值,则只处理悬挂的重画事件,放弃回调事件。
防止中断
即使正在执行回调是不能被中断的,当回调达到drawnow,figure,gefframe或pause命令时,仍然处理悬挂的重画事件。通过避免在回调中使用所有这些特殊命令,消除此类事情。如果回调中需要这些特殊命令,但又不要任何悬挂事件,甚至是刷新事件,来中断回调,则可以使用如下所讨论的特殊形式的drawnow。
Drawnow
Drawnow命令迫使MATLAB更新屏幕,只要MATLAB回到命令提示,或执行drawnow,figure,getframe或pause命令,屏幕就更新。drawnow的特殊形式draunow( ' discard ' )使事件队列中所有事件的放弃。在回调中将drawnow( ' discard ' )包含在一个特殊命令之前,就含有清除事件队列的效果,防止刷新事件,以及回调事件中断回调。
21.8 M文件举例
精通MATLAB工具箱中一些函数阐明了上面所讨论的若干技术。第一个例子mmview3d应用全局变量和递归函数调用,把方位角和仰角滑标加到图形中。函数中有大量的对象,但函数很直观。因为mmview3d文件相当得长,故分段表示。第一段定义了函数标号,帮助文本和全局变量。
function mmview3d(cmd)
% MMVIE3D GUI controllled Azimuth and Elevation adustment.
% for adjusting azimuth and elevation using the mouse.
%
% The ' Revert ' pushbutton reverts to the original view.
% The ' cmd ' argument executes the callbacks.
% Copyright (c) 1996 by Prentice-Hall,Inc.
global Hc_acur Hc_esli Hc_ecur CVIEW
第二段处理初始用户的调用,建立必要的uicontrol对象并把回调定义为递归函数调用。
if nargin==0
%------------------------------------------------------------------
% Assign a handle to the current figure window.
% Get the current view for slider initial values.
% Use normalized uicontrol units rather than the default ' pixels ',
%------------------------------------------------------------------
Hf_fig=gcf;
CVIEW=get(gca,' View ' );
if abs(CVIEW(1))>180,CVIEW(1)=CVIEW(1)-(360*sign(CVIEW(1)));end
set(Hf_fig,' DefaultUicontrolUnits ',' normalized ' );
%-------------------------------------------------------------------
% Define azimuth and elevation sliders.
% The position is in normalized units (0-1).
% Maximum,minimum,and initial values are set.
%--------------------------------------------------------------------
Hc_asli=uicontrol(Hf_fig,' style ',' slider ',...
' position ',[.09,02,3,05],...
' min ',-180,' max ',180,' value ',CVIEW(1),...
' callback ',' mmview3d(991) ' );
Hc_esli=uicontrol(Hf_fig,' style ',' slider ',...
' position;,[.92,5,04,42],...
' min ',-90,' max ',90,' val ',CVIEW(2),...
' callback ',' mmview3d(992) ' );
%--------------------------------------------------------------------
% Place the text boxes showing the minimum and maximum values at the
% ends of each slider,These are text displays,and cannot be edited.
%---------------------------------------------------------------------
uicontrol(Hf_fig,' style ',' text ',...
' pos ',[.02,02,07,05],...
' string ',num2str(get(Hc_asli,' min ' )));
uicontrol(Hf_fig,' style ',' text ',...
' pos ',[.39,02,07,05],...
' string ',num2str(get(Hc_esli,' min ' )));
uicontrol(Hf_fig,' style ',' text ',...
' pos ',[.915,92,05,05],...
' string ',num2str(get(Hc_esli,' max ' )));
%--------------------------------------------------------------------
% Place labels for each slider
%--------------------------------------------------------------------
uicontrol(Hf_fig,' style ',' text ',...
' pos ',[9.095,08,15,05],...
' string ',' Azimuth ' );
uicontrol(Hf_fig,' style ',' text ',...
' pos ',[.885,39,11,05],...
' string ',' Elevation ' );
%--------------------------------------------------------------------
% Define the current value text displays for each slider,
%--------------------------------------------------------------------
% These are editable text display the current value
% of the slider and at the same time allow the user to enter
% a value using the keyboard.
%
% Note that the text is centered on XWindow Systems,but is
% left-justified on MS-Windows and Macintosh machines.
%
% The initial value is found from the value of the sliders.
% When text is entered into the text area and the return key is
% pressed,the callback string is evalueated.
%--------------------------------------------------------------------
Hc_acur=uicontrol(Hf_fig,' style ',' edit ',...
' pos ',[.25,08,13,053],...
' string ',num2str(get(Hc_asli,' val ' )),...
' callback ',' mmview3d(993) ' );
Hc_ecur=uicontrol(Hf_fig,' style ',' edit ',...
' pos ',[.885,333,11,053],...
' string ',num2str(get(Hc_esli,' val ' )),...
' callback ',' mmview3d(994) ' );
%--------------------------------------------------------------------
% Place a ' Done ' button is the lower right corner.
% When clicked,all of the uicontrols eill be erased.
%--------------------------------------------------------------------
uicontrol(Hf_fig,' style ',' push ',...
' pos ',[.88,02,10,08],...
' backgrouncolor ',[.7,7,8],...
' string ',' Done ',...
' callback ',' delete(finobj(gcf,' ' Type ' ',' ' uicontrol ' ' )) ' );
%--------------------------------------------------------------------
% Place a ' Revert ' button next to the ' Done ' button.
% When clicked,the view reverts to the original view.
%--------------------------------------------------------------------
Hc_ecur=uicontrol(Hf_fig,' style ',' edit ',...
' pos ',[.77,02,10,08],...
' backgroundcolor ',[.7,7,8],...
' string ',' Revert ',...
' callback ',' mmview3d(995) ' );
现在已建立了控制框并定义了回调。
else
%--------------------------------------------------------------------
% The callbacks for the azimuth and elevation sliders;
%--------------------------------------------------------------------
% 1) get the value of the slider and display it in the text windows
% 2) set the ' View ' property to the current values of the azimuth
% and elevation sliders.
%--------------------------------------------------------------------
if cmd==991
set(Hc_acur,' string ',num2str(get(Hc_asli,' val ' )));
set(gca,' View ',[get(Hc_asli,' val ' ),get(Hc_esli,' val ' )]);
elseif cmd==992
set(Hc_ecur,' string ',num2str(get(Hc_esli,' val ' )));
set(gca,' View ',[get(Hc_asli,' val ' ),get(Hc_esli,' val ' )]);
%--------------------------------------------------------------------
% The ' slider current value ' text display callbacks;
%--------------------------------------------------------------------
% When text is entered into the text area and the return key is
% pressed,the entered value is compared to the limits.
%
% If the limits have been exceeded,the display is reset to the
% value of the slider and an error message is displayed.
%
% If the value is within the limits,the slider is set to the
% new value,and the view is updated.
%--------------------------------------------------------------------
elseif cmd==993
if str2num(get(Hc_acur,' string ' )) < get(Hc_asli,' min)...
| str2num(get(Hc_acur,' string ' )) >get(Hc_asli,' max ' )
set(Hc_acur,' string ',num2str(get(Hc_asli,' val ' )));
disp( ' ERROR - Value out of range ' );
else
set(Hc_asli,' val ',str2num(get(Hc_acur,' string ' )));
set(gca,' View ',[get(Hc_asli,' val ' ),get(Hc_esli,' val ' )]);
end
elseif cmd==994
if str2num(get(Hc_ecur,' string ' )) < get(Hc_esli,' min)...
| str2num(get(Hc_ecur,' string ' )) >get(Hc_esli,' max ' )
set(Hc_ecur,' string ',num2str(get(Hc_esli,' val ' )));
disp( ' ERROR - Value out of range ' );
else
set(Hc_esli,' val ',str2num(get(Hc_ecur,' string ' )));
set(gca,' View ',[get(Hc_asli,' val ' ),get(Hc_esli,' val ' )]);
end
%--------------------------------------------------------------------
% Revert to the original view.
%--------------------------------------------------------------------
elseif cmd==995
set(Hc_asli,' val ',CVIEW(1));
set(Hc_esli,' val ',CVIEW(2));
set(Hc_acur,' sting ',num2str(get(Hc_asli,' val ' )));
set(Hc_ecur,' sting ',num2str(get(Hc_esli,' val ' )));
set(Hc_acur,' View ',[get(Hc_asli,' val ' ),get(Hc_esli,' val ' )]);
%--------------------------------------------------------------------
% Must be bad arguments.
%--------------------------------------------------------------------
else
disp( ' mmview3d,Illegal argument,' )
end
end
第二个例子mmcxy,在图形左下角建立一个小文本框,当指针处在图形中时,显示图形内鼠标指针的坐标[x,y]。点击鼠标清除坐标的显示。
虽然mmcxy是一个短小的函数,它仍然利用许多有效的GUI函数的元素,包括递归函数调用、全局变量和 ' UserData ' 属性。此例还说明图形的 ' WindowButtonDownFcn ' 和 ' WindowButtonMotionFcn ' 属性起动回调的用法。
function out=mmcxy(arg)
% MMCXY Show x-y Coordinates of the mouse in the
% lower left hand corner of the current 2-D figure window.
% When the mouse is clicked,the coordinates are erased.
% XY=MMCXY returns XY=[x,y] coordinates where mouse was clicked.
% XY=MMCXY returns XY=[] if a key press was used.
% Copyright (c) by Prentice-Hall,Inc.
global MMCXY_OUT
if -nargin
Hf=mmgcf;
if isempty(Hf),error( ' No Figure Available,' ),end
Ha=findobj(Hf,' Type ',' axes ' );
if isempty(Ha),error( ' No Axes in Current Figure,' ),end.
Hu=uicontrol(Hf,' Style ',' text ',...
' units ',' pixels ',...
' Position ',[1 1 140 15],...
' HorizontalAlignment ',' left ' );
set(Hf,' Pointer ',' crossh ',...
' WindowButtonMotionFcn ',' mmcxy( ' ' move ' ' ),...
' WindowButtonDownFc ',' mmcxy( ' ' end ' ' ) ',...
' Userdata ',Hu)
figure(Hf) %bring figure forward
if nargout %must return x-y data
key=waitforbuttonpress; % pause until mouse is pressed
if key
out=[]; %return empty if aborted
mmcxy( ' end ' ) %clean thins up
else
out=MMCXY_OUT; %now that move is complete return point
end
return
end
elseif strcmp(arg,' move ' ) % mouse is moving in figure window
cp=get(gca,' CurrentPoint ' ); % get current mouse position
MMCXY_OUT=cp(1,1:2);
xystr=sprintf( ' [%.3g] ',MMCXY_OUT);
Hu=get(gcf,' Userdata ' );
set(Hu,' String ' xystr) % put x-y coordinaates in text box
elseif strcmp(arg,' end ' ) % mouse click occurred,clean things up
Hu=get(gcf,' Userdata ' );
set(Hu,' visible ',' off ' ) % make sure text box disappears
delete(Hu)
set(gcf,' Pointer ',' arrow ',...
' WindowButtonMotionFcn ',' ',...
' WindowButtonDownFcn ',' ',...
' Userdata ',[])
end
第一次被调用时,mmcxy建立文本uicontrol,改变指针形状,设定 ' WindowButtonDownFcn ' 和 ' WindowButtonMotionFcn ' 的回调,然后等待按键或先揿按钮。若有键揿下,就调用清除(cleanup)程序,清除文本框,恢复鼠标指针,清除图形回调及 ' UserData ' 属性。若点击鼠标按钮,' WindowButtonDownFcn ' 回调就处理清除任务。在等待时,图形中鼠标指针的移动会触发 ' WindowButtonMotionFcn ' 回调,更新uicontrol中文本串。
精通MATLAB工具箱中的另一个函数是mmtext,它利用 ' WindowButtonDownFcn ',' WindowButtonUpFcn ' 和 ' WindowButtonMotionFcn ' 回调的另一个短小函数以安置和拖曳文本。
function mmtext(arg)
% MMTEXT place and drag text with mouse
% MMTEXT waits for a mouse click on a text object
% in the current figure then allows it to be dragged
% while the mouse button remains down.
% MMTEXT( ' whatever ' ) places the string ' whatever ' on
% the current axes and allows it to be dragged.
%
% MMTEXT becomes inactive after the move is complete or
% no text object is selected.
% Copyright (c) 1996 by Prentice-Hall,Inc.
if -nargin,arg=0;end
if isstr(arg) % user entered text to be placed
Ht=text( ' units ',' normalized ',...
' Position ',[.05,05],...
' String ',arg,...
' HorizontalAlignment ',' left ',...
' VerticalAlignment ',' baseline ' );
mmtext(0) % call mmtext again to drag it
elseif arg==0 % initial call,select text for dragging
Hf=mmgcf;
if isempty(Hf),error( ' No Figure Available,' ),end
set(Hf,' BackingStore ',' off ',...
' WindowButtonDownFcn ',' mmtext(1) ' )
figure(Hf) % bring figure forward
elseif arg==1 & strcmp(get(gco,' Type ' ),' text ' ) % text object selected