[转载] 关于matlab GUI的一点心得

                                                              转载自 落落轻尘

[Fig文件方式,即使用菜单File->New->GUI来设计界面]


首先值得注意的是,在低版本matlab上制作的含GUI的m文件一般不能在高版本的matlab上面运行,但是从7.0版开始改进了一点,我试过 6.5版的含GUI的m文件可以在 7.0版上运行,但如果在7.0版上面修改过.fig文件,那么就不能回到6.5版上运行了(显示一堆错误,我没看懂,不知道能否通过修改使之重新可在 6.5版上运行,呵呵),而在7.0版上制作的含GUI的m文件就完全不能放到6.5版上运行了(也是同样的错误)。这可能是因为matlab各版本在 GUI上改动较大,所以都不支持向下兼容。也许兼容性差是使用GUI编写程序的最大麻烦。

其次,说说使用GUI的好处。不知道是否承袭了VC or BCB的习惯,我比较喜欢编写完程序后按快捷键运行(此时会自动保存m文件),但是,对于程序有输入参数的时候,如果不采用GUI方式,这显然是行不通的――当然了,可以在command window按“向上”键然后重新改写你想要的参数。对于一些较大型的程序,正因为我比较偏向于按F5运行程序,所以我会选择使用GUI方式。不过GUI 方式的好处就远不止于此了,它可以把几个功能相关的模块集中起来,使得不需要重新关闭、运行就能得到不同的结果,便于比较;可以减少figure满天飞的 现象,这在matlab中尤为明显;便于交互式地观看不同的数据,例如在我处理的fMRI问题中,希望观看每个三维点对应的时间序列,这时候需要提供交互 式的选择……不过,尽管GUI有很多好处,但是其缺点也是有的,除了上面提到的兼容性(移植性)以外,要管理、维护一个GUI界面也是比较麻烦的,相信有 其他语言的界面设计经验的朋友也知道这点。

再者,对GUI问题,说说我的一点经验吧。其实,matlab已经在这方面改进了不少,从控件的增加和教程的学习都可以看出来。例如6.5版本是没有Panel和Button Group这两个控件的,在2006a版本中就新增了这两个控件(这个好像7.0版就有了,不确定);教程方面,也使用了最为直观的视频教学方式,建议新手看看Creating a GUI with GUIDE 和Creating Graphical User Interfaces两节。所以,操作上的细节我就不多讲了,自己摸索一下就ok了。
至于编程时最为关键的参数传递问题,可以参考本论坛或者仿真论坛上面的“函数结构与参数传递”一文,作者整理得较完整,不过有点难懂。
这里我简单说一下吧:
[转cwit]关于参数传递的方法:
1. 在内存中提取:
① 采用function的varargin和varargout传送,
② 存在handle的ApplicationData中,
③保存在handles里面。当然还有其他方法;

2. 存在硬盘上,采用save和load;

3. 用全局变量,最次的办法,有时却是最好的办法;

我想,对于这里处理的GUI问题,一般不会用到save和load命令(要从mat文件读取数据初始化或者把数据存储到mat文件中例 外),“function的varargin和varargout传送”的方法也不较少使用(因为对于控件的回调函数,matlab规定了输入参数为 “hObject, eventdata, handles, varargin”,且一般没有返回值,所以不同于自定义的函数),所以剩下的方法就只有三种了:

1. 存在handle的ApplicationData中――利用setappdata函数将数据保存在对象的“ApplicationData”属性中,利 用getappdata函数从对象的“ApplicationData”属性中提取数据,如果没有定义,就为空。例如,假设figure的tag属性是 figure1,则setappdata(handles.figure1,’A’,A)就可以把变量A存放到figure1的 “ApplicationData”属性中,名字为A,而B = getappdata(handles.figure1,’A’)就可以把figure1的“ApplicationData”属性中名字为A的变量赋给 B。setappdata和getappdata操作的对象,最好为figure_handle,便于管理和扩展。Matlab的高级编程和对象控制,将 大量使用这两个函数[转自cwit];


2. 保存在handles里面――首先,这是访问控件的常用方法,注意到Tag是唯一标识控件的属性,也就说,如果一个控件的Tag属性是a,那么,在fig 对应的m文件中,只要含有“handles”作为其输入参数的函数中,就可以使用handles.a对该控件进行访问。既然句柄(相当于其他语言中的指 针)已经拿到手了,就任由“宰割”了(例如设置属性、存取数据等就比较容易了);其次,可以利用这种方法把自定义的变量保存在handles里面进行存取,这在matlab帮助的Creating a GUI with GUIDE一节中有具体介绍,不多说;

