采用guide创建gui界面
a.是否只允许单一gui(gui_state.gui_singleton)
b.检查gui的可见性(获取visible属性值)
c.创建或更新gui数据(handles结构体)
d.检查输入参数是否为【属性】、【属性值】成对出现,并逐对设置属性,直到遇到错误跳出
e.检查句柄可见性(handlevisibility属性)
f.执行opening函数(gui_state.gui_openingfcn)
g.根据visible值决定是否将窗口显示到屏幕
h.执行output函数(gui_state.gui_outputfcn)
i.设置句柄可见性(handlevisibility属性)
%%【创建handles结构体】在【执行opening函数】之前,所以在opening函数中,可使用handles结构体访问该gui的所有组件对象
%%当输入参数成对出现时,matlab会将输入参数逐对从左至右设置为对象的属性,一旦遇到未定义的属性或错误的属性设置,将不再设置后面的属性对,也不弹出错误提示,而是直接跳出属性设置的循环。
%%【执行opening函数】在【显示窗口到屏幕】之前,只有执行完了opening函数,gui窗口才会可见。例如,可用uiwait命令在窗口弹出之前,等待用户操作。
gui数据
Gui数据由handles结构体保存。当运行guide创建的gui时,M文件会自动生成一个叫做handles的结构体。Handles结构体可看作一个数据的容器,包含所有的gui对象数据。Handles与对应的gui窗口相关联,它作为第3个输入参数传递给每个回调函数,使得它们可随意访问gui数据。
Handles结构体主要有两个用途:访问gui数据,由于handles结构体作为第3个输入参数传递进了每个回调函数中,而handles结构体包含了gui对象的tag值和句柄的信息,所以,每个回调函数可通过handles获取任何gui对象的数据。例如:
Str=get(handles.a,‘string’)
另一个用途就是:在回调函数之间共享数据。在gui中,要使一个变量成为全局变量,一个有效的办法就是将其存在handles结构体中。例如将变量a存在handles中:
Handles.a=a %创建新的字段a,将变量a存入handles
Guidata(hObject,handles) %更新handles数据
%%handles结构体具有一定局限性。Handles只将fig文件内的gui组件信息保存进去,而不会将m文件内创建的gui对象存进去。也就是说,handles只存储gui布局区内放置或设置的gui组件。例如gui布局区内的pushbutton、button ground、figure窗口的菜单、工具栏等
例如:若在opening函数中创建一个pushbutton对象
H=uicontrol(‘tag’,‘push1’)
在Guidata(hObject,handles)后加一行命令: handles.push1 会出现错误
说明m文件中创建的对象数据并没有存入handles中。
可采用以下方法实现: h=uicontrol('tag’,’push1’)
Handles.push1=h
Guidata(hObject,handles)
gui数据的专用函数:guidata和guihandles。
Guidata:存储或更新gui数据。 Guidata(obj handle,data)
存储data到obj_handle所在的参窗口中,作为gui数据。若obj_handle不是figure对象句柄,将data保存到对象obj_handle的figure父类中。Guidata任何时刻只能管理一个gui数据,也就是说,任何gui任何时刻只能有一个handles结构。例如:guidata(hObject,handles)表示将handles结构体(即gui数据)的数据更新存储到hObject对象指定的figure对象中。
采用函数guidata管理gui数据,步骤如下:
a.采用语句data=guidata(obj_handle),获取之前的gui数据,备份到data结构体中
b.更新data结构体
c.采用语句guidata(obj_handle,data)将data结构体储存到figure中,作为新的gui数据
guihandles:创建handles结构体。
Handles=guihandles(obj_handle)
返回一个结构体,字段名为obj_handle对象所对应gui窗口内的所有gui对象(包括figure对象)的tag属性值,字段值为这些gui对象的句柄。所以获取handles结构体内的gui对象句柄,可采用结构体的访问方法:handles.(字段名),即handles.(对象tag)
Handles=guihandles
返回当前figure的handles结构体。相当于handles=guihandles(gcf)
采用guihandles创建handles结构体时,要注意:
a.若对象tag为空,或为非法的变量名(例如以数字开头)时,该对象排除在handles结构体外
b.若某些对象具有相同的tag值,他们对应的字段值为一个行向量
c.句柄隐藏的对象包括在handles结构体中
d.由M文件创建的GUI对象也包括在handles结构体内
e.guihandles会清除handles结构体内非gui对象信息的手段
application数据:
application数据也保存在一个结构体中,数据保存在GUI对象的一个未公开属性内,即application data属性,该属性的值为一个结构体。获取application数据由两种方法:
a.采用get或set函数获取或修改对象的applicationdata属性
b.采用application数据的专用函数
setappdata:---添加新字段到指定对象的application数据中、
setappdata(h,name,value) %添加新的字段到对象h的application数据中。字段名为name,字段值为值value
Getappdata:---获取对象的application数据、
value= Getappdata(h,name) %获取对象h的application数据中,name字段的值
values=getappdata(h) %获取对象h的所有application数据
isappdata:---判断对象h的application数据中是否存在字段name。存在,返回真,否则返回假、
isappdata(h,name)
rmappdata:移除对象h的application数据中的字段name
rmappdata(h,name)
%%一个gui中,最多只能同时存在一个gui数据和一个application数据;而且gui数据和application数据均为结构体
%%若采用编程的方法创建gui,可以将guihandles创建的handles结构体作为application数据存储,而不必存为gui数据,若使用guide创建的gui。则必须将handles存为gui数据。
Userdata属性
每个gui对象都有userdata属性,它与applicationdata属性的区别在于:
a.userdata为公开的属性,applicationdata为未公开的属性
b.application的值为一个结构体,而userdata的值可以是任何数据类型,例如数值、矩阵、数组等
%%userdata用于存储用户定义的数据,采用get和set函数访问。
%%除以上3种方式共享gui对象之间的数据,还可采用global定义全局变量的方式共享数据,但global数据并不随着gui的删除而清除,而是一直存在。一个方法是将figure的closerequestfcn函数改为:
Clear glocal %清除全局变量
Delete(hObject) %关闭当前窗口
Opening函数:在gui开始运行但不可见的时候执行,主要进行一些初始化的操作
Output函数:如果需要。可输出数据到命令行
Callback函数:用户每次触发gui对象时,一般都会执行一次相应的callback函数
Guide创建的gui文件中,除主函数外的所有回调函数都有如下两个输入参数:
hObject:所有回调函数的第一个参数。在opening函数和output函数中,表示当前figure对象的句柄;在回调函数中,表示该回调函数所属对象的句柄(注意O是大写)
handles:所有回调函数的第三个参数。表示gui数据,包括所有对象信息和用户数据的结构体,相当于一个gui对象和用户数据的容器。
%%在guide创建的gui的m文件中,无论是gui对象的回调函数,还是其他对象(如串口对象、定时器等)的回调函数,这个回调函数的前两个参数都是这么定义的:第一个参数为该对象句柄,第二个参数为附加参数。如果是gui对象,还有 第三个参数---handles
Feval(fun,x1,….,xn)-----表示使用参数x1,….,xn执行函数fun
输入参数Varargin有两种情况:
输出参数varargout:
为单元数未知的单元数组,理论上能包含任意个输出参数,默认只创建一个输出参数:handles.output。若要返回第二个输出参数,可添加下列语句到output函数:
Varargout{2}=handles.output2
但是gui执行的顺序为:opening函数-----output函数---回调函数。其中opening函数和output函数只会执行一次,执行完output函数就已经输出varargout了
若要gui根据用户的操作来输出varargout,可以使用暂停和继续函数:uiwait和uiresume。
当一个图形对象发生特殊事件时,gui传递要执行的子函数名到m文件中,该子函数称为回调函数(callback函数)
回调函数类型:每个回调函数都有一个触发机制或事件,导致其被调用。
Buttondownfun---单击,执行该函数
Celleditcallback---当编辑表格的单元格时执行的回调函数
Cellselectioncallback---当鼠标选中表格单元时执行的回调函数
Clickedcallback----当push tool或toggle tool被单击时执行
Closerequestfun----当figure关闭时执行
Createfun---在对象创建之后,显示之前执行的函数
Createfun在openingfun前执行,只有在所有的createfcn执行完成后,才进入opening函数
Deletefcn---仅仅在删除对象之前执行
Keypressfcn---当按下按键时,执行当前对象的keypressfun
Keyreleasefcn---在figure对象上释放按键时执行的回调函数
Offcallback----当toggle tool状态为off时执行
Oncallback---当toggle tool状态为on时执行
Resizefcn---重塑figure,panel或button group形状时执行
Selectionchangefcn---当选择button group内不同的ratio button或toggle button时执行
Windowbuttondownfcn---当在窗口内按下鼠标时执行
Windowbuttonmotionfcn---当在figure窗口内移动鼠标时执行
Windowbuttonupfcn---当释放鼠标按键时执行
Windowkeypressfcn---当在窗口内任意对象上按下键盘时执行
Windowkeyreleasefun—当在窗口内任意对象上释放按键时执行
Windowscrollwheelfcn---当在窗口内任意对象上滚动鼠标滑轮时执行
如何控制回调函数的可中断性?
编写回调函数,要充分利用每个回调函数的两个输入参数---hObject和handles,而对于keypressfcn和keyreleasefcn,还要利用其附加参数eventdata,hObject为当前对象的句柄,而handles为所有对象的数据集合,其字段为每个gui对象的标识符(tag值),而字段值为对应gui对象的句柄。keypressfcn和keyreleasefcn的附加参数eventdata包含了当前的按键信息
一旦获得对象的句柄,可以采用get、set、findobj、findall、copyobj、delete、close等一系列的句柄操作函数,对gui对象进行随心所欲的操作
Gui跨平台的兼容性设计
为了设计出在不同平台上运行时外观一致的gui,要注意一下几点:
a.uicontrol对象尽量使用系统默认的字体,即设置uicontrol对象的fontname属性为default,例如: set(handles.pushbutton1,‘fontname’,‘defalut’)
b.uicontrol对象尽量使用默认的背景色,该颜色由系统设定
c.由于像素的大小在不同的计算机显示器上是可变化的,所以使用像素作为单位不能使gui的外观在所有的平台上都一致,若figure窗口大小可随意改变,为了使所有gui组件的大小跟着等比例改变,窗口的units属性值应该设为characters,其他gui对象的units属性值应设为normalized或characters。
%%断点测试主要用于代码的编写和测试阶段,查看函数空间或基本空间内各变量值是否按预期变化。F10键可以单步运行,F5键可以运行到下一个断点处。
%%程序性能分析器可以精确分析每个函数调用、每个语句所花费的时间。
%%gui设计不大可能一步到位就把程序编好,需要不断地修改和完善代码,在这个过程中采用断点测试,是必不可少的手段,而为了测试和优化软件的性能,就需要采用程序性能分析器了
例题:创建一个string为‘颜色设置’,enable为inactive的pushbutton,点击时调用颜色设置对话框,设置pushbutton上的标签颜色(也就是颜色设置四个字会变色)。
%function pushbutton1_ButtonDownFcn(hObject, eventdata, handles)
col = uisetcolor(get(hObject, 'foregroundColor'));
set(hObject, 'foregroundColor', col);
例题:创建一个标签为‘字体设置’且两行显示,背景为白色且处于未激活状态的static text,单击时调用字体设置对话框,设置标签的字体。
%function text1_ButtonDownFcn(hObject, eventdata, handles)
font = uisetfont(hObject);
%%切换按钮(toggle button)通常用于表示二值状态,如‘运行’与‘停止’
例题:创建一个static text和toggle button,当toggle button弹起时,static text显示为红色,当toggle button按下时,static text显示为绿色。
%function togglebutton1_Callback(hObject, ~, handles)
if get(hObject, 'Value')
set(handles.text1, 'BackgroundColor', 'green');
else
set(handles.text1, 'BackgroundColor', 'red');
end
%%slider用于获取指定范围内的数值,用户通过滑动滑块,改变slider的value值,使得其value值在Min和Max之间变化
Slider的步长与步长比例的关系:
最小步长x=(max-min)×最小步长比例
最大步长y=(max-min)×最大步长比例
例题:用slider控制static text显示【0,200】范围内的任意整数
%function slider1_Callback(hObject, eventdata, handles)
val = get(hObject, 'Value');
set(handles.text1, 'String', num2str(val, '%3.0f'));
%function slider1_CreateFcn(hObject, eventdata, handles)
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
例题:设计一个标签为‘保存数据’的radio button,当鼠标单击使ratio button处于‘选中’状态时,弹出文件保存对话框,并显示用户选择的路径和保存的文件名。
%function radiobutton1_Callback(hObject, eventdata, handles)
if get(hObject, 'Value')
[fName, pName, index] = uiputfile('*.*', '文件另存为');
if index
str = [pName fName];
set(handles.text1, 'String', str);
end
end
%%edit text允许用户修改文本内容,用于数据的输入与显示,若Max-Min>1,允许edit text显示多行文本,否则,只允许单行输入。
例题:在edit text内输入0~1之间的任意数,来改变slider的滑块位置。
%function edit1_Callback(hObject, eventdata, handles)
str = get(hObject, 'String');
num = str2double(str);
if ~isnan(num) && (num <= 1) && (num >= 0)
set(handles.slider1, 'Value', num);
end
%%check box与radio button类似,用于显示一对互斥的状态,通过鼠标左键单击,可在‘选中’与‘未选中’两种状态之间切换。对应这两种状态,其value值也在min属性值与max属性值之间切换
%function checkbox1_Callback(hObject, eventdata, handles)
if get(hObject, 'Value')
set(handles.slider1, 'Enable', 'on');
else
set(handles.slider1, 'Enable', 'off');
end
%% a=eventdata.Indices(1) %获取当前单元格的行索引
b=eventdata.Indices(2) %获取当前单元格的列索引(I一定大写)
例题:设计一个选项依次为‘语文’,‘数学’,‘英语’的list box和一个空白的static text,当左键双击listbox中任一项时,将其内容显示于static text中。
%function listbox1_Callback(hObject, eventdata, handles)
mouseType = get(handles.figure1, 'SelectionType');
if strcmp(mouseType, 'open')
index = get(hObject, 'Value');
str = get(hObject, 'String');
set(handles.text1, 'String', str{index});
end
若是单击左键则是:
%function listbox1_Callback(hObject, eventdata, handles)
index = get(hObject, 'Value');
str = get(hObject, 'String');
set(handles.text1, 'String', str{index});
%%strcmp(s1,s2)---寻找s1和s2是否完全匹配
Strcnmp(s1,s2,n)---寻找s1和s2的前n个字符是否完全匹配
%%对于pop-up menu与listbox对象,在设置string的同时,记得一定要设置value值,原因很简单,string值若为字符串单元数组,则其单元个数限定了value的最大值???
例题:设计一个pop-up menu和listbox,pop-up menu选项依次为‘黑龙江’,‘湖北’。当pop-up menu选择‘黑龙江’时,listbox依次显示‘哈尔滨’,‘大庆’……;当pop-up menu选择‘湖北’时,listbox依次显示‘武汉’,‘黄冈’……
%%注意:case 1之间有一个空格
%%button group为gui对象的容器,它可以包含下列类型的子对象:axes对象、uicontrol对象、button group对象
%%selectionchangefcn回调函数的第一个输入参数为hObject,它并不是button group对象的句柄,而是button group内当前所选对象的句柄,也就是button group的selectedObject属性值
例题:设计一个二进制与十进制相互转换的gui界面,要求在edit text的输入正整数,当选中二进制时,该值转换为二进制,若选择为十进制,该值转换为十进制
%function bin_dec_SelectionChangeFcn(hObject, eventdata, handles)(在button group对象上)
str = get(handles.num, 'String');
switch get(hObject, 'Tag')
case 'bin',
val = str2double(str);
if ~isnan(val) && (floor(abs(val)) == val) %floor向下取整数
set(handles.num, 'String', dec2bin(val));
end
case 'dec',
if all((str == '0' | str == '1'))
set(handles.num, 'String', [num2str(bin2dec(str)) '.']);
end
end
%%绘制直方图(bar)
y=[1,2;5,4;6,2]
bar(y,0.2) %0.2表示柱子的宽度(0.2如果不写代表默认宽度)
set(gca,'xticklabel',{'2002','2004','2006'})
xlabel('时间')
ylabel('利润')
legend('A公司','B公司')
例题:编写一个gui,读取excel文件data.xls,并将其显示在uitable对象中,要求:
a.将文件data.xls中的第一行显示为列名
b.鼠标选中单元格时,uitable对象右边显示该单元格的行、列、数据等信息
c.修改完表格数据后,单击[保存]将表格中的数据存为excel文件
原excel文件数据如下:
%function example6_1_11_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to example6_1_11 (see VARARGIN)
% Choose default command line output for example6_1_11
handles.output = hObject;
[~, ~, raw] = xlsread('data.xls');
set(handles.table, 'ColumnName', raw(1, :));
data = raw(2:end, :);
for i = 1 : numel(data)
if isnan(data{i})
data{i} = ' ';
end
end
set(handles.table, 'Data', data, 'ColumnEditable', true);
guidata(hObject, handles);
%function table_CellSelectionCallback(hObject, eventdata, handles)
data = get(hObject, 'Data');
temp = eventdata.Indices;
set(handles.line, 'String', num2str(temp(1)));
set(handles.col, 'String', num2str(temp(2)));
set(handles.data, 'String', num2str(data{temp(1), temp(2)}));
%function save_Callback(~, ~, handles)
[fName, pName, index] = uiputfile('*.xls', '数据保存为', datestr(now, 30));
if index == 1
data = get(handles.table, 'Data');
colName = get(handles.table, 'columnName');
data2 = [colName'; data];
str = [pName fName];
xlswrite(str, data2);
end
%%axes用于数据的可视化,即显示图形或图像。Axes是核心图形对象的容器,它可以包含下列gui核心图形对象:image、light、line、patche、rectangle、surface、text对象,以及由核心对象组成的hggroup。
例题:设计一个坐标轴和一个按钮,单击按钮时弹出文件选择对话框,载入用户指定的*.jpg或*.bmp图片
%function loadPic_Callback(hObject, eventdata, handles)
if exist('path.mat', 'file')
load('path.mat', 'pathName');
else
pathName = pwd;
end
[fName, pathName, index] = uigetfile({'*.bmp'; '*.jpg'}, '选择图片', pathName);
if index
str = [pathName fName];
imshow(str);
save path.mat pathName;
end
回调函数中的数据传递:
Gui的m文件中包含很多回调函数和其他函数,这些函数都有自己的函数空间,它们之间的数据传递是必不可少的。Guide创建的gui,可用以下方法传递信息:
当在两个回调函数的开始都使用了下面的定义:
global a %将a声明为全局变量
变量a就成为这两个回调函数共享的数据了
对于由guide创建的gui,创建时会将所有tag值不为空的信息存入handles结构体,对象的tag值为字段名,对象的句柄值为字段值。所以,guide创建的gui,对象之间可以进行随意访问
Handles不仅可以存储gui对象的信息,还可以存储变量:
Handles.变量名=变量值 %新建字段
Guidata(h.handles) %更新handles
gui对象有一个未公开属性:applicationdata,它用于存储application数据,值为一个结构体(不妨称之为application结构体)。要访问application数据,很多时候还是要首先利用handles结构体获取gui对象的信息。
getappdata:获取application结构体指定字段的值
setappdata:创建或设置application结构体指定字段的值
rmappdata:移除application结构体指定的字段
每个gui对象都有一个供用户存取数据的属性:userdata。userdata仅能存取一个变量值,因此当同一对象存储两个变量时,先前的变量值就会被覆盖掉,因此都用userdata存储简单的数据。
%%如果变量需要占用大量的内存,不宜存储为gui数据。若放在handles里,会加大每个回调函数不必要的内存开销,因为handles是每个回调函数的输入参数。大的变量若存取不频繁,建议放到某个对象的userdata属性或者application结构体内;若存取比较频繁,例如定时器的回调函数经常访问该变量,此时建议将其存为global变量
gui界面之间的数据传递
findall(0,‘type’,‘figure’,‘tag’,‘figure1’)
假设在窗口1的openingfcn函数中,采用函数创建了一个子窗口2:
h_fig = figure(‘visible’ , ’off’,…….)
h_btn1 = uicontrol(‘parent’ , ’h_fig’ , ‘tag’ , ‘btn1’,…….)
h_btn2 = uicontrol(‘parent’ , ’h_fig’ , ‘tag’ , ‘btn2’,…….)
h_btn3 = uicontrol(‘parent’ , ’h_fig’ , ‘tag’ , ‘btn3’,…….)
如果要在窗口1的任何回调函数中直接访问子窗口2的任意控件,可以在上述语句后紧跟着写下如下语句:
handles.btn1 = h_btn1
handles.btn2= h_btn2
handles.btn3 = h_btn3
最后,需要一个guidata语句。当然,opening函数最后有guidata语句,所以不用自己添加
%%有时在执行windowbuttondownfcn回调函数时,需要知道用户是单击左键、单击中键、单击右键还是双击左键和右键,此时需要用到figure的selectionType属性。
selectionType属性值为窗口中最后一次鼠标操作的类型。以下为属性对应的操作类型:
normal----单击左键
extend----单击中键、shift+左键
alt----单击右键、ctrl+左键
open----双击左键、双击右键