Functions in gui script not seeing handles struct

2 views (last 30 days)
Hi,
I have produced a gui using GUIDE in which I add several fields to the handles struct for use later on (within the gui script).
I had two pieces of code that I stored in separate scripts (.m files) and these were called by several of the callbacks within the gui script. To call each of the separate scripts I just wrote the name of the script with parenthesis on the end:
function pitch_bounce_input_gui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to pitch_bounce_input_gui (see VARARGIN)
% Choose default command line output for pitch_bounce_input_gui
handles.output = hObject; % not required?
% Set handles fields with initial values from UIcontrols
handles.mass = str2double(get(handles.editMassNom,'String'));
% FURTHER CODE REMOVED FOR BREVITY
% Update handles structure
guidata(hObject, handles);
% Calculate characteristic values and update gui
calc_characteristics();
update_gui();
This worked perfectly - noting that I did not need to pass anything (i.e. handles) to the scripts for them to work. I'm not experienced enough to know why this is but was happy that it worked.
I have now chosen to incorporate those separate scripts as functions within the gui script file and now they don't work. The functions are written as:
%%A function to calculate the characteristic values
function calc_characteristics()
cgx =((((handles.frload/100)*handles.mass)/handles.mass)*handles.wb)-(handles.wb/2);
ryy = sqrt(handles.pinertia/handles.mass);
a = (1-(handles.frload/100))*handles.wb;
b = handles.wb-a;
alpha = (2*handles.frrate+2*handles.rrrate)/handles.mass;
beta = (2*handles.rrrate*b-2*handles.frrate*a)/handles.mass;
gamma = (2*handles.frrate*(a^2)+2*handles.rrrate*(b^2))/(handles.mass*ryy^2);
handles.bfreq = sqrt(((alpha+gamma)/2)-sqrt(((alpha-gamma)^2)/4+(beta^2/ryy^2)))/(2*pi);
% FURTHER CODE REMOVED FOR BREVITY
% pass back characteristic values
guidata(handles.bounce_pitch_fig, handles);
My first thought was that for some reason I know needed to pass handles to the functions, calling them as follows:
calc_characteristics(handles);
update_gui(handles);
And receiving them as:
function calc_characteristics(handles)
function update_gui(handles)
This worked for the first call but not the second. When I broke the script and continued line by line it was apparent that the contents of handles was growing correctly during the first function (to 116 fields) and then when the function ended and passed back to OpeningFcn the new content of handles was lost (back down to 98 fields!). This is despite using guidata to endure the content of handles was retained.
Why do I know need to pass handles explicitly when the function would seem to be closer to the gui script scope than it was previously? And why is it not storing the output of the first function correctly?
Also, as in a previous post of mine, the gui flashes up when it is called:
pitch_bounce_input_gui;
and then instantly disappears before getting to the first line of OpeningFcn.
These two problems are presumably related.
Thanks,
Simon.

Accepted Answer

Walter Roberson
Walter Roberson on 29 Oct 2018
calc_characteristics(handles);
handles = guidata(hObject) ;
update_gui(handles);
Remember that when you update handles inside the calc_characteristics function, you are not updating the copy that lives in the workspace of pitch_bounce_gui_data
  4 Comments
Walter Roberson
Walter Roberson on 30 Oct 2018
The way that GUIDE uses the handles structure is that there is a "master" version of the structure that lives as Application Data https://www.mathworks.com/help/matlab/ref/setappdata.html named UsedByGUIData_m that is attached to the figure.
guidata() is a convenience function that takes as input the handle to any object within the figure, and uses that handle to locate the application data; if there is a second input to guidata, then it completely overwrites that application data.
When GUIDE constructs the various callback properties associated with a graphics object, it does not call the user-provided routine directly. Instead, it uses guidata() on the handle of the graphics object and passes that as a third parameter to the function. MATLAB itself only passes two parameters to callback functions, and GUIDE is adding on another one. Because it uses guidata(), then at each point that a callback is invoked by this mechanism, it is a copy of the most recent master version of the application data that gets passed to the callback.
What is received by the callback is just a struct, not any kind of fancy object. Because it is a plain struct, within the callback, MATLAB follows the regular rules for variables passed in, which is to say that it is always a local copy of the variable that is updated if the value of the variable is changed.
Therefore, if the callback creates new fields in the handles structure, it is doing so in the local copy of the handles structure. Likewise, if it updates a field, it is updating the local copy of the handles structure. However, if the callback sets properties of a graphics object it located by referring to the handles structure, then because graphics objects are derived from the handle class, it is always the property of the "real" graphics object that are updated, rather than the contents of the handles structure.
If the callback wants to update the master copy of the handles structure, then it needs to call guidata() (or, more obscurely, setappdata()) in order to update the master copy.
When the master copy is updated, that does not affect any local copies of the handles structure that might exist. Therefor if one function calls a second and the second updates the master copy of the handles structure, then if the first function needs to know about the updates, then it needs to fetch the handles structure again.
MATLAB cannot tell the basic situation apart from the following:
function driver
X = 7;
double(X);
square(X);
log9(X);
function double(X)
X = X * 2;
function square(X)
X = X .^2 ;
function log9(querty)
querty = log(querty)./log(9);
When you call double(X), the content of X is passed by value, and any changes that double() make to X are contained locally within double(), with X not being updated in the calling function driver. Likewise, when square(X) is called in driver, X has not been updated in the workspace of driver, so the 7 that is still in X is what is passed to square(), not the 7*2 = 14 that lived for a moment inside of double(). Same with the log9(X) call: X has not been updated within driver so it is still the 7 that is passed to log9(), not (7*2).^2 . I show the log9() call to emphasize that the name of the parameter the value is received into does not matter to MATLAB (as long as it is not one of the reserved names): the log9 function in no way has any different action for not having a receiving variable name matching the name of the variable that was passed in.
Now, since your calc_characteristics function is not being used as a callback, another way you could have written it would have been to define
function handles = calc_characteristics(handles)
and not call guidata() inside calc_characteristics, with the caller function using
handles = calc_characteristics(handles);
update_gui(handles);
Then any changes made to the local handles structure inside calc_characteristics would be returned and assigned into the handles variable, so after the call, handles would be the updated version (but the master copy would not yet have been updated.)
Simon Aldworth
Simon Aldworth on 31 Oct 2018
That's terrific Walter, many thanks indeed for taking the time to explain that.
Regards.

Sign in to comment.

More Answers (0)

Categories

Find more on Environment and Settings in Help Center and File Exchange

Products


Release

R2017b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!