gpt4 book ai didi

matlab - "right"GUI代码的组织方式是什么?

转载 作者:太空宇宙 更新时间:2023-11-03 19:06:35 26 4
gpt4 key购买 nike

我正在开发一个相当复杂的 GUI 程序,该程序将与 MATLAB Compiler 一起部署。 (有充分的理由使用 MATLAB 来构建此 GUI,这不是这个问题的重点。我意识到 GUI 构建不是这种语言的强项。)

有很多方法可以在 GUI 中的函数之间共享数据,甚至可以在应用程序中的 GUI 之间传递数据:

  • setappdata/getappdata/_____appdata - 将任意数据关联到句柄
  • guidata - 通常与 GUIDE 一起使用; “存储 [s] 或检索 [s] GUI 数据”到句柄结构
  • 对句柄对象的 UserData 属性应用 set/get 操作
  • 在主函数中使用嵌套函数;基本上模拟“全局”范围变量。
  • 在子函数之间来回传递数据

我的代码结构不是最漂亮的。现在我将引擎与前端分离(很好!),但 GUI 代码非常像意大利面条。这是一个“事件”的骨架,借用 Android 的说法:

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(fig ) 来存储实际数据。无论如何,我一直将一个“事件”保存在一个文件中,而且我确信这将成为 future 维护的噩梦。回调与应用程序数据和图形句柄对象交互,我认为这是必要的,但这阻止了代码库的两个“部分”的完全隔离。

所以我在这里寻找一些组织/GUI 设计方面的帮助。即:

  • 有没有我应该用来组织的目录结构? (回调与绘图函数?)
  • 与 GUI 数据交互并使其与应用程序数据隔离的“正确方法”是什么? (当我提到 GUI 数据时,我指的是设置/获取处理对象的属性)。
  • 如何避免将所有这些绘图函数放入一个数千行的巨大文件中,同时仍然有效地来回传递应用程序和 GUI 数据?这可能吗?
  • 经常使用 set/getappdata 是否会降低性能?
  • 我的后端代码(3 个对象类和一堆辅助函数)是否应该采用任何结构,以便从 GUI 的角度更易于维护?

我不是专业的软件工程师,我只是知道足够危险,所以我确信这些对于经验丰富的 GUI 开发人员(使用任何语言)都是相当基本的问题。我几乎觉得 MATLAB 中缺少 GUI 设计标准(是否存在?)严重干扰了我完成该项目的能力。这是一个 MATLAB 项目,比我做过的任何项目都要庞大,而且我以前从来没有考虑过具有多个图形窗口等的复杂 UI。

最佳答案

作为@SamRoberts解释,Model–view–controller (MVC) 模式非常适合作为设计 GUI 的架构。我同意没有很多 MATLAB 示例可以展示这种设计...

下面是我编写的一个完整而简单的示例,用于在 MATLAB 中演示基于 MVC 的 GUI。

  • 模型 表示一些信号 y(t) = sin(..t..) 的一维函数。它是一个句柄类对象,这样我们就可以传递数据而不会创建不必要的副本。它公开可观察的属性,允许其他组件监听更改通知。

  • View 将模型呈现为线条图形对象。该 View 还包含一个 slider 来控制其中一个信号属性,并监听模型更改通知。我还包含了一个特定于 View (而非模型)的交互式属性,其中可以使用右键单击上下文菜单控制线条颜色。

  • Controller 负责初始化所有内容并响应来自 View 的事件,并相应地正确更新模型。

请注意, View 和 Controller 是作为常规函数编写的,但如果您更喜欢完全面向对象的代码,则可以编写类。

与通常的 GUI 设计方式相比,这需要一些额外的工作,但这种架构的优点之一是数据与表示层的分离。这使得代码更清晰、更易读,尤其是在使用复杂的 GUI 时,代码维护变得更加困难。

这种设计非常灵活,因为它允许您为同一数据构建多个 View 。您甚至可以拥有多个同时 View ,只需在 Controller 中实例化更多 View 实例,然后查看一个 View 中的更改如何传播到另一个 View !如果您的模型可以以不同的方式直观地呈现,这将特别有趣。

此外,如果您愿意,可以使用 GUIDE 编辑器构建界面,而不是以编程方式添加控件。在这样的设计中,我们只会使用 GUIDE 通过拖放来构建 GUI 组件,但不会编写任何回调函数。所以我们只对生成的 .fig 文件感兴趣,而忽略伴随的 .m 文件。我们将在 View 函数/类中设置回调。这基本上就是我在 View_FrequencyDomain View 组件中所做的,它加载了使用 GUIDE 构建的现有 FIG 文件。

GUIDE generated FIG-file


模型.m

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

View_TimeDomain.m

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

View_FrequencyDomain.m

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

Controller .m

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

MVC GUI1 MVC GUI2

在上面的 Controller 中,我实例化了两个独立但同步的 View ,它们都表示和响应同一基础模型中的变化。一个 View 显示信号的时域,另一个 View 显示使用 FFT 的频域表示。

关于matlab - "right"GUI代码的组织方式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20064610/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com