3. 用全局变量的方法――这是我进行参数传递的常用方法,使用方法是:在两个函数体(当然可以是多个了,这里是主函数和子函数的意思)中使用前加入语句 “global A”,其中A是多个函数需要传递的变量名,多个变量用空格格开,句末不需要加分号。这种方法由于存放在硬盘上,存取速度会减慢,但是如果变量个数不多、或 者对该变量引用的函数不多,可以考虑用此办法(我尚未能领会cwit兄所说“最次的办法,有时却是最好的办法”之含义)。
至于其他有关GUI命令、控件属性和图形属性可以参考matlab的帮 助,或者参考“GUI命令大全”、《精通GUI图形界面编程》(这两个本论坛有)、“Matlab图形图像属性”、“GUI.rtf”(这两个参见附件, 后者下载后手动修改一下后缀名)。实际上,用得比较多的控件属性不外乎以下几种(高手例外):
Visible属性,例如axes、edit、button等,格式:set(handles.***, ‘Visible’, ‘on’); 或者set(handles.***, ‘Visible’, ‘off’);
String属性,例如edit、text等,格式:str=get(handles.***, ‘String’)或set(handles.***,’String’,str);
Enable属性,例如edit、button、text等,格式:set(handles.***, ‘Enable’, ‘on’);或者set(handles.***, ‘Enable’, ‘off’);
Value属性,例如radio button、check box等,格式:a=get(handles.***, ‘Value’)或set(handles.***,’Value’,1); 或set(handles.***,’Value’,0);
留意一下响应函数的注释部分,有时候会得到提示的。例如,我的 matlab 2007a 中,在设计界面上增加了 listbox 控件后,m 文件的对应回调函数处显示如下:
% --- Executes on selection change in listbox1.
function listbox1_Callback(hObject, eventdata, handles)
% hObject handle to listbox1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: contents = get(hObject,'String') returns listbox1 contents as cell array —— 这是该控件基本用法的一点提示
% contents{get(hObject,'Value')} returns selected item from listbox1 —— 这是该控件基本用法的一点提示

最后,介绍一下我在使用的过程中发现的一些问题或者技巧(真的只有一点点而已,希望以后可以不断补充拉,这些技巧对m文件方式的GUI也适用):

1. 当控件位置有重叠时,直接放置方式与程序描绘方式不兼容。直接放置方式是指在GUI设计窗口中放置axes控件,程序描绘方式是指在程序中利用plot、 subplot函数作图。经本人测试,在同一个fig文件中,先使用程序描绘方式把图形plot出来(此时直接放置的axes控件为不可见状态),后欲再 把直接放置的axes控件重新设置为可见状态时,如果通过这两种方式控制的axes位置有重叠,那么放置的axes控件句柄会莫名其妙消失,变成无效句 柄,即出现Invalid handle的错误(尽管其handle是有值的)。所以,在交替采用这两种方式的时候,其位置不能发生重叠。――其根本原因应该是第5点所说的


2. 有多个axes控件时,可以用axes(handles.haxis)来选择切换,这个方法比plot(x,y,’parent’,haxis)更加灵 活,应该不算原创(因为在精通GUI一书上有介绍),但是在我读此书之前就一直使用到现在。具体来说,在fig文件方式的GUI中,假设你的axes控件 的tag属性值为hA,则在画图前使用axes(handles.hA);就可以指定在这个axes上面作图;在m文件方式的GUI中,假设你是通过 hA=axes(...);创建这个控件的,那么使用plot(x,y,'parent',hA);语句就可以指定在这个axes上面作图。其他控件也类 似,以 Edit 控件为例,首先给 edit1 增加一个Tag属性,即:H_edit1=……'Tag','edit1'……; ,然后使用 set(findobj('Tag','edit1'),'string','1234'); 便可。

3. 设置对象透明属性的alpha(.5)语句会影响其他对象的可见性设置,也就是说,当fig窗口中某个对象(我的例子是axes对象)使用了alpha语 句设置其透明度之后,就不能对fig窗口中其他对象设置Visible属性为off了,此时off值是失效的,解决办法是对其他对象设置Enable属性 来近似达到需要的效果。


【以下转自newseee】

4. 设计GUI,mailab版本越高越好,bug少,功能全面。比如 uigeifile(还是uigetdir?记不清了。)在R12中就不支持。初始化时,加上版本判断。
5. axes物件不能放到其它物件之上!
6. axes没有Callback,但是可以在上面放一张图(image),可以有Callback。 
7. windows和linux下 GUI会看起来不一样,为了兼容,字体改成default,Unit 的单位要用characters。 
8. 动态图形是循环画出来的。打开figure的DoubleBuffer到on,可以避免图形的闪烁。记得要用axes(h)。因为每plot一次,坐标系就刷新了。人不能踩进同一条河里。 
9. matlab r13 有bug,linux下submenu不能动态更新,能删除,不能添加。我的解决办法是在callback中把GUI都关闭,然后更新submenu (在主函数中 function varargout = untitled_OutputFcn(hObject, eventdata, handles) 这一行下面添加,此时窗口还没有创建。),然后重新打开gui。


