Make MATLAB accept KeyPress only once - matlab

I am currently trying to write an experiment in MATLAB. As part of it, it should accept and record a key response, either 1 or 0. The problem is that I only need the keys to be recorded at one specific time slot, and to be ignored in the other parts of the experiment. The response has to be recorded along with the time it took to respond, and it should be done once only, the earliest (so that once the user has pressed a key, the program does not record the subsequent ones).
So far, I have attempted numerous ways. These are probably nooby workarounds, but I am not good with object oriented programming.
One option is to use
set(gcf,'KeyPressFcn',#KeyDownListener)
where KeyDownListener is
function KeyUpListener(key_hand, key_obj, starting_time)
toc(starting_time)
key_pressed = key_obj.Key; return; end
However, there are two problems: 1) I am struggling trying to get the value from this function back to the calling script; 2) Once MATLAB reads this set(...) piece of code, it keeps capturing every single key pressed. So, basically, if there are 100 trials (each consisting of, say, 5 stages, of which the keypress should only be accepted at stage 4) in the experiment put in a loop, the set(...) will be ignored at the first run at stages 1-3 before it first appears, but will then be present in all the runs from the second, at every stage, 1-5.
Then I tried to put both the calling script and the called function into another function called from an outer script, so that once the control is returned to that higher level script, I put in another
set(gcf,'KeyPressFcn',#mute)
by which mute function performs no actions whatsoever. This seems to work for problem No 2, but it still does not allow me to obtain the value for the keypress callback, and, as I am using pause(..) to allow the user time for response, it does not interrupt with the first key pressed, it waits for the entire time allocated in the brackets for pause.

Passing variable around:
I recommend using setappdata and getappdata to pass variables around between callbacks of a GUI.
You can also read Share Data Among Callbacks for more informations about this aspect.
Assigning and disabling callbacks:
To disable a callback function, you do not need to re-assign the callback to a function that does nothing, you can simply assign an empty array:
% assign the 'KeyDownListener' function and pass one parameter ('var1') with it
set( h.fig, 'KeyPressFcn',{#KeyDownListener,var1})
% then later when you don't need it anymore:
% Disable the 'KeyPressFcn listener, assign 'empty' to it
set( h.fig, 'KeyPressFcn',[])
Full example:
Below is a minimal GUI which demonstrate how to capture a single key press (and the time), then save the data collected into the appdata space of the application, where they are collected again by a 'display' function (where you could also do whatever you want with the data collected).
code for SingleKeyPressDemo.m:
function h = SingleKeyPressDemo
% create a minimal figure with a button and 2 text fields
h.fig = figure ;
h.btn = uicontrol('Style','Pushbutton','String','Start capture',...
'units','Normalized','Position',[0.1 0.6 0.8 0.3],...
'Callback',#StartKeyCapture) ;
h.txtKey = uicontrol('Style','text','String','Key pressed:',...
'units','Normalized','Position',[0.1 0.4 0.8 0.1]) ;
h.txtTime = uicontrol('Style','text','String','Elapsed time:',...
'units','Normalized','Position',[0.1 0.3 0.8 0.1]) ;
% assign a callback to the KeyRelease event to intercept passing the
% eventdata to the command window
set(h.fig,'KeyReleaseFcn',#KeyReleased)
% initialise appdata variables to hold the captured key and the time
setappdata( h.fig , 'CapturedKey' , [] )
setappdata( h.fig , 'Elapsed_time' , [] )
% save the handle structure
guidata( h.fig , h)
function StartKeyCapture(hobj,~)
h = guidata( hobj ) ; % retrieve the handle structure
StartTime = tic ; % Start a stopwatch
% assigne the callback funtion, passing the stopwatch in parameter
set(h.fig,'KeyPressFcn',{#KeyDownListener,StartTime})
% disable the button until a key is pressed (also makes it loose the
% focus, which is handy otherwise the button keeps the focus and hides
% the 'KeyPressedFcn'
set( h.btn , 'Enable','off')
function KeyDownListener(hobj, key_obj, starting_time)
% Detect key pressed and time elapsed
Elapsed_time = toc(starting_time) ;
key_pressed = key_obj.Key;
h = guidata( hobj ) ; % retrieve the handle structure
setappdata( h.fig , 'CapturedKey' , key_pressed ) ; % save the captured key
setappdata( h.fig , 'Elapsed_time' , Elapsed_time ) ; % save the elapsed time
set(h.fig,'KeyPressFcn',[]) % remove listener so new key press will not trigger execution
set( h.btn , 'Enable','on') % re-enable the button for a new experiment
% (optional) do something after a key was pressed
display_results(h.fig) ;
function display_results(hobj)
h = guidata( hobj ) ; % retrieve the handle structure
% retrieve the saved data
CapturedKey = getappdata( h.fig , 'CapturedKey' ) ;
Elapsed_time = getappdata( h.fig , 'Elapsed_time' ) ;
% update display
set( h.txtKey , 'String', sprintf('Key pressed: %s',CapturedKey) ) ;
set( h.txtTime , 'String', sprintf('Elapsed time: %f ms',Elapsed_time) ) ;
function KeyReleased(~,~)
% do nothing
% The only purpose of this function is to intercept the KeyReleased event
% so it won't be automatically passed on to the command window.
Creating GUI programmatically is full of verbose in Matlab, focus on the code in the 2 functions StartKeyCapture and KeyDownListener to achieve what you were asking.
This example will produce the following GUI:
Additional note:
I would also recommend against the use of gcf in a GUI application. It is a handy shortcut when debugging or working in the console with figures opened around, but in a GUI the calls to its own elements should be as self-contained as possible. MATLAB offers way to save the handles of all your GUI elements (all the uicontrols, including the main figure), so you can call them explicitely when you need them. This reduces the risk of errors (imagine your user is also running other figures in the same MATLAB session, if your callback triggers and call gcf when the user was fiddling with another figure, you are going to try to execute code on a different figure than it was intended to ... which can easily result in errors).
Read the documentation on guidata for more information (and/or observe how I've used it in the example above).
Edit:
To avoid the focus coming back to the command window at every key pressed, you also have to also define a callback function to the corresponding KeyRelease event, otherwise the event will automatically be forwarded to the command window (which will take the focus).
A clean way to do is to simply add callback assignment
set(h.fig,'KeyReleaseFcn',#KeyReleased)
once and for all in the figure definition (you don't have to set/undo this callback at each experiment), and then define an empty function function KeyReleased(~,~) which does nothing. This way is implemented in the modified code example above.
Another way to do without the extra empty function, would be to simply define the callback in line at the assignment time:
set(h.fig,'KeyReleaseFcn','disp([])')
This way you do not need to have an empty KeyReleased function.
Note however that a callback function must be defined (either inline or later in the code). Simply assigning empty to the callback will not work. (e.g. both options below will fail to intercept the event and will forward it to the command window:
set(h.fig,'KeyReleaseFcn','') % not working
set(h.fig,'KeyReleaseFcn',[]) % not working either

Related

Close uialert figure by button click

Based on a MATLAB example, I added a close command in order to close the uifigure after pressing the OK button. However, in its current state the figure is closed automatically, rather than on the button click. How can I alter the below to have the figure close on click?
Code:
fig = uifigure;
message = {'Fire hazard!','Consider reducing temperature.'};
uialert(fig,message,'Warning',...
'Icon','warning');
close(fig)
You should use a callback in uialert:
fig = uifigure;
message = {'Fire hazard!','Consider reducing temperature.'};
uialert(fig, message, 'Warning', 'Icon', 'warning', ...
'CloseFcn', #(~, ~)close(fig)); % This will be executed after Ok is pressed
The syntax # is the Matlab way to define an anonymous function (Matlab calls these function handles, other languages usually call these lambdas). It allows passing functions as parameters to other functions. If your anonymous function needs to receive parameters, the syntax #(p1, p2, p3) can be used.
In the case of callbacks for uifigures, the callbacks always expect two parameters: fig and event. fig is the figure where the event happened, event is a structure describing the event. In the case above, since all you want to do is close the figure (and you already know which figure you want to close), you can ignore both parameters. The syntax in Matlab to tell that you are receiving a parameter that you purposefully are ignoring is by using a tilde (~) in the position of the parameter. This can be used anywhere actually, including in the definition of normal functions or when unpacking the return value. For example:
[U,~,V] = svd(A)
tells that you are not interested in the singular values of your SVD, only in left and right singular vectors.
You can read more about function handles in the Matlab documentation.

live updating edit field when slider value is changing

I have a slider and a edit field, which are defined as
uicontrol(fig,'Style','Slider','Units','characters','Position',[17.1+f*iwidth 10.5 4 28.6],'Min',0,'Max',1000,'Value',500,'SliderStep', [1/500 , 10/500 ],'Callback','evaluation_callbacks(''results'',guidata(gcbo))','Tag',['slider' int2str(f)]);
uicontrol(fig,'Style','Edit','Enable','inactive','Units','characters','Position',[14+f*iwidth 8.2 9 1.6],'FontSize',10,'String',500,'Tag',['rating' int2str(f)]);
With the following Callback functions:
function evaluation_callbacks(varargin)
% EVALUATION_CALLBACKS Callback functions for the evaluation interface
%
% evaluation_callbacks(fname,varargin) executes the callback function
% fname_callback with various parameters
fname=[varargin{1},'_callback'];
feval(fname,varargin{2:end});
%%%saving the rating results and proceeding to the next experiment or exiting
function results_callback(handles)
% stop audio
clear sound
%getting the ratings for all files
for f=1:handles.nbfile
handles.ratings(handles.expe_order(handles.expe),handles.file_order(f))=get(getfield(handles,['slider' int2str(f)]),'Value');
end
%saving the whole results (to avoid losing data if the program terminates early)
results='';
fid=fopen(handles.resultfile,'w');
for e=1:handles.nbexpe
for f=1:handles.nbfile
fprintf(fid,'%d\n',handles.ratings(e,f));
end
end
fclose(fid);
if handles.expe<handles.nbexpe
handles.expe=handles.expe+1;
% updating title
set(handles.experiment,'String',handles.parameter{handles.expe});
% update evaluation parameters
set(handles.scale90,'String',handles.high{handles.expe});
set(handles.scale10,'String',handles.low{handles.expe});
if handles.expe==handles.nbexpe
pos=get(handles.results,'Position');
pos(1)=pos(1)+2.5;
pos(3)=19;
set(handles.results,'Position',pos,'String','Save and exit');
end
%moving all the sliders back to 50
for f=1:handles.nbfile
shandle=getfield(handles,['slider' int2str(f)]);
set(shandle,'Value',500);
rhandle=getfield(handles,['rating' int2str(f)]);
set(rhandle,'String',500);
end
%randomizing the order of the tested files for the next experiment
handles.file_order=randperm(handles.nbfile);
%testing whether a break is needed before the next experiment
if etime(clock,handles.time) > 20*60
wfig=warndlg(['You have been working for ' int2str(round(etime(clock,handles.time)/60)) 'minutes. It is recommended that you take a break of at least the same duration before starting the next experiment. Click on OK when you are ready.'],'Warning');
uiwait(wfig);
end
handles.time=clock;
% Start next audio sample
play_callback(handles,1)
guidata(gcbf,handles);
else
%exiting
close(gcbf);
end
%%%rounding and displaying the values of the sliders
function slider_callback(handles,f)
shandle=getfield(handles,['slider' int2str(f)]);
set(shandle,'Value',round(get(shandle,'Value')));
rhandle=getfield(handles,['rating' int2str(f)]);
set(rhandle,'String',get(shandle,'Value'));
As it is right now, the edit field value are not live updated if. The value of the edit field are only updated if the slider is moved and the mouse button is released.
I have tried to add a listener to the slider, but I cannot make it work. So far I have followed the following guides/posts without any luck.
http://undocumentedmatlab.com/blog/continuous-slider-callback
and
https://se.mathworks.com/matlabcentral/answers/140706-use-slider-for-live-updating-of-threshold-on-2d-contour-plot
Can anybody help?
Add a listener in the myGUI_OpeningFcn. Note you need to change myGUI to match your GUI script filename.
addlistener(handles.slider1,'ContinuousValueChange',#(hObject,eventdata)myGUI('updateText',hObject,eventdata,guidata(hObject)));
Then, add the following function in your GUI script.
function updateText(hObject, eventdata, handles)
set(handles.text2,'String',get(hObject,'Value'));
guidata(hObject,handles);
More specifically, in your case, you need to add a bunch of listeners:
for i=1:handles.nbfile
addlistener(getfield(handles,['slider' int2str(i)]),'ContinuousValueChange',#(hObject,eventdata)myGUI('updateText',hObject,eventdata,guidata(hObject)));
end
Then add:
function updateText(hObject, eventdata, handles)
f = hObject.Tag(7:end);
set(getfield(handles,['rating' f]),'String',get(hObject,'Value'));
guidata(hObject,handles);
Update
In your GUI script, add listeners in the OpeningFcn (which is the second function automatically generated by Matlab when your GUI was created):
for i=1:handles.nbfile
addlistener(getfield(handles,['slider' int2str(i)]),'ContinuousValueChange',#(hObject,eventdata)myCallBackFileName('updateText',hObject,eventdata,guidata(hObject)));
You will need to change myCallBackFileName to match the filename of your callback script. Then add the updateText function.

Duplicating Command window of matlab into customed GUI [duplicate]

i am using GUI to call a terminal command. By using dos(my_command, '-echo') i can get the command output in Matlab's Command Window. Is there anyway to replicate that -echo in a static text in my GUI?
Currently, with my_command, I write the output to a log file, and update the static text's String by this log file after process finishes, but what I want is a live-view like in Command Window: output is displayed line by line in real-time. Thanks.
Update:
#Hoki: another question is: when the console commands are being executed, if I close the GUI then Matlab returns errors unstopable, how can I freeze the whole GUI until the commands are finished?
Thanks to Yair Altman info in this article, I got something to work but it involves hacking into Matlab Java base objects (namely the command window).
It involves attaching a listener to the Matlab command window. Now be careful, save your work often and be prepared to kill Matlab process quite a few times until you get it right ... because every time you have an error in the code you are stuck in a kind of infinite loop (the error/warning is sent to the command window, which triggers your listener, which re-trigger the error etc ...). I had to restart Matlab a good dozen of times just to get the example below to work in a stable way.
That's also the reason why I only attach the listener temporarily. just before sending the dos command and I remove the listener directly after. You can leave the listener permanently or adjust that to your needs but remember the advice from just above. Also consider that the command window can hold a massive amount of character, which you may not want all in your textbox, so you have to manage the text that you get from it (take a subset as in the example), and consider if you want to append or simply refresh the text in the textbox.
The example below seems to be stable as it is, any modification at your own risks ;-)
After request in comment I added 3 functions:
An onCleanup. This is a Matlab functionality to allow last resort action in case something goes wrong (a kind of "catch all" mechanism). Heavily recommended for this kind of program using undocumented functions.
A myCloseRequestFcn which intercept the closing action of the window to remove the listener and avoid error loops.
A scroll_to_bottom function. This one allows to move the text box caret to the end of the text (= to scroll to the bottom in case there is more text than visible space).
Warning: The last functionality could deserve a separate question and again call for undocumented functionality (so the compatibility is never guaranteed). To be able to implement it you need to have the findjobj function available in your Matlab path. If you do not want to download external component, then delete/comment the part of code that uses it and the subfunction scroll_to_bottom (and forget about scrolling the text box, there is no way to do that in pure Matlab). Or you can pick the previous version of the code by looking at the edit history of the post.
function h = gui_MirrorCmdWindow
%% // just to remove the listener in case something goes wrong
closeup = onCleanup(#() cleanup);
%% // create figure and uicontrol
h.f = figure;
h.txtOut = uicontrol(h.f,'Style','edit','Max',30,'Min',0,...
'HorizontalAlignment','left',...
'FontName','FixedWidth',...
'Units','Normalized',...
'Enable','On',...
'Position',[.05 .2 .9 .75]);
h.btnPing = uicontrol(h.f,'Style','Pushbutton',...
'String','Ping',...
'Units','Normalized',...
'Position',[.05 .05 .9 .1],...
'Callback',#btnPing_callback);
guidata(h.f,h)
%// intercept close request function to cleanup before close
set(gcf,'CloseRequestFcn',#myCloseRequestFcn)
%% // Get the handle of the Matlab control window
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jCmdWin = jDesktop.getClient('Command Window');
jTextArea = jCmdWin.getComponent(0).getViewport.getView;
%% // Get the handle of the jave edit box panel component
jtxtBox = findjobj(h.txtOut) ;
jTxtPane = jtxtBox.getComponent(0).getComponent(0) ;
%// Save these handles
setappdata( h.f , 'jTextArea', jTextArea ) ;
setappdata( h.f , 'jTxtPane', jTxtPane ) ;
function btnPing_callback(hobj,~)
h = guidata(hobj) ;
jTextArea = getappdata( h.f , 'jTextArea' ) ;
my_command = 'ping google.com -n 10' ;
startPos = jTextArea.getCaretPosition ;
set(jTextArea,'CaretUpdateCallback',{#commandWindowMirror,h.f,startPos}) ;
dos( my_command , '-echo' ) ;
pause(1) %// just to make sure we catch the last ECHO before we kill the callback
set(jTextArea,'CaretUpdateCallback',[]) ;
scroll_to_bottom(h.f)
function commandWindowMirror(~,~,hf,startPos)
h = guidata(hf) ;
jTextArea = getappdata( h.f , 'jTextArea' ) ;
%// retrieve the text since the start position
txtLength = jTextArea.getCaretPosition-startPos ;
if txtLength > 0 %// in case a smart bugger pulled a 'clc' between calls
cwText = char(jTextArea.getText(startPos-1,txtLength) ) ;
end
%// display it in the gui textbox
set( h.txtOut, 'String',cwText ) ;
scroll_to_bottom(h.f)
function scroll_to_bottom(hf)
%// place caret at the end of the texbox (=scroll to bottom)
jTxtPane = getappdata( hf , 'jTxtPane' ) ;
jTxtPane.setCaretPosition(jTxtPane.getDocument.getLength)
function myCloseRequestFcn(hobj,~)
cleanup ; %// make sure we remove the listener
delete(hobj) ; %// delete the figure
function cleanup
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jCmdWin = jDesktop.getClient('Command Window');
jTextArea = jCmdWin.getComponent(0).getViewport.getView;
set(jTextArea,'CaretUpdateCallback',[]) ;
Matlab does not offer a native way to fetch results continuously during the execution of dos, unix and system. Nevertheless there is a possibility to achieve the desired behaviour.
This is the proposed work-around:
Execute your command and pipe the output to a file.
Use the & character to continue execution of the Matlab-code.
Read the contents of this file continuously and update the GUI accordingly.
Introduce a second temporary file indicating the end of the command to stop the update-process.
Here is the code:
% create figure and uicontrol
fh = figure;
txtbox = uicontrol(fh,'Style','edit','Max',30,'Min',0,...
'HorizontalAlignment','left',...
'FontName','FixedWidth',...
'Position',[30 30 450 200]);
% store current path
path = cd;
% delete token to make sure the loop continues as expected
warnstate = warning('off','MATLAB:DELETE:FileNotFound');
delete(fullfile(path,'temp_cmdlog_done'));
warning(warnstate); % restore original warning state
% execute dos-command in background
cmd = 'ping -c 5 192.168.200.1';
dos([cmd,' > temp_cmdlog && echo > temp_cmdlog_done &']);
% refresh text in uicontrol until dos-command is done
while 1
out = fileread('temp_cmdlog');
set(txtbox,'String',out);
if exist(fullfile(path,'temp_cmdlog_done'),'file')
break;
end
pause(0.2); % adjust to suit your needs
end
% delete the temporary files
delete(fullfile(path,'temp_cmdlog'));
delete(fullfile(path,'temp_cmdlog_done'));
% indicate that process finished
set(txtbox,'ForegroundColor',[0,0.5,0]);

Showing data on Matlab GUI which is continuously being updated in a separate Matlab function

I have a function in Matlab which is getting continuous sensor values from a hardware. It gives a flag when new values are available and we can update the variables holding these values. Following is a dummy function to mimic what this function is doing.
function example( )
% Example function to describe functionality of NatNetOptiTrack
% Hardware initialization,
% Retriving real time information continuously
for i = 1:100 %in real time this loop runs for ever
data = rand(3,6);
% Send the updated data to gui in each iteration
end
end
i have made a gui using guide as shown in the figure:
So the data to be displayed is a 3x6 matrix with columns corresponding to X Y Z Roll Pitch and Yaw values while rows correspond to Objects.
I want to show the continuously updated values from this function on the gui. Is there a way i can initialize gui inside my example function and update the output value by using the handles inside my loop. I tried copying the gui code inside the example function as a script, it was able to initialize but was not recognizing the handles.
Also i want to show the current values on command window when i press the button.
Thanks
If you launch the GUI and then run the function, you should be able to get the handles to the controls on the GUI provided that you make the GUI figure handle visible and set its tag/name to something appropriate. In GUIDE, open the Property Inspector for the GUI and set the HandleVisibility property to on, and the Tag property to MyGui (or some other name). Then in your example.m file do the following
function example( )
% Example function to describe functionality of NatNetOptiTrack
% get the handle of the GUI
hGui = findobj('Tag','MyGui');
if ~isempty(hGui)
% get the handles to the controls of the GUI
handles = guidata(hGui);
else
handles = [];
end
% Hardware initialization,
% Retriving real time information continuously
for i = 1:100 %in real time this loop runs for ever
data = rand(3,6);
% update the GUI controls
if ~isempty(handles)
% update the controls
% set(handles.yaw,…);
% etc.
end
% make sure that the GUI is refreshed with new content
drawnow();
end
end
An alternative is to copy the example function code into your GUI - the hardware initializations could occur in the _OpeningFcn of your GUI and you could create a (periodic) timer to communicate with the hardware and get the data to display on the GUI.
As for displaying the current data when pressing the GUI button, you can easily do this by writing the contents of the GUI controls to the command line/window with fprintf. You will though need to make your example function interruptible so that the push button can interrupt that continuously running loop. You can do this by either adding a pause call (for a certain number of milliseconds) that gets executed at the end of each iteration of your loop, or just make use of the drawnow call from above (that is why I placed it outside of the if statement - so that it will be called on each iteration of your loop.
Try the above and see what happens!

How to prevent a callback from being interrupted by a figure's close function?

Was wondering how the question in the title can be achieved. I have some callbacks that run from button presses. These callbacks, if interrupted by the figure close, will cause errors because the function seems to run, then gets interrupted by the close function which closes the figure, and then the callback seems to resume after the figure is closed.
If I set the button's 'Interruptible' property to 'on', it prevents other callbacks from interrupting it, but does not seem to work for the figure's close function. Another idea I had was to specify the 'closefunction' in the figuring's 'CloseRequestFcn' callback and then call drawnow to flush the eventqueue before deleting the figure but this did not work.
The last resort for me is to simply set the figure's 'CloseRequestFcn' to '' when running callbacks but this seems tedious. Is there a standard solution to accomplish this?
EDIT:
From matlab's documentation:
Note If the interrupting callback is a DeleteFcn or CreateFcn
callback or a figure's CloseRequest or ResizeFcn callback, it
interrupts an executing callback regardless of the value of that
object's Interruptible property. The interrupting callback starts
execution at the next drawnow, figure, getframe, pause, or waitfor
statement. A figure's WindowButtonDownFcn callback routine, or an
object's ButtonDownFcn or Callback routine are processed according to
the rules described above.
So it appears to be the case that the interruptible property doesn't effect the close function.
EDIT 2:
Ok, so I think I found a problem. It's really bizarre. I actually discovered from the matlab documentation that callbacks are only interruptible if they have the interruptible property set to on AND :
If there is a drawnow, figure, getframe, waitfor, or pause command in the running callback, then MATLAB executes the interrupting callbacks which are already in the queue and returns to finish execution of the current callback.
I don't use any of these functions explicitly, so it turns out most of my callbacks aren't interruptible by the closereqfcn. BUT, it turns out some are, and the reasons why seem very strange. If have a callback with:
`large computation -> imshow -> imshow
large computation -> set -> set -> set -> set
where the set command is setting the axes visible property to off, then no interruption seems to occur if I exit during the callback
Now, if I have:
large computation -> imshow -> set -> imshow -> set
matlab issues an error if I exit during the callback on the second set command. Also, if I have:
large computation -> imshow -> imshow -> set
matlab issues an error if I exit during the callback on the first set command.
large computation -> imshow -> imshow -> imshow
also issues an error on the third imshow if I cancel during the callback.
For some reason it seems that two successive calls to imshow makes my callback interruptible. Is it possible matlab implicitly calls drawnow or does something weird if you use multiple imshows? BTW, my matlab version is R2009a.
I never really trusted that Interruptible flag (or comparable mechanisms)...I immediately admit I have never used it a lot, but that was because when I was experimenting with it for the first time, I noticed that 'Interruptible', 'off' (and friends) seemed to have more exceptions to the rule than vindications of it -- headache material alert!
So, I got in the habit of tackling this sort of problem simply by using flags, and wrapping all callbacks that must really be uninterruptible in a locking/releasing function.
Something like this:
% Define a button
uicontrol(...
'style', 'pushbutton',...
'interruptible', 'off',... % Nice, but doesn't catch DeleteFcn, CreateFcn, ...
% CloseRequestFcn or ResizeFcn
% ...
% further definition of button
% ...
% Put callback in a wrapper:
'callback', #(src,evt) uninterruptibleCallback(#buttonCallback, src,evt)...
);
where uninterruptibleCallback() looks something like this:
function varargout = uninterruptibleCallback(callback, varargin)
% only execute callback when 'idle'
% (you can omit this if you don't want such strict ordering of callbacks)
while ~strcmp( get(mainFigure, 'userData'), 'idle' )
pause(0.01);
% ...or some other action you desire
end
% LOCK
set(mainFigure, 'userData', 'busy');
try
% call the "real" callback
[varargout{:}] = callback(varargin{:});
% UNLOCK
set(mainFigure, 'userData', 'idle');
catch ME
% UNLOCK
set(mainFigure, 'userData', 'idle');
throw(ME);
end
end
Which allows you to use this closeReqFcn() for your figure:
function closeReqFcn(~,~)
% only when the currently running locked callback (if any) has finished
while ~strcmp( get(mainFigure, 'userData'), 'idle' )
pause(0.01);
% ...or some other action you desire
end
% ...
% further clean-up tasks
% ...
% deletion
delete(mainFigure);
end
Theoretically, when you put all callbacks in this sort of schema, it is basically equal to managing your own event queue.
This of course has a few advantages, but many, many drawbacks -- you might want to think this through for a bit. This whole mechanism might be unacceptably slow for your use case, or you might need to define a few more locking functions with far more specific behavior.
In any case, I suspect it's a good place to start off from.
An alternative to #Rody Oldenhuis's solution is to start a timer inside the CloseRequestFcn to close the figure when no uninterruptible code is in progress (which could be indicated by a flag; Closing_Allowed).
function mainFig_CloseRequestFcn(hObject, eventdata, handles)
Time = 3; % Wait time before force killing (in sec)
Kill.tmr = timer('executionMode', 'fixedRate',...
'Period', 1/10,...
'TimerFcn', {#KillingTimer_Callback, handles});
Kill.counts = ceil(Time/Kill.tmr.Period);
setappdata(handles.mainFig,'Kill',Kill);
start(Kill.tmr);
function KillingTimer_Callback(hObject, eventdata, handles)
Kill = getappdata(handles.mainFig,'Kill');
Kill.counts = Kill.counts - 1; % Count down
setappdata(handles.mainFig,'Kill',Kill);
if Kill.counts == 0 || getappdata(handles.mainFig, 'Closing_Allowed')
stop(Kill.tmr);
delete(handles.mainFig);
end
if Kill.counts == 0 means time out, and closes the figure even if an uninterruptible task is in progress, which then would result in the same errors you get sometimes now, but if you know the maximum amount of time you need to finish the uninterruptible jobs, then you can set the Time above properly.
Finally wrap the uninterruptible code by setting the Closing_Allowed flag.
function pushbutton_Callback(hObject, eventdata, handles)
setappdata(handles.mainFig, 'Closing_Allowed', 0); % Closing is not allowed
pause(2);
setappdata(handles.mainFig, 'Closing_Allowed', 1); % Closing is allowed