user3548298
user3548298

Reputation: 196

Matlab - GUI - Modify a variable by changing it in an other callback

I want to make a simple program that displays an increasing number in a loop upon pressing a run button and change that value by pressing an other button while in the loop. The program I came up with using what I've found so far increases the number and displays it correctly, but the variables I use are apparently independant from each other so whenever I reset the value to 0, the loop continues where it was left before the reset.

% --- Executes on button press in stop.
function stop_Callback(hObject, eventdata, handles)
test = 0;
set(handles.display, 'String', num2str(test));
guidata(hObject, handles);

% --- Executes on button press in run.
function run_Callback(hObject, eventdata, handles)
test = 1;
while test > 0
    test = test + 1;
    set(handles.display, 'String', num2str(test));
    guidata(hObject, handles);
    pause(1);
end

Any idea how to make this test variable global, how to initialize it and where I should put it in the file ?

Upvotes: 2

Views: 265

Answers (3)

Stefan Karlsson
Stefan Karlsson

Reputation: 1085

if your project is small and fits into a single M-file, you can solve this most easily by using nested functions.

If you are making your GUI using guide, then you can do this most easily in the following way: (make sure you save a copy first of the auto-generated m-file from GUI editing)

  1. open the m-file
  2. If the functions are not ended with keyword end, then add it to all functions defined, except the very first function (whose name is on the m-file)
  3. put an extra end at the very bottom of your m-file

After this, any variables that you define in the top function will be available to the lower functions (the nested ones, who are also the callbacks). The standard Matlab editor will after this color highlight your variables differently if they have scope across many functions

Upvotes: 0

Martin J.H.
Martin J.H.

Reputation: 2205

Yes, this is expected behavior! The scope of the variable test is local to each function, so you cannot change it in one function and expect the changed value to appear in the other function.

There are a few options around this! For example, you can use the handles structure to pass around "global" variables. In your code, you would have to modify it like so:

% --- Executes on button press in stop.
function stop_Callback(hObject, eventdata, handles)
handles.test = 0;
set(handles.display, 'String', num2str(handles.test));
guidata(hObject, handles); % Store the changed handles structure

% --- Executes on button press in start.
function run_Callback(hObject, eventdata, handles)
handles.test = 1;
while handles.test > 0
    handles.test = handles.test + 1;
    set(handles.display, 'String', num2str(handles.test));
    guidata(hObject, handles); % stores the changed handles structure
    pause(1);
    handles = guidata(hObject); % updates "handles" to see the change!
end

This is a pretty standard approach, but it has a few drawbacks: It's super easy to accidentally omit updating or retrieving the handles structure. Furthermore, since the two functions run in parallel, they are susceptible to race conditions.

A second method is to use the global statement. It's inserted easily:

% --- Executes on button press in stop.
function stop_Callback(hObject, eventdata, handles)
global test;
test = 0;
set(handles.display, 'String', num2str(test));
guidata(hObject, handles);

% --- Executes on button press in run.
function run_Callback(hObject, eventdata, handles)
global test;
test = 1;
while test > 0
    test = test + 1;
    set(handles.display, 'String', num2str(test));
    guidata(hObject, handles);
    pause(1);
end

But this too comes with a few drawback: Now the variable test is truly global. It can be changed in other scripts, functions, or GUIs as well, so you should choose a more unique name than test, and be careful in general. Also, when the (singleton) GUI is restarted without closing it first, the GUI's visual state, the contents of the handles structure, and the contents of the global variables can become "out of sync". I got bitten by this twice, so I don't use this method anymore.

A third method embraces the fact that the variable test should always be linked to the GUI text field. It therefore uses the string in the display field in place of a variable. Essentially that means using get, set, str2double, and num2str a lot:

% --- Executes on button press in stop.
function stop_Callback(hObject, eventdata, handles)
set(handles.display, 'String', num2str(0));
guidata(hObject, handles);

% --- Executes on button press in start.
function run_Callback(hObject, eventdata, handles)
set(handles.display, 'String', num2str(1));
while str2double(get(handles.display, 'String')) > 0
    set(handles.display, 'String', ...
        num2str(str2double(get(handles.display, 'String')) + 1));
    guidata(hObject, handles);
    pause(1);
end

It's verbose, but it's what I use in these cases. It is the most robust solution, especially when restarting a (singleton) GUI without closing it first.

Upvotes: 1

fatih
fatih

Reputation: 1010

You can just declare your variable as global. This will do.

% --- Executes on button press in stop.
function stop_Callback(hObject, eventdata, handles)
global test;
test = 0;
set(handles.display, 'String', num2str(test));
guidata(hObject, handles);

% --- Executes on button press in run.
function run_Callback(hObject, eventdata, handles)
global test;
test = 1;
while test > 0
    test = test + 1;
    set(handles.display, 'String', num2str(test));
    guidata(hObject, handles);
    pause(1);
end

Upvotes: 0

Related Questions