MATLAB和全局变量的使用?

3
我正在编写一款针对Dicom图像和光谱的工具,其中有很多共享数据我想要在我制作的函数之间使用。我有一个自己制作的GUI,不同的滑块和按钮使用了许多来自Dicom文件的共享数据。
我一直在使用全局变量来存储所有这些函数共享的信息。我目前有很多全局变量。据我所学,应尽可能避免使用全局变量,因为这会增加耦合。每个函数中都读取Dicom文件中的数据是否更好?这似乎是冗余的。使用MATLAB面向对象编程是否有帮助?
5个回答

6
我建议使用应用程序数据结构。
应用程序数据是由您的应用程序定义的结构,通常附加在GUI应用程序或图形窗口上的基本数据。
要使用应用程序数据(appdata),请使用setappdatagetappdata函数。例如,假设您有一个存储为hGUI的GUI句柄,则以下代码从MATLAB文档中获取一个随机矩阵并将其添加到您的应用程序数据中,然后稍后检索它。
% Save matrix for later
matrix = randn(35);
setappdata(hGUI, 'mydata', matrix);

% Do some stuff...

% Retrieve my matrix, this could be in a different file to `setappdata`
myMatrix = getappdata(hGUI, 'mydata');

你可以在应用数据中存储基本上任意的数据,并且只要 hGUI 引用你的 GUI 应用程序,你就可以从任何源文件中存储和获取它。

你也可以使用 guidata,它是 getappdata 的快捷方式。 - Andrey Rubshtein
1
@Andrey 是的,但是“guidata 一次只能管理一个变量”。因此,您需要使用结构体,并根据需要添加字段。 - Chris
是的,当然您说得对。这是个人偏好问题。 - Andrey Rubshtein

4

既然你提到你正在使用GUI并且想要在控制回调之间共享数据,我建议你使用嵌套函数设计你的代码。整个代码大致如下:

function dicomGUI

  %# Initialize your GUI here, linking the control callbacks to the
  %#   nested functions below:

  hLoad = uicontrol('Style', 'push', 'String', 'Load file', ...
                    'Callback', @load_file);
  ...

  %# Initialize the data variables for the DICOM files here:

  data = [];  %# Shared among nested functions
  ...

  %# Below are all the nested functions your controls will use:

  function load_file(hSource, event)
    data = ...;  %# Load the data here
  end
  ...

end

这不仅让您将所有GUI代码放在一个m文件中,而且简化了控制回调,并使它们可以轻松共享父函数dicomGUI的工作区变量。此方法的示例以及在GUI控件之间共享数据的其他建议可以在此文档页面上找到:在GUI回调之间共享数据

正如Chris所提到的,对于大型和复杂的GUI,这可能会导致非常大的m文件。在这种情况下,为了保持文件大小,我建议将每个回调的主体简单地调用一个单独文件中的函数,该函数接受共享数据变量,执行必要的工作,然后将修改后的数据返回给相同的共享变量。例如:

function transform_callback(hSource, event)

  %# Apply some transform to the data:
  data = transform_data(data);

  %# If the above changes the GUI (disabling controls, changing a
  %#   display, etc.), then those changes should be made here.

end

这与我所做的类似,我喜欢这个想法。@Chris 我已经有大约2K行了,这可能是一个愚蠢的问题,但为什么这会使这个建议不是最好的呢? - Ben Fossen
显然这是个人口味问题,但我发现将代码分成小模块(文件),每个模块都有明确定义的目的,会让代码更易于导航。特别是当我不得不尝试理解别人的代码时! - Chris
@Chris:说得好。我在最近的编辑中解决了大小问题。另外,我通常使用许多注释和长分隔线(如%-----------)来清晰地标记函数的开始和结束位置,特别是嵌套和子函数的部分。我还编写了一个有用的小工具ftoc,用于创建函数目录。 - gnovice
ftoc 看起来是一个不错的工具。编辑功能的加入也很好,点赞 +1。 - Chris
@BenFossen,嵌套函数具有包含函数的整个作用域。这有点违反了封装原则。更准确地说,是信息隐藏原则。 - Andrey Rubshtein

2

由于 MATLAB 的面向对象特性,还有另外一个可能性。您可以定义自己的句柄类,并在初始化阶段将其作为附加参数传递给每个回调函数:

classdef Data<handle
    properties (Access=public)
        Val;
    end
end

function SimpleGui
    data = Data();

    hLoad = uicontrol('Style', 'push', 'String', 'Push me', ...
                      'Callback', {@callback data});
    data.Val = 5;
end

function callback(hSource, event, data)
    data.Val = data.Val+1;
    disp(data.Val);
end

另一个选项:

此外,关于 guidata/appdata(如@Chris所述),可以通过以下方式进行改进:

创建一个封装回调函数,始终获取并设置 guidata

function CallbackWrapper(hObj,evt,func)
    data = guidata(hObj);
    data = func(hObj,evt,data);
    guidata(hObj,data);
end

现在您的回调函数应该按照以下方式定义(请注意不同的签名):
function SimpleGui
    hSave = uicontrol('Style', 'push', 'String', 'Push me', ...
        'Callback', {@CallbackWrapper @myCallBack});
    data.x = 1;
    guidata(hSave,data);
end

function data = myCallBack(hObj,evt,data)
    data.x = data.x + 1;
    disp(data.x);
end

2
作为一种规则,全局变量通常是不好的。通常有几种更好的方法,包括:
  1. 最初读取数据,并将其传递给需要它的每个函数。
  2. 读取数据,每个需要它的函数调用一个返回数据的函数。
你可能还需要在返回时更新数据包,这取决于你是否仅使用数据或者是否同时更改数据。
以上任何一种想法都应该有助于优化你的过程。这使得你的代码更易读,也更不容易出现错误。

0

如果您正在使用较新版本的MATLAB,则应该利用对象导向编程系统(OOPS)。

您应该遵循软件设计原则,首先设计一个合理的软件架构。在编写任何代码之前,您应该这样做。我建议使用UML进行软件建模。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接