我正在编写一个用于 dicom 图像和光谱学的工具,并且我想在我正在制作的功能之间使用很多共享数据。我有自己制作的 GUI,不同的滑块和按钮使用来自 dicom 文件的大量共享数据。
我一直在使用全局变量来存储所有这些函数共享的信息。我目前有很多全局变量。由于耦合增加,我被教导尽可能避免使用全局变量。在每个函数中从 dicom 文件中读取数据会更好吗?这似乎是多余的。将 MATLAB 用作面向对象的帮助吗?
我建议使用应用程序数据结构。
应用程序数据是存储为由应用程序定义的结构的基本数据,通常附加到 GUI 应用程序或图形窗口。
要使用应用程序数据 ( appdata
),请使用setappdata
和getappdata
函数。例如,假设您的 GUI 的句柄存储为hGUI
,以下内容会向您的应用程序数据添加一个随机矩阵,然后稍后检索它(从 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 应用程序即可。
由于您提到您正在使用 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
由于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
全局变量通常是一件坏事。通常有几种更好的方法,其中包括:
您可能还需要在返回时以某种方式更新数据包,具体取决于您是仅使用数据还是更改数据以及使用它。
这些想法中的任何一个都应该有助于您的过程。它使您的代码更具可读性,并且不太可能犯某种错误。
如果您使用的是 MATLAB 的较新版本之一,则应利用 OOPS(面向对象的编程系统)。
您应该遵守软件设计原则,并从构建一个合理的软件设计开始。您应该在编写任何代码之前执行此操作。我推荐使用 UML 进行软件建模。