CloseRequestFcn of GUI not working with wrong current folder

I have in the GUI m-file a 'figure1_CloseRequestFcn'. This works fine as long the current Matlab folder is not changed.
When I run another file from another folder, I cannot quit Matlab unless I make the GUI-folder to the current folder.
With the standard CloseRequestFcn in the GUI property I can allways quit Matlab, even if the current folder is not the GUI folder.
Is there a way to insert into the GUI-figure-property something like:
'try;MyGUI(''figure1_CloseRequestFcn'',hObject,eventdata,guidata(hObject));catch;closereq;end;' ?
I do not want to change anything like the search path in Matlab.

 Accepted Answer

You have coded figure1_CloseRequestFcn in a way that calls upon a function that you expect to be in the MATLAB path, but turns out not to be because you relied upon it being in the current directory instead of creating a specific MATLAB path entry for the appropriate directory.
What you can do, is in the OpenFcn for the GUI, use mfilename() to detect where the .m file for the GUI itself is located. fileparts() to get the directory, and cd to that directory, recording the old directory. Now take the handle of the function that you need in the close request function, and store it in handles; then cd back to the old directory.
Then in the close request function, pull the handle of the function out of the handles structure, and use it to call the needed function, instead of counting on the function being in the current directory. When you construct a handle of a function using @ then MATLAB remembers the directory the code is located in and so knows where to find it.

6 Comments

Thank you for your investigation.
It is not clear to me what I should do.
This is the m-file of my test for close request:
function varargout = TestCloseReq(varargin)
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @TestCloseReq_OpeningFcn, ...
'gui_OutputFcn', @TestCloseReq_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% --- Executes just before TestCloseReq is made visible.
function TestCloseReq_OpeningFcn(hObject, eventdata, handles, varargin)
p = mfilename('fullpath');
%This works with an external funtion CloseGUI in the same folder as this GUI:
%set(handles.figure1,'CloseRequestFcn',{@CloseGUI,handles});
handles.output = hObject;
guidata(hObject, handles);
function varargout = TestCloseReq_OutputFcn(hObject, eventdata, handles)
varargout{1} = handles.output;
function figure1_CloseRequestFcn(hObject, eventdata, handles)
disp('Closing')
delete(hObject);
When I create an external function 'CloseGUI' in the same folder as above GUI ,I can change the current folder and the GUI will close.
function CloseGUI(hobj,ev,handles)
disp('Close GUI')
delete(hobj)
How is the statement (uncommented in above GUI 'TestCloseReq')
set(handles.figure1,'CloseRequestFcn',{?????});
correct, if I want to use the 'function figure1_CloseRequestFcn(hObject, eventdata, handles)' inside the GUI?
Of course as long as the current folder does not change, following statement is ok:
set(handles.figure1,'CloseRequestFcn',...
{@(hObject,eventdata)TestCloseReq('figure1_CloseRequestFcn',hObject,eventdata,guidata(hObject))})
function TestCloseReq_OpeningFcn(hObject, eventdata, handles, varargin)
p = mfilename('fullpath');
[guipath,~] = fileparts(p);
olddir = cd(guipath);
set(handles.figure1,'CloseRequestFcn',{@CloseGUI,handles});
cd(olddir);
handles.output = hObject;
guidata(hObject, handles);
This ensures that the @CloseGUI is evaluated from the directory that TestCloseReq itself is stored in, even if TestCloseReq is invoked from a different directory.
Walter, thank you. I understand now your approach.
Is there a method to use the GUI-internal 'function figure1_CloseRequestFcn(hObject, eventdata, handles)' to get the same closing behavior?
When you set the CloseRequestFcn using function handle syntax, then all functions mentioned in the function handle are searched for at the time the function handle is constructed. So when you do
set(handles.figure1,'CloseRequestFcn',...
{@(hObject,eventdata)TestCloseReq('figure1_CloseRequestFcn',hObject,eventdata,guidata(hObject))})
then the @(hObject,eventdata)TestCloseReq('figure1_CloseRequestFcn',hObject,eventdata,guidata(hObject)) parameter is evaluated before the set() is done, and the reference to TestCloseReq would be noted and the file name for TestCloseReq would be written into the data structure for the anonymous function.
At the moment, it appears that TestCloseReq is the same as the name of your .m file that has the set() call in it, so it should be a plain reference to the .m that is executing, even if you started the GUI running while you are cd'd to a different directory. That part of it works like you would hope to ensure that you do not need to give it further hints about which directory to search, and to ensure that you do not need to cd to any particular directory before executing the callback.
However, at the time the function handle is being constructed, MATLAB has no idea what TestCloseReq is going to do with that quoted string 'figure1_CloseRequestFcn' that you have, and so does not do any kind of searching to find out where figure1_CloseRequestFcn is: the searching for figure1_CloseRequestFcn will not be done until run-time when the CloseRequestFcn is activated.
At the time that CloseRequestFcn is activated, the GUI .m file is invoked (possibly suspending an existing invocation of the .m file) It will look at the first parameter, see that it is not one of the standard strings, and then will str2func() it to turn it into a function handle. In order for that to work, it has to name an un-nested function inside the GUI's .m file, or else it has to be the name of an external function that is present on the MATLAB path. So if TestCloseReq has function figure1_CloseRequestFcn' inside it, then the str2func() will work fine and be able to call the function, and that will work even if you cd'd around, because the str2func() or @ constructor always looks inside the current .m file . Again, that works as you would hope; even though it is on-the-fly (and not exactly as robust as you might hope, in the nitty details), when you use @() naming the GUI's own function name and the quoted string is the name of a top-level function inside the GUI's .m file, the call will work out.
So what can go wrong with the sequence? Well, if the quoted string refers to a function that is not inside the .m because it got deleted, then you have a problem. Or if the quoted string refers to a function that is in the same directory as the GUI's .m file, then if you are not cd'd to that directory at the time the callback is triggered, then you have a problem. Or if the @() refers to the name of the function for the GUI itself, but the GUI .m file got renamed without going through the proper update sequence, then the TestCloseReq() would turn out to not be referring to the GUI's function TestCloseReq because in MATLAB the name of the first function of a function file is ignored and replaced with the name of the file . Pr if the @() refers to the name of an external function that was expected to be in the same directory as the GUI's .m file but you cd'd out of there, then the external function would not be found.
So...
  • the @(hObject,eventdata)TestCloseReq('string' form would most often work fine no matter which directory you were in
  • The @() some external function only works if the external function is on the MATLAB path at the time the function handle is constucted -- which could require cd'ing to the directory the GUI's .m file is in before doing the @ .
  • But you can have problems even with the @()TestCloseReq('string' form
Walter, thank you very much for your help! I understand now much more what is going on with '@'.
To make it clear for other users how I solved the problem, here my code:
function TestCloseReq_OpeningFcn(hObject, eventdata, handles, varargin)
set(handles.figure1,'CloseRequestFcn',{@CloseTestCloseReq,handles});
handles.output = hObject;
guidata(hObject, handles);
function CloseTestCloseReq(hObject, eventdata, handles)
disp('Closing')
delete(hObject);
Both functions are inside of the GUI m-file.
Now you can run the GUI, change the working directory and close the GUI figure.
Yup, that should work fine, even if the GUI's .m was invoked whent the user is cd'd to another directory.

Sign in to comment.

More Answers (0)

Categories

Community Treasure Hunt

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

Start Hunting!