Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Nested Function Interaction with Global Variables

Recently on two different occasions, people have asked me to help debug their nested function< code. I was struck by the similarity of their problems and today will show you the problem and some solutions.

The problem, simply stated, is this. The users wrote GUIs using nested functions and brought up a single instance which ran fine. Then they brought up a second instance while the first one was running, and ran the second one. So far, so good. Then each user went to the original GUI window and pushed a button. To the surprise of those watching, the action all happened in the second GUI from that moment forward, regardless of which GUI's button was pressed. What was happening?

The users each wrote GUIs using code with this sort of code structure.

function myGUI
 
global hLines hColor
hfig = figure('name','Changing line colors');
hax = axes('parent',hfig);
init();
 
    function init(varargin)
        hLines = plot(hax,magic(3));
        hColor = uicontrol('style','pushbutton',...
            'string', 'Change line colors', ...
            'units','normalized',...
            'position',[0.01 0.01 0.25 0.05],...
            'callback',@changeColor);
    end
 
    function changeColor(varargin)
        colors = {rand(1,3); rand(1,3); rand(1,3)};
        set(hLines, {'Color'}, colors);
    end
end

Notice that some of the handles are declared to be global. Both the init and myStop functions need access to this information, but the main function myGUI doesn't know about them. However, declaring them global (persistent would have the same effect here since there's only one file) is what's causing the first and second instances to both point to the line and uicontrol handles in the second figure, causing incorrect interactions.

How can you stop that from happening? Two ways:
  1. Instead of declaring the shared variables global, simply initialize them. Replace:
    global hLines hColor
    
    with
    hLines = []; hColor = [];
    
  2. Here's a solution that is perhaps even more elegant. Since init only gets called once, don't make it a nested function and instead include the code directly in myGUI. Since that code runs when myGUI is created, the variables in question are automatically initialized to their correct values and are now visible to the changeColor nested function. The code is now slightly shorter, possibly even more readable, and doesn't have unpleasant side effects.
function myGUIclean
 
% initialize GUI
hfig = figure('name','Changing line colors');
hax = axes('parent',hfig);
hLines = plot(hax,magic(3));
hColor = uicontrol('style','pushbutton','units','normalized',...
    'position',[0.01 0.01 0.25 0.05],'string','Change line colors',...
    'callback',@changeColor);
 
% callback for pushbutton
    function changeColor(varargin)
        colors = {rand(1,3); rand(1,3); rand(1,3)};
        set(hLines, {'Color'}, colors);
    end
end

For a more complex GUI example, see The MathWorks News & Notes - January 2006 article and find the code on the MATLAB File Exchange.


  • print