Reputation: 4650
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 imshow
s? BTW, my matlab version is R2009a.
Upvotes: 3
Views: 2850
Reputation: 1860
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
Upvotes: 0
Reputation: 38032
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.
Upvotes: 2