I want to pause a timer function (for like 5 seconds) in the middle of its execution in MATLAB GUI. Pause(5) can do it but the problem is that it pauses all other callback functions to execute (including other timer functions I am using in the MATLAB GUI, for example).
I was thinkink maybe I can write a dummy loop that could be executed for 5 sec but it might not be accurate and efficient. Do you have any suggestion?
Additional info:
handles.maintmr = timer(...
'ExecutionMode', 'fixedRate', ...
'TasksToExecute',1,'StartDelay',299, 'Period', 1,...
'TimerFcn', {#ttl_timer, hObject});
handles.et_tmr = timer(...
'ExecutionMode', 'fixedRate', ...
'Period', 0.5, ...
'TimerFcn', {#timer_ET_rec, hObject});
handles.tmr = timer(...
'ExecutionMode', 'fixedRate', ...
'Period', 9.85, ...
'TimerFcn', {#timer_update_display, hObject});
These are the defined three timer functions I am using in MATLAB GUI
And these are how I call the callback timer functions:
function timer_ET_rec(obj,event,hObject,eventdata)
handles = guidata(hObject);
function timer_update_display(obj,event,hObject,eventdata)
handles = guidata(hObject);
function ttl_timer(obj,event,hObject,eventdata)
handles= guidata(hObject);
I would try something like
t = tic();
while toc(t) < 5
pause(0.1);
drawnow('limitrate');
end
I like Edric's solution. Other options:
Option 1
The waitfor(obj) function (interrupts execution until object 'obj' is deleted).
eg.
%Pop-up msg;
h1 = errordlg({'This message interrupts the code. Close me to continue'},...
'Hi! I'm a pop-up')
% interrupt until closed
waitfor(h1)
Option 2
The timer() function doesn't run off the wallclock (ie. actual real world time) so I'm not sure it can do what you seem to what --- maintaining regular execution of code every x minutes regardless of interrupts and pauses in the code. But some methods can get close to it.
a) use the timer function to regularly TEST if you have passed a particular period according to wallclock time or not (if statement with clock()). If so, execute some other code.
b) Change the timer 'BusyMode' from the assumed default 'drop' to 'queue'and it will insert code to run as close as possible to the desired time.
Best solution will depend on what exactly you are trying to achieve.
Related
I'm trying to make a timer that counts down from 20 to 0 (seconds) in GUIDE. In the meantime the user will perform a simple action (clicking a radio button in a group button) and at the end of that 20 seconds a message will appear (depending on which button the user clicked).
I looked around but it seems that there isn't a timer object for GUIDE (why don't they make one since it's so useful??). However I tried to make one and below there's the result, it doesn't work.
I initialised setappdata in MyGUI_OpeningFcn:
% Initialize setappdata
timeout = 20;
setappdata(handles.figure1,'timeout', timeout);
Next_calculation is radio button and timerBox is a static text.
function Next_calculation_Callback(hObject, eventdata, handles)
[..]
timeout = getappdata(handles.figure1,'timeout');
t = timer('Period', 1.0,... % 1 second
'StartFcn', set(handles.timerBox,'String',num2str(timeout)), ...
'ExecutionMode', 'fixedRate', ... % Starts immediately after the timer callback function is added to the MATLAB execution queue
'TasksToExecute', timeout, ... % Indicates the number of times the timer object is to execute the TimerFcn callback
'TimerFcn', #my_timer ... % callback to function
);
start(t)
Once the timer begins, it calls TimerFcn that calls my_timer. I should pass a handle to my_timer, but I don't know exactly how.
function my_timer(hObject, eventdata)
% I think I'm supposed to pass (hObject, eventdata) to my_timer
% handles should be getting the current figure from hObject
handles = guidata( ancestor(hObject, 'figure1') );
timeout = getappdata(handles.figure1,'timeout');
t_left = timeout - 1.0;
% show the updated time
set(handles.timerBox,'String',num2str(t_left));
% update 'timeout'
setappdata(handles.figure1,'timeout',t_left)
You need to use a custom anonymous function for the TimerFcn to pass the necessary data to your timer callback
set(t, 'TimerFcn', #(s,e)my_timer(hObject, handles))
You can then define your my_timer callback as
function my_timer(hObject, handles)
% Do stuff with handles
end
I am stuck in an error of timer function. Although I have tried to search, I find no answer.
function figure1_CreateFcn(hObject, eventdata, handles)
global t
t = timer( 'ExecutionMode', 'fixedRate', 'StartDelay',1, 'Period',1,'TasksToExecute',150);
t.TimerFcn = {#my_callback_fcn};
The error I got is:
Error while evaluating TimerFcn for timer 'timer-1'
Too many input arguments.
My MATLAB version is R2014a. The start function is called pushbutton2_Callback function.
I have tried to change Period to 1.0, TasksToExecute to inf or TimerFcn in the call to timer. It does not help.
Is there anyone who can help me out?
The error is caused because your provided callback function my_callback_fcn does not have two input arguments. You have two possibilities to solve that. Only do one of them:
Create an anonymous function with two input arguments when you assign the timer callback:
t.TimerFcn = {#(obj,event)my_callback_fcn};
Add two input arguments to my_callback_fcn so your first line of that function looks like:
function my_callback_fcn(obj,event)
You can ignore the arguments with ~ if you do not need them in the function. So your code from the question could look like this:
t = timer('ExecutionMode', 'fixedRate', 'StartDelay',1, 'Period',1, 'TasksToExecute',150);
t.TimerFcn = {#(~,~)my_callback_fcn};
Here is some more information on timer callback functions.
To address this comment and that one:
Don't attempt to apply both solutions at the same time because then you'll add two arguments to the function prototype and then don't provide it. The result would be the following error:
Error while evaluating TimerFcn for timer 'timer-1'
Not enough input arguments.
You only need to do solution 2 according to the prototype you posted in your comment. Here is a working demo:
function timertest
t = timer('ExecutionMode', 'fixedRate', 'StartDelay',1, 'Period',1, 'TasksToExecute',150);
t.TimerFcn = {#my_callback_fcn};
start(t); pause on; pause; stop(t); delete(t);
function my_callback_fcn(handles,~)
handles;
disp('xy');
Is there any way to extract the elapsed time from the beginning of each period from a timer object in MATLAB?
Say we have a timer object like this:
t=timer('ExecutionMode', 'fixedRate', ...
'Period', 15, ...
'TimerFcn',#(x,y)disp('Hello World!'))
The y contains the absolute timestamp. Store it when the timer is stated and take the difference:
function my_callback(m,x,y)
persistent starttime
switch m
case 'start'
starttime=y.Data.time;
case 'timer'
disp(datenum(y.Data.time-starttime)*24*60*60);
otherwise
error('unknown argument');
end
end
With a timer:
t=timer('ExecutionMode', 'fixedRate','Period', 15,'StartFcn',#(x,y)my_callback('start',x,y),'TimerFcn',#(x,y)my_callback('timer',x,y))
start(t)
Alternative solution using userdata:
t=timer('ExecutionMode', 'fixedRate','Period', 15,'StartFcn',#(x,y)set(x,'UserData',y.Data.time),'TimerFcn',#(x,y)disp(datenum(y.Data.time-get(x,'UserData'))*24*60*60))
Approach using tic, toc and 'UserData'
There is no direct method on the timer-object to read the elapsed time since its last call. It is possible to use a construct with tic and toc to achieve what you want.
The generic field 'UserData' can be used to add user-data to the timer object. We can write this value with set() and read it with get(). So we set this value with tic in the callback-function and get it back later to compute the time difference using toc.
More specifically, here is the callback-function:
function timerCallback(timerObj,event) %#ok<INUSD>
disp('Hello World!');
set(timerObj,'UserData',tic);
Now we can write a wrapper-function to get the elapsed time:
function retval = getElapsedTime(timerObj)
retval = toc(get(timerObj,'UserData'));
Example usage of proposed approach
To see if this approach works, we use pause to wait in a for loop and read the elapsed time approximately every second. Here is a complete example:
function timer_example
t = timer('ExecutionMode', 'fixedRate', ...
'Period', 5, ...
'TimerFcn', #timerCallback); % create timer
start(t); % start timer
pause on; % make sure pause can be used
for i = 1:10
pause(1); % wait a second
disp(getElapsedTime(t)); % display the elapsed time
end
stop(t); % stop timer
delete(t); % delete timer
function timerCallback(timerObj,event) %#ok<INUSD>
disp('Hello World!');
set(timerObj,'UserData',tic);
function retval = getElapsedTime(timerObj)
retval = toc(get(timerObj,'UserData'));
This gives the following output:
>> timer_example
Hello World!
1.0163
2.0173
3.0235
4.0285
Hello World!
0.0366
1.0390
2.0427
3.0461
4.0470
Hello World!
0.0515
Use tic as StartFcn of your timer and then use toc to elicit elapsed time, so:
timerObj = timer; %create timer object
timerObj.ExecutionMode = 'fixedSpacing';
timerObj.Period = 15;
timerObj.StartFcn = #(~,~)tic; % start ticking when timer starts
timerObj.TimerFcn = #myTimerFunc;
myTimerFunc is:
function [] = myTimerFunc(timerObj,event)
% use toc
disp(toc);
end
I have a for loop like this
for t = 0: 1: 60
// my code
end
I want to execute my code in 1st, 2nd, 3rd, ..., 60th seconds. How to do this? Also how can I run my code at arbitrary times? For example in 1st, 3rd and 10th seconds?
What you can do is use the pause command and place how many seconds you want your code to pause for. Once you do that, you execute the code that you want. As an example:
times = 1:60;
for t = [times(1), diff(times)]
pause(t); % // Pause for t seconds
%// Place your code here...
...
...
end
As noted by #CST-Link, we should not take elapsed time into account, which is why we take the difference in neighbouring times of when you want to start your loop so that we can start your code as quickly as we can.
Also, if you want arbitrary times, place all of your times in an array, then loop through the array.
times = [1 3 10];
for t = [times(1), diff(times)]
pause(t); %// Pause for t seconds
%// Place your code here...
...
...
end
Polling is bad, but Matlab is by default single-threaded, so...
For the first case:
tic;
for t = 1:60
while toc < t, pause(0.01); end;
% your code
end;
For the second case:
tic;
for t = [1,3,10]
while toc < t, pause(0.01); end;
% your code
end;
The pause calls were added following the judicious observation of Amro about busy waiting. 0.01 seconds sounds like a good trade between timing precision and "amount" of spinning...
while pause is most of the time good enough, if you want better accuracy use java.lang.Thread.sleep.
For example the code below will display the minutes and seconds of your computer clock, exactly on the second (the function clock is accurate to ~ 1 microsecond), you can add your code instead of the disp command, the java.lang.Thread.sleep is just to illustrate it's accuracy (see after the code for an explanation)
while true
c=clock;
if mod(c(6),1)<1e-6
disp([c(5) c(6)])
java.lang.Thread.sleep(100); % Note: sleep() accepts [mSecs] duration
end
end
To see the difference in accuracy you can replace the above with java.lang.Thread.sleep(999); vs pause(0.999) and see how you sometimes skip an iteration.
For more info see here.
EDIT:
you can use tic\ toc instead of clock, this is probably more accurate because they take less time...
You can use a timer object. Here's an example that prints the numbers from 1 to 10 with 1 second between consecutive numbers. The timer is started, and it stops itself when a predefined number of executions is reached:
n = 1;
timerFcn = 'disp(n), n=n+1; if n>10, stop(t), end'; %// timer's callback
t = timer('TimerFcn' ,timerFcn, 'period', 1, 'ExecutionMode', 'fixedRate');
start(t) %// start the timer. Note that it will stop itself (within the callback)
A better version, with thanks to #Amro: specify the number of executions directly as a timer's property. Don't forget to stop the timer when done. But don't stop it too soon or it will not get executed the expected number of times!
n = 1;
timerFcn = 'disp(n), n=n+1;'; %// this will be the timer's callback
t = timer('TimerFcn', timerFcn, 'period', 1, 'ExecutionMode', 'fixedRate', ...
'TasksToExecute', 10);
start(t) %// start the timer.
%// do stuff. *This should last long enough* to avoid stopping the timer too soon
stop(t)
Background: I have several portion in an GUI to handle different tasks. In 1 portion (portion1) I have text input and send button. So once I will click send it would send one data to my serial port. Another portion (portion2) would receive signal from serial port which have been received from other devices. Both the portions are using two buttons; one to start the work of that particular portion and one to stop the work. I have used global variables and while loop (with boolean) to exit the infinite loop, as I need to send the data or receive data continuously.
My question: Problem is when I am using global variables and the above mentioned way of using infinite loop, if I click portion1, portion1 will start iterate. Now if I click portion2 then portion1 will be stopped. But I need to use them both at the same time and both of them will continuously send and receive data until I click other button (s) to exit the infinite loop.
My sample code for clarification:
function send_Callback(hObject, eventdata, handles)
global firstFlag;
firstFlag = true;
while firstFlag
%Required codes
end
function haltSend_Callback(hObject, eventdata, handles)
global firstFlag;
firstFlag = false;
function receive_Callback(hObject, eventdata, handles)
global secondFlag;
secondFlag = true;
while true
%Required codes
end
function stopReceive_Callback(hObject, eventdata, handles)
global secondFlag;
secondFlag=false;
I have tried to find a solution referring to internet, but most of the solutions are using global variable. It would be better if I could work without global variables. But even if my requirements are fulfilled (as per my question) it would work.
Global or not is not the issue here.
Callbacks in Matlab all run on the same thread.
So when callback1 is running and you trigger callback2, callback2 will interrupt callback1.
callback1 will then only proceed once callback2 is finished.
You can only slightly modify this procedure using the BusyAction property:
http://www.mathworks.de/de/help/matlab/ref/uicontrol_props.html#bqxoija
But this won't help in your case.
In a "proper" programming language, you'd have a sending and a receiving thread running in parallel. You can't do this matlab however - unless you're e.g. willing to write java-code.
If you want to stick with matlab, the closest to thread would be timers.
These would replace your while loops.
E.g. as in the following minimalistic example:
function cbtest()
try close('cbtest');end
f = figure('name', 'cbtest');
% create the timers:
period = 0.2; % period in seconds, in which the timer shall execute
sendTimer = timer('TimerFcn', #sendFcn, 'ExecutionMode', 'fixedDelay', 'Period', period, 'TasksToExecute', Inf);
recvTimer = timer('TimerFcn', #recvFcn, 'ExecutionMode', 'fixedDelay', 'Period', period, 'TasksToExecute', Inf);
uicontrol(f, 'position', [10 10 100 25], 'Callback', #(a,b) start(sendTimer), 'string', 'start1');
uicontrol(f, 'position', [120 10 100 25], 'Callback', #(a,b) stop(sendTimer), 'string', 'stop1');
uicontrol(f, 'position', [10 50 100 25], 'Callback', #(a,b) start(recvTimer), 'string', 'start2');
uicontrol(f, 'position', [120 50 100 25], 'Callback', #(a,b) stop(recvTimer), 'string', 'stop2');
end
function sendFcn(hTimer, timerEvt)
% your send-loop-code
disp('sending');
end
function recvFcn(hTimer, timerEvt)
% your receive-loop-code
disp('receiving');
end
sendFcn and recvFcn here then should contain the code you have within your according while loops.
You can of course lower the period to your needs, I chose the above for testing purposes.
Your problem is that you're executing the loop inside the Callbacks. So when the second button is clicked, the second callback will start and loop infinitely, until it is ended. The first loop will wait for the second Callback to terminate until it can resume. What you need is a main program where you execute your loop. Nesting the Callbacks will make sure they can access and change the local variables without making them global. If you're fine with building a GUI programmatically, try this:
function main()
sendFlag = false;
receiveFlag = false;
while true
if sendFlag
% Your Sending code
end
if receiveFlag
% Your Receiving code
end
end
function send_Callback(~,~,~)
sendFlag = true;
end
% other Callbacks
end
Then in the Callbacks for your Buttons (you could use Toggle Buttons by the way), you simply set the sendFlag and receiveFlag, respectively.
Using GUIDE, you want to use toggle buttons.
I don't exactly know how GUIDE handles the OpeningFcn, so you should probably put a "Start" button into your GUI which basically executes the above program with a few changes:
function startbutton_Callback(hObject,~,handles)
while true
handles = guidata(hObject); % Updates handles structure
sendFlag = get(handles.sendtoggle, 'Value');
if sendFlag
% Your Sending code
end
receiveFlag = get(handles.receivetoggle, 'Value');
if receiveFlag
% Your Receiving code
end
end
Also, create send and receive toggle buttons and set their 'Min' to 0 and 'Max' to 1. This will make them switch their 'Value' property (which you read in the above loop) between 0 and 1 when you click them. In the Callback, you can change what they display:
function send_Callback(hObject,~,handles)
on = get(hObject,'Value');
if on
set(hObject, 'String', 'Stop sending');
else
set(hObject, 'String', 'Start sending');
end
guidata(hObject,handles); % Update GUI data
Now your main function, which starts when you click the start button, runs the loop and just checks the toggle buttons' states to determine whether to send and receive.