[m文件方式设计界面]

这种方式的入门最好根据自己的需要,下载一些具体例子作为参考(例如振动 或仿真等论坛上一些牛人发的帖子),结合手头上的任务(或者兴趣)来学。与上面的fig文件方式相比,这种方式可以夸不同的matlab平台使用,不必考 虑兼容性问题(当然了,要满足一个前提:要该平台的语法或命令支持才行,例如&&在6.5版本无效,也就是说,如果你的程序是用7.0以 上版本的matlab编写,但是希望程序可以在6.5版本中运行,那么在编写条件判断语句时只能使用&),但是换来的代价是所有控件的创建和布 局、事件的响应函数入口等等都需要自己用代码来实现,因此比fig文件方式要困难一点。当然,你可以下载别人写好的界面来做修改。一般来说,如果是用来内 部调试、测试的话,用fig方式比较好,但是如果用来向外发布的话,还是m文件方式比较通用一点(虽然可以打包来换取多一点的通用性,但你总不能带着 100多m的MCRInstaller文件到处走吧?2006b是100多m,2006a好像是60多m,记不清了)。
1. 建立控件时,直接采用“'属性名','属性值'”的方式(如'CallBack','delete(gcbf);clear all;abc()')将导致程序不能调试到达其响应函数(貌似调用自己时不能,调用其他函数可以,没有完全测试,呵呵), 解决方法是使用消息传递,即'CallBack’, @Action,然后写:
Function Action(hObject,eventdata,handles)
delete(gcbf);
clear all;
abc();
2. 实现进度条有两种方式(可能不止):waitbar方式和uicontrol方式,后者可以使用两个edit控件,或者使用axes控件加上line函数 生成,但是无论哪种方式,如果每次循环都刷新一下,那么当循环总次数很大时(如几十万次),运行速度会减慢,建议每隔一段距离(step)刷新一次,这样 可以加快运行速度。例如:
if (i - 100*k/N) <= 1e-003
waitbar(k/N);
i = i + step;
end
其中i是控制进度条在何处刷新,k是程序的循环变量,N是程序循环的总次数。
使用 uicontrol 方式实现进度条的示例:
复制内容到剪贴板
代码:figure;
e1 = uicontrol(gcf,'style','edit','BackgroundColor','w',...
'unit','normalized','position',[0.35,0.6,0.02,0.25]);
e2 = uicontrol(gcf,'style','edit','BackgroundColor','r',...
'unit','normalized','position',[0.3525,0.6,0.015,0.01]);
drawnow;
n = 1000;
ii = 1;
for i = 1:0.01:n
if (ii - 100*i/n) <= 1e-003
set(e2,'position',[0.3525,0.6,0.015,0.01+0.24*i/n]);
drawnow;
ii = ii + 1;
end
end【附录】转自cwit兄:
m文件与fig文件创建figure的十大差异——用代码写figure,并不是麻烦。如果你习惯了,或者熟悉了,会发现用代码实现guide编程,比GUI方便很多,功能也很强大。 用m文件实现guide编程,相比GUI编程有几大差异: 
1. 代码可复用,节省成本; 
2. GUI不能灵活创建uimenu,并且不能编辑其所有属性; 
3. GUI还没有实现创建uitoolbox; 
4. GUI不能创建所有axes的子对象; 
5. 写界面也有一些算法,在GUI中无法实现。而用m文件就可以实现在不同窗口尺寸下给对象以合适的位置; 
6. m文件可以生成非常复杂的界面; 
7. 采用GUI编程,代码编译后依赖于*.fig文件; 
8. m文件可以实现组件; 
9. m文件创建的对象,可以方便的在handle中存取数据; 
10.m文件可以将创建对象代码与动作执行代码很好的结合起来。
【注】关于两种方式的运行:
对于fig文件方式:fig文件记录的是控件的布局和属性等资源信息,m 文件记录的是控件的响应消息(这是个人理解,更准确的介绍请参阅有关GUI的书籍,我懒得去查了,呵呵),两者缺一不可。所以,不能通过打开fig文件 (如菜单中的Open命令)来运行该GUI,这种方式的运行方法有两种:打开该GUI对应的m文件,然后像普通m文件那样运行;打开该GUI的设计界面 (菜单操作:FileàNewàGUIàOpen Existing GUI,打开 fig 文件

posted @ 2015-07-30 10:39  wenglabs  阅读(4059)  评论(0编辑  收藏  举报