Stop/Delete Timer Issues While Closing GUI

Hi,
I use a timer to update an axes in a GUI. I want to handle the case in which the user closes the window while the timer is still running, i.e., the axes still beeing updated.
The Problem:
However, every so often the execution time of the TimerFcn is longer than the timers period (depending on the pc's performance). This can cause an error when closing the GUI while the timer is still running.
Error while evaluating TimerFcn for timer 'timer-1'
Bad handle
FYI, I use
T = timerfind;
if ~isempty(T)
stop(T)
delete(T)
end
in the figure's delete function. Apparently, the TimerFcn is still being called after the execution of the figure's delete function. Hence, within my TimerFcn an error occurs during a call to
cla(myaxes),
for cla expects a valid figure handle (though the GUI's figure has just been deleted).
All in all, it seems that the execution time being larger than the timer period is causing this. Of course I could reduce the timer period, but this is not desirable. I figured out a workaround by using a try-catch phrase within the TimerFcn, however it's not a proper solution.
Question:
Is there any way to force the TimerFcn to stop and somehow flush its executin queue/event buffer, regardless of its current state? Any thoughts about solving this issue are very welcome!
Thank you in advance!
Hannes

 Accepted Answer

Here's my function. I call it in the figMainWindow_CloseRequestFcn() function which gets called when the user tried to close the GUI by any means. To make double sure, I also call in in the callback for a push button I have labeled "Exit", btnExit_Callback(). It looks pretty similar to yours, perhaps a little more robust. I never have any problem with it but I have my timer running at 20 calls per second to monitor a switch on a light booth.
%----------------------------------------------------------------------------
function StopTimer(handles)
try
fprintf('Entering StopTimer...\n');
listOfTimers = timerfindall % List all timers, just for info.
% Get handle to the one timer that we should have.
if isempty(listOfTimers)
% Exit if there is no timer to turn off.
fprintf('There are no timers to turn off. Leaving StopTimer().\n');
return;
end
handleToTimer = getappdata(handles.figMainWindow, 'timerObj');
% Stop that timer.
stop(handleToTimer);
% Delete all timers from memory.
listOfTimers = timerfindall
if ~isempty(listOfTimers)
delete(listOfTimers(:));
end
fprintf('Left StopTimer and turned off all timers.\n');
catch ME
errorMessage = sprintf('Error in StopTimer().\nThe error reported by MATLAB is:\n\n%s', ME.message);
fprintf('%s\n', errorMessage);
WarnUser(errorMessage);
end
return; % from btnStopTimer_Callback

3 Comments

Hannes
Hannes on 1 Sep 2014
Edited: Hannes on 1 Sep 2014
Thank you for your quick reply. Unfortunately it is not solving my problem. I implemented your solution and tried it, sadly the issue remains. The TimerFcn is still being called after the execution of the figures delete function, which is highly unwanted.
FYI the period of my timer is set to 25 fps.
I was wondering that I can not be the only one with this problem, since timers in GUIs are pretty common.
I don't know. You'll have to call the Mathworks. You say you have some headless, rogue timer running amok even after the timer has been stopped and the timer handle deleted. That's very weird. I have no answer for it.
Hannes
Hannes on 2 Sep 2014
Edited: Hannes on 3 Sep 2014
As I just figured out the probleme you have been right all along!
I was working with the GUI's DeleteFcn and not its CloseReqFcn! So I created a solution that includes another timer. See at below for my solution.

Sign in to comment.

More Answers (5)

Hannes
Hannes on 3 Sep 2014
Edited: Hannes on 3 Sep 2014
As mentioned above, thanks to Image Analyst and Sean de Wolski i figured out a solution to my problem. In addition to my earlier attempt it includes the use of the figure's close request function.
At the beginning of the TimerFcn function I set a flag that the timer is running. If a closereq triggers the CloseReqFcn it validates if the timer is running or not. If so it returns without deleting the figure/GUI, if not it deletes it. At the end of the TimerFcn I set the running flag to false and check if a closereq ist pending. If not, proceed without further ado. If closereq is pending stop/delete the timer and start a second timer that triggers after a certain start delay. Its TimerFcn then calls the closereq function again which finally deletes the figure. This can even be made more robust by doing additional checks and validation using the second timer.
I tested this solution for different computation times.
See attachment for the solution of this problem.
Again, thank you alot for your support! =)

4 Comments

That's a lot more effort than just storing the timer and stopping it in the closereq function regardless of it's current state.
I.e. the closereq function would be something like
stop(handles.Timer1) % stop timer
delete(handles.Timer1) % delete it
delete(handles.figure1) % force figure to be cleared
Well, sadly exactly that is not possible! The closereq can trigger when the TimerFcn is still being executed and apparently interrupts it. Then the CloseReqFcn gets executed, the figure deleted AND only then the programm seems to return to the interrupted TimerFcn and finishes its execution. Ofcourse an error occurs since all the graphical handles are no more. Thats the very problem I described at the beginning.
I just did try out your solution and the timer echos the very error I described before.
Error while evaluating TimerFcn for timer 'timer-1'
Invalid object handle
And I did verify that MATLAB returns to finish the presumably interrupted TimerFcn by writing information to the command window when the TimerFcn reached certain sections of its code. Something like
TimerFcn:
Started timer ...
Started heavy computation ...
Finished heavy computation.
Updated axes.
Finished timer.
CloseReqFcn:
Timer stopped.
Timer deleted.
Figure deleted.
is expected. But
TimerFcn:
Started timer ...
Started heavy computation ...
CloseReqFcn:
Timer stopped.
Timer deleted.
Figure deleted.
Error while evaluating TimerFcn for timer 'timer-1'
Invalid object handle
happens. Well, thats how I understand it. Or am I getting it totally wrong and just don't understand what you are trying to tell me? I am eager to learn :-).
Okay, then wait after stopping
stop(T)
wait(T) % waits for timer to actually stop
delete(T)
delete(fig)
Good idea! Didn't know that. However, error :(.
Error using timer/wait (line 35)
Can't wait with a timer that has an infinite
TasksToExecute.
I tried to set the TasksToExecute property to 1 after i stopped the timer, but then the same old error is echoed.
Error while evaluating TimerFcn for timer 'timer-1'
Invalid object handle

Sign in to comment.

Hannes
Hannes on 2 Sep 2014
Edited: Hannes on 2 Sep 2014
I just created a minimal working example that maybe better explains my problem. I attached the mytimer.fig and mytimer.m file.
function varargout = mytimer(varargin)
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @mytimer_OpeningFcn, ...
'gui_OutputFcn', @mytimer_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
function mytimer_OpeningFcn(hObject, eventdata, handles, varargin)
% Create global that stores the figure-, axes- and timer-handle.
global gui
gui.figure = hObject;
gui.axes = handles.axes1;
gui.timer = timer('BusyMode', 'drop',...
'ExecutionMode', 'fixedRate',...
'Period', 1/25,...
'TimerFcn', @timer_callback);
handles.output = hObject;
guidata(hObject, handles);
function varargout = mytimer_OutputFcn(hObject, eventdata, handles)
varargout{1} = handles.output;
function pushbutton_start_Callback(hObject, eventdata, handles)
% Update button strings for information.
set(handles.pushbutton_start,'Enable','off')
set(handles.pushbutton_stop,'Enable','on')
% Start the timer.
global gui
start(gui.timer);
function timer_callback(varargin)
% Runs some time consuming calculations.
[x,y] = heavyCalculation();
% Plots data.
global gui
figure(gui.figure);
axes(gui.axes);
cla(gui.axes);
plot(gui.axes,x,y,'Color',[rand rand rand]);
function pushbutton_stop_Callback(hObject, eventdata, handles)
% Stop the timer.
global gui
stop(gui.timer)
% Update button strings for information.
set(handles.pushbutton_start,'Enable','on')
set(handles.pushbutton_stop,'Enable','off')
function figure1_DeleteFcn(hObject, eventdata, handles)
% Delete any timers and display some
% information about that process.
T = timerfind;
if ~isempty(T)
disp('Timers found! Deleting timers ...')
stop(T)
delete(T)
if isempty(timerfind)
disp('All timers deleted!')
else
disp('Still found timers!')
end
else
disp('No timers found!')
end
function [x,y] = heavyCalculation()
N = 50;
data = rand(N,N,N);
tic
for i = 1:size(data,1)
for j = 1:size(data,2)
mean(data(i,j,:));
end
end
x = rand(1,N);
y = rand(1,N);
% Displays the default and required fps.
% % fprintf('Timer period: 0.0400 seconds per frame <=> 25.0000 fps\n')
% % fprintf('Required: %1.4f seconds per frame <=> %2.4f fps\n',toc,1/toc)
On my system it causes the following message and error.
Timers found! Deleting timers ...
Still found timers!
Error while evaluating TimerFcn for timer 'timer-1'
Invalid object handle
By setting N in heavyCalculation() from now 50 to , e.g., 10, it decreases the required computation time alot and the required fps are much larger than 25. This way no error occurs. So my assumption is, that the computation time being to long causes this error.
Another thing you can do is inside of the timerfcn, validate that it won't error by checking the whether the axes (or whatever else) is still valid. For example:
function TimerEx3
ax = axes;
T = timer('Period',1,... %period
'ExecutionMode','fixedRate',... %{singleShot,fixedRate,fixedSpacing,fixedDelay}
'BusyMode','drop',... %{drop, error, queue}
'TasksToExecute',inf,...
'StartDelay',0,...
'TimerFcn',@(src,evt)timerfcn(src,evt,ax),...
'StartFcn',[],...
'StopFcn',[],...
'ErrorFcn',[]);
start(T);
end
function timerfcn(src,evt,ax)
drawnow
% Check if the axes exists, if it doesn't, kill the timer
if ~ishandle(ax)
stop(src)
delete(src)
else
% Do whatever
plot(rand(1,10))
end
end

3 Comments

Yes, that is an idea! But don't I have to do check that for every graphics object I am using during the call to my TimerFcn? For every call to plot, area, axes, figure, etc. ... that seems kind of inconvenient. But I am afraid that there is not much I can do to solve this problem in any different way :(. I am going to give it a try!
Well if you're plotting to the axes, just checking the axes will assert the existence of the parent figure. If you're updating the line, checking the line will assert the existence of the axes and figure. Basically, the lower you can check on the graphics hierarchy, the less you have to do.
I think if the axes is gone, everything else will be gone too because you're shutting down or have already shutdown, so you should not have to check for existence of any other controls.

Sign in to comment.

Hannes
Hannes on 16 Sep 2014
Conclusion:
Running TimerFcns can be interrupted, e.g., by the GUIs CloseRequestFcn or several other callback functions. MATLAB continous to finish execution of the TimerFcn after the interrupting routine is finished.
Such being the case, onne has to check if the interrupting routine changed/deleted variables/objects the TimerFcn requires. For example, in the case posted above during execution of the TimerFcn a call to a deleted graphic object (due to the figure being closed) caused an error. To negotiate this obstacle, before I delete the figure, I check whether the TimerFcn is running or not and delay closing the figure accordingly.
Many thanks to Image Analyst and Sean de Wolski for their help and suggestions :-).
This is a bit old but I was facing the same issue.
The main problem is that the close figure callback needs to exit before the timer callback can resume (and consequently the stop function to be triggered).
One proper implementation of such a case is to have the close figure callback checking if the timer is running. If yes, it requests stop function timer to recall the close figure callback by setting a flag in UserData (tm.UserData.req_close).
In the first example, the timer is running. In the second, the time is not running.
function example
% This example shows how to stop a timer from a callback while the timer is running.
fprintf('--------------------\nFigure with timer.\n');
d.hfig = figure('CloseRequestFcn', @cb_close, 'Name', 'Timer is running.');
d.tm = timer('ExecutionMode', 'fixedDelay', 'Period', 4, 'BusyMode', 'drop', ...
'StartFcn', @tm_start, 'StopFcn', @tm_stop, 'TimerFcn', @tm_fun);
d.tm.UserData.hfig = d.hfig;
guidata(d.hfig, d);
start(d.tm);
waitfor(d.hfig);
% Try without the timer. Note that the timer must still be a valid object even if not running.
fprintf('--------------------\nFigure without timer.\n');
d.hfig = figure('CloseRequestFcn', @cb_close, 'Name', 'Timer is not running.');
d.tm = timer('ExecutionMode', 'fixedDelay', 'Period', 4, 'BusyMode', 'drop', ...
'StartFcn', @tm_start, 'StopFcn', @tm_stop, 'TimerFcn', @tm_fun);
d.tm.UserData.hfig = d.hfig;
guidata(d.hfig, d);
waitfor(d.hfig);
end
%% Timer functions.
function tm_start(tm, ~)
fprintf('Timer starts.\n');
tm.UserData.req_close = false;
end
function tm_stop(tm, ~)
fprintf('Timer stops.\n');
if tm.UserData.req_close
fprintf('Recall cb_close.\n');
cb_close(tm.UserData.hfig);
% NOTE: from here to the end of the function, tm cannot be used.
end
end
function tm_fun(~, ~)
% NOTE: this function will never be interrupted by stopping the timer.
fprintf('Function running...\n');
pause(2); % Just do something.
fprintf('Function ended.\n');
end
%% Callbacks.
function cb_close(hobj, ~)
d = guidata(hobj);
fprintf('Closing called.\n');
if d.tm.Running == "on"
% Do not close the figure here.
d.tm.UserData.req_close = true;
stop(d.tm); % Will recall cb_close(hobj);
else
% Now timer and figure can be deleted.
delete(d.tm);
delete(d.hfig);
end
fprintf('Closing ended.\n');
end
Here is an example of output
--------------------
Figure with timer.
Timer starts.
Function running...
Function ended.
Function running...
Function ended.
Function running...
Closing called. % User click on the top left cross.
Closing ended. % Here the close figure callback ends.
Function ended. % The timer function ends
Timer stops. % Then the timer stop is called.
Recall cb_close. % Triggering another call of the close figure callback.
Closing called.
Closing ended.
--------------------
Figure without timer.
Closing called.
Closing ended.

Categories

Find more on Code Execution in Help Center and File Exchange

Asked:

on 1 Sep 2014

Edited:

on 16 Jul 2024

Community Treasure Hunt

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

Start Hunting!