Reputation: 196
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
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)
end
, then add it to all functions defined, except the very first function (whose name is on the m-file)end
at the very bottom of your m-fileAfter 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
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
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