Matlab 什么';这是“什么?”;对",;组织GUI代码的方法?
我正在开发一个相当复杂的GUI程序,该程序将与MATLAB编译器一起部署。(有很好的理由使用MATLAB构建这个GUI,这不是问题的重点。我意识到GUI构建并不适合这种语言。) 有很多方法可以在GUI中的函数之间共享数据,甚至在应用程序中的GUI之间传递数据:Matlab 什么';这是“什么?”;对",;组织GUI代码的方法?,matlab,user-interface,matlab-guide,matlab-deployment,Matlab,User Interface,Matlab Guide,Matlab Deployment,我正在开发一个相当复杂的GUI程序,该程序将与MATLAB编译器一起部署。(有很好的理由使用MATLAB构建这个GUI,这不是问题的重点。我意识到GUI构建并不适合这种语言。) 有很多方法可以在GUI中的函数之间共享数据,甚至在应用程序中的GUI之间传递数据: setappdata/getappdata/____;appdata-将任意数据关联到句柄 guidata-通常与指南一起使用;“存储或检索GUI数据”到句柄结构 对句柄对象的UserData属性应用set/get操作 在主函数中使用嵌
-将任意数据关联到句柄setappdata/getappdata/____;appdata
-通常与指南一起使用;“存储或检索GUI数据”到句柄结构guidata
- 对句柄对象的
属性应用UserData
操作set/get
- 在主函数中使用嵌套函数;基本上模拟“全局”范围变量
- 在子功能之间来回传递数据
function myGui
fig = figure(...);
% h is a struct that contains handles to all the ui objects to be instantiated. My convention is to have the first field be the uicontrol type I'm instantiating. See draw_gui nested function
h = struct([]);
draw_gui;
set_callbacks; % Basically a bunch of set(h.(...), 'Callback', @(src, event) callback) calls would occur here
%% DRAW FUNCTIONS
function draw_gui
h.Panel.Panel1 = uipanel(...
'Parent', fig, ...
...);
h.Panel.Panel2 = uipanel(...
'Parent', fig, ...
...);
draw_panel1;
draw_panel2;
function draw_panel1
h.Edit.Panel1.thing1 = uicontrol('Parent', h.Panel.Panel1, ...);
end
function draw_panel2
h.Edit.Panel2.thing1 = uicontrol('Parent', h.Panel.Panel2, ...);
end
end
%% CALLBACK FUNCTIONS
% Setting/getting application data is done by set/getappdata(fig, 'Foo').
end
我以前编写的代码中没有嵌套任何内容,因此我在任何地方来回传递h
(因为需要重新绘制、更新内容等)和setappdata(图)
,以存储实际数据。无论如何,我一直在一个文件中保存一个“活动”,我相信这将是未来维护的噩梦。回调同时与应用程序数据和图形句柄对象交互,我认为这是必要的,但这阻止了代码库的两个“一半”的完全分离
因此,我在这里寻找一些组织/GUI设计帮助。即:
- 是否有我应该用来组织的目录结构?(回调与绘图函数?)
- 与GUI数据交互并将其与应用程序数据隔离的“正确方式”是什么?(当我提到GUI数据时,我指的是
ting句柄对象的属性)set/get
- 我如何避免将所有这些绘图功能放在一个包含数千行的巨大文件中,并且仍然高效地来回传递应用程序和GUI数据?可能吗
- 持续使用
是否会导致性能下降set/getappdata
- 我的后端代码(3个对象类和一堆助手函数)是否应该采用某种结构,以便从GUI的角度进行维护
setappdata
,getappdata
,rmappdata
,isappdata
,等等)提供了一个很好的替代方法,可以替代比较笨拙的get/set(hFig,'UserData',dataStruct)
方法。事实上,为了管理GUI数据,《指南》使用了该功能,它只是setappdata
/getappdata
函数的包装
AppData方法相对于'UserData'
属性的几个优点如下:
- 多个异构属性的更自然界面
仅限于一个变量,要求您设计另一层数据组织(即结构)。假设您要存储一个字符串用户数据
和一个数字数组str='foo'
。对于v=[12]
,您需要采用结构方案,例如UserData
和s=struct('str','foo','v',[12])
只要你想要任何一个属性(例如set/get
)。使用s.str='bar';set(h,'UserData',s);
,过程更直接(也更高效):setappdata
setappdata(h,'str','bar')代码>
- 底层存储空间的受保护接口
虽然
只是一个常规句柄图形属性,但包含应用程序数据的属性不可见,尽管可以通过名称('ApplicationData',但不要这样做!)访问它。您必须使用'UserData'
来更改任何现有的AppData属性,这可以防止您在尝试更新单个字段时意外地删除setappdata
的全部内容。此外,在设置或获取AppData属性之前,您可以使用'UserData'
验证命名属性的存在,这有助于异常处理(例如,在设置输入值之前运行进程回调)和管理GUI的状态或它所管理的任务(例如,通过某些属性的存在来推断进程的状态,并适当地更新GUI)isappdata
'UserData'
和'ApplicationData'
属性之间的一个重要区别是'UserData'
默认为[]
(一个空数组),而'ApplicationData'
本质上是一个结构。这一差异,加上setappdata
和getappdata
没有M文件实现(它们是内置的),表明使用setappdata
设置命名属性不需要重写数据结构的全部内容(想象一个执行结构字段就地修改的MEX函数-MATLAB能够通过将结构维护为
hFig = ancestor(hObj,'Figure')
function pushbutton17_callback(hObject,evt, handles)
some_text = someOtherFunction();
set(handles.text45, 'String', some_text);
end
handles.panel17 = uipanel(...);
createTable(handles.panel17); % creates a table in the specified panel
classdef Model < handle
%MODEL represents a signal composed of two components + white noise
% with sampling frequency FS defined over t=[0,1] as:
% y(t) = a * sin(2pi * f*t) + sin(2pi * 2*f*t) + white_noise
% observable properties, listeners are notified on change
properties (SetObservable = true)
f % frequency components in Hz
a % amplitude
end
% read-only properties
properties (SetAccess = private)
fs % sampling frequency (Hz)
t % time vector (seconds)
noise % noise component
end
% computable dependent property
properties (Dependent = true, SetAccess = private)
data % signal values
end
methods
function obj = Model(fs, f, a)
% constructor
if nargin < 3, a = 1.2; end
if nargin < 2, f = 5; end
if nargin < 1, fs = 100; end
obj.fs = fs;
obj.f = f;
obj.a = a;
% 1 time unit with 'fs' samples
obj.t = 0 : 1/obj.fs : 1-(1/obj.fs);
obj.noise = 0.2 * obj.a * rand(size(obj.t));
end
function y = get.data(obj)
% signal data
y = obj.a * sin(2*pi * obj.f*obj.t) + ...
sin(2*pi * 2*obj.f*obj.t) + obj.noise;
end
end
% business logic
methods
function [mx,freq] = computePowerSpectrum(obj)
num = numel(obj.t);
nfft = 2^(nextpow2(num));
% frequencies vector (symmetric one-sided)
numUniquePts = ceil((nfft+1)/2);
freq = (0:numUniquePts-1)*obj.fs/nfft;
% compute FFT
fftx = fft(obj.data, nfft);
% calculate magnitude
mx = abs(fftx(1:numUniquePts)).^2 / num;
if rem(nfft, 2)
mx(2:end) = mx(2:end)*2;
else
mx(2:end -1) = mx(2:end -1)*2;
end
end
end
end
function handles = View_TimeDomain(m)
%VIEW a GUI representation of the signal model
% build the GUI
handles = initGUI();
onChangedF(handles, m); % populate with initial values
% observe on model changes and update view accordingly
% (tie listener to model object lifecycle)
addlistener(m, 'f', 'PostSet', ...
@(o,e) onChangedF(handles,e.AffectedObject));
end
function handles = initGUI()
% initialize GUI controls
hFig = figure('Menubar','none');
hAx = axes('Parent',hFig, 'XLim',[0 1], 'YLim',[-2.5 2.5]);
hSlid = uicontrol('Parent',hFig, 'Style','slider', ...
'Min',1, 'Max',10, 'Value',5, 'Position',[20 20 200 20]);
hLine = line('XData',NaN, 'YData',NaN, 'Parent',hAx, ...
'Color','r', 'LineWidth',2);
% define a color property specific to the view
hMenu = uicontextmenu;
hMenuItem = zeros(3,1);
hMenuItem(1) = uimenu(hMenu, 'Label','r', 'Checked','on');
hMenuItem(2) = uimenu(hMenu, 'Label','g');
hMenuItem(3) = uimenu(hMenu, 'Label','b');
set(hLine, 'uicontextmenu',hMenu);
% customize
xlabel(hAx, 'Time (sec)')
ylabel(hAx, 'Amplitude')
title(hAx, 'Signal in time-domain')
% return a structure of GUI handles
handles = struct('fig',hFig, 'ax',hAx, 'line',hLine, ...
'slider',hSlid, 'menu',hMenuItem);
end
function onChangedF(handles,model)
% respond to model changes by updating view
if ~ishghandle(handles.fig), return, end
set(handles.line, 'XData',model.t, 'YData',model.data)
set(handles.slider, 'Value',model.f);
end
function handles = View_FrequencyDomain(m)
handles = initGUI();
onChangedF(handles, m);
hl = event.proplistener(m, findprop(m,'f'), 'PostSet', ...
@(o,e) onChangedF(handles,e.AffectedObject));
setappdata(handles.fig, 'proplistener',hl);
end
function handles = initGUI()
% load FIG file (its really a MAT-file)
hFig = hgload('ViewGUIDE.fig');
%S = load('ViewGUIDE.fig', '-mat');
% extract handles to GUI components
hAx = findobj(hFig, 'tag','axes1');
hSlid = findobj(hFig, 'tag','slider1');
hTxt = findobj(hFig, 'tag','fLabel');
hMenu = findobj(hFig, 'tag','cmenu1');
hMenuItem = findobj(hFig, 'type','uimenu');
% initialize line and hook up context menu
hLine = line('XData',NaN, 'YData',NaN, 'Parent',hAx, ...
'Color','r', 'LineWidth',2);
set(hLine, 'uicontextmenu',hMenu);
% customize
xlabel(hAx, 'Frequency (Hz)')
ylabel(hAx, 'Power')
title(hAx, 'Power spectrum in frequency-domain')
% return a structure of GUI handles
handles = struct('fig',hFig, 'ax',hAx, 'line',hLine, ...
'slider',hSlid, 'menu',hMenuItem, 'txt',hTxt);
end
function onChangedF(handles,model)
[mx,freq] = model.computePowerSpectrum();
set(handles.line, 'XData',freq, 'YData',mx)
set(handles.slider, 'Value',model.f)
set(handles.txt, 'String',sprintf('%.1f Hz',model.f))
end
function [m,v1,v2] = Controller
%CONTROLLER main program
% controller knows about model and view
m = Model(100); % model is independent
v1 = View_TimeDomain(m); % view has a reference of model
% we can have multiple simultaneous views of the same data
v2 = View_FrequencyDomain(m);
% hook up and respond to views events
set(v1.slider, 'Callback',{@onSlide,m})
set(v2.slider, 'Callback',{@onSlide,m})
set(v1.menu, 'Callback',{@onChangeColor,v1})
set(v2.menu, 'Callback',{@onChangeColor,v2})
% simulate some change
pause(3)
m.f = 10;
end
function onSlide(o,~,model)
% update model (which in turn trigger event that updates view)
model.f = get(o,'Value');
end
function onChangeColor(o,~,handles)
% update view
clr = get(o,'Label');
set(handles.line, 'Color',clr)
set(handles.menu, 'Checked','off')
set(o, 'Checked','on')
end