3

I am writing a tool for dicom images and spectroscopy and there is a lot of shared data I want to use between the functions I am making. I have GUI that I made and the different sliders and buttons use a lot of this shared data from the dicom files.

I have been using global variables to store information that all of these functions share. I have a lot of globals currently. I have been taught to avoid global variables if possible because of increasing coupling. Would it be better to read in the data from the dicom file in each function? This seems redundant. Would using MATLAB as object-oriented help?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ben Fossen
  • 997
  • 6
  • 22
  • 48

5 Answers5

6

I would recommend using application data structures.

Application data is essential data stored as a structure that is defined by your application and is typically attached to a GUI application or figure window.

To use application data (appdata) use the setappdata and getappdata functions. For example, assuming that you have a handle to your GUI stored as hGUI, the following adds a random matrix to your application data and then retrieves it later (lifted from MATLAB documentation)

% 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');

You can store essentially arbitrary data in your application data, and you can store it and get it from any of your source files, as long as hGUI refers to your GUI application.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris
  • 44,602
  • 16
  • 137
  • 156
4

Since you mention you are working with a GUI and wanting to share data between the control callbacks, I would suggest designing your code using nested functions. The overall code would look something like this:

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

Not only does this let you put all your GUI code in one m-file, but it simplifies the control callbacks and makes it easy for them to share variables in the workspace of the parent function dicomGUI. An example of this approach, along with other suggestions for sharing data between GUI controls, can be found on this documentation page: Share Data Among a GUI's Callbacks.

As Chris mentions, this could become a very large m-file for a large and intricate GUI. To keep the file size down in such a case I would suggest making the body of each callback simply a call to a function in a separate file which accepts the shared data variables, performs whatever work is necessary, then returns the modified data to the same shared variables. For example:

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
gnovice
  • 125,304
  • 15
  • 256
  • 359
  • This is similar to what I have done and I like the idea. @Chris I have about 2K lines so far this might be a stupid question but why would that make this suggestion not be the best? – Ben Fossen Feb 02 '12 at 20:54
  • Obviously it is personal taste, but I find it *a lot* easier to navigate code that is split up into small modules (files) each with a well defined purpose. Especially when I have had to try and understand someone elses code! – Chris Feb 02 '12 at 20:57
  • @Chris: Good point. I addressed the size concern in my recent edit. Alternatively, I usually use many comments and long separating lines like `%-----------` to clearly mark where functions begin and end, especially sections for nested and subfunctions. I also wrote myself a helpful little tool called [ftoc](http://www.mathworks.com/matlabcentral/fileexchange/20164-ftoc-v1-2) to create a function table of contents. – gnovice Feb 02 '12 at 21:08
  • `ftoc` looks like a nice tool. Good addition with the edit aswell +1. – Chris Feb 02 '12 at 21:22
  • @BenFossen, nested functions have the entire scope of the containing function. It kind of breaks the encapsulation principle. Or more precisely, the information hiding principle. – Andrey Rubshtein Feb 07 '12 at 21:16
2

Globals as a rule are a bad thing. There are a couple of better ways typically, which include:

  1. Reading in the data initially, and passing it to each function which needs it.
  2. Reading it the data, and each function which needs it calls a function which returns it.

You might need to update the data package upon return somehow as well, depending on if you only use the data or if you change the data as well as using it.

Either one of these ideas should help your process. It makes your code much more readable, and less likely to make some kind of a mistake.

PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
2

There is another possibility due to the object-oriented nature of MATLAB. You can define your own handle class and pass it in the initialization phase to each callback as an additional argument:

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

Yet another option:

Also, regarding the guidata/appdata (as described by @Chris), it can be improved in the following way:

Create an encapsulating callback that always gets and sets guidata:

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

Now your callbacks should be defined in the following way (note the different signature):

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrey Rubshtein
  • 20,795
  • 11
  • 69
  • 104
0

If you are using one of the later releases of MATLAB, you should take advantage of the OOPS (object oriented programming system).

You should adhere to software design principles and start by architecting a sound software design. You should do this before writing any code. I recommend using UML for software modeling.

siliconwafer
  • 732
  • 4
  • 9