My GUI's aim is to show images and to get a response from the user: either a key press (E or I) or no response. Between the images themselves there should be a 3 second pause showing some text (a7 UIcontrol in my code). The problem is that I need to do it for 30 times, so I use a loop with a timer inside it. But the GUI works badly..
It should do the following:
for 30 times do
2 sec showing text (a7)
then showing an image for 3 sec or until I\E are pressed
end
This is my code; I am adding two versions, because they differ mostly in the TIMER functions and properties..
https://docs.google.com/document/d/1N6LSDAYo_DVrBCUbuPth4JPCvkI3pBNcnAZcV6Kl9wM/edit
more readable version: http://pastebin.com/vd3HNGv1
and the photos are here (although you can use any 2 photos): https://picasaweb.google.com/alex.goltser/ScrapbookPhotos
At first the problem was always an error:
you try to start the timer while it works
But now it is something else..
Why run timer functions at all?
Here's an other way how you could run your loop:
for repeat = 1:30
*show text*
drawnow %# to make sure the graphics are updated
pause(2) %# wait two seconds
*show image*
drawnow
t = tic;
done = false;
while ~done && toc(t)<3 %# checks for keypress or until 3 secs
*check for keypress*
if E/I key has been pressed
done = true;
end
end
end %# repeat 30x
Related
I have a program using two timers. The first one allows me to communicate with a microcontroller every second and to update charts, and I use the second one to stop the program after 50 seconds if the user did not press stop button before. The problem is that I don’t know how to wait the end of the execution of the callback function of the first timer before ending the program. Sometimes, it stops in the middle of the first callback. I would like to avoid that, but I don’t know how. I tried to use “waitfor”, but it doesn’t work.
Here is a simple example with the same problem.
t = timer('ExecutionMode','fixedRate','Period', 1,'TimerFcn',#testWait);
fwait = figure('Visible','off');
start(t);
disp('start');
pause(5);
delete(fwait);
i=0;
while true
waitfor(fwait);
disp(int2str(i));
pause(0.05);
i = i + 1;
end
function testWait(src,event)
disp('before');
waitfor(evalin('base','fwait'));
disp('after');
assignin('base','fwait',figure('Visible','off'));
pause(1);
delete(evalin('base','fwait'));
end
Can someone help me please ! :)
You can try using uiwait and uiresume. Here's a code snippet for how to use it:
f = figure;
h = uicontrol('Position',[20 20 200 40],'String','Continue',...
'Callback','uiresume(gcbf)');
disp('This will print immediately');
uiwait(gcf);
disp('This will print after you click Continue');
close(f);
Using timer objects gets too complicated especially when you have to use more than one timer so I was trying to think of alternative approaches.
I want to avoid using pause, since it stops other functions from executing. I thought of using the tic toc functions to measure elapsed time but the code I have written below does not work as I expect.
time=tic;
if(abs(toc(time)))==3 %% if 3 second past
my function
end
How can I modify this code so that it executes the command after 3 seconds?
TLDR;
A tic/toc pair and a while loop is literally no different than using pause since they both block execution of any other functions. You have to use timer objects.
A More Detailed Explanation
In order to make this work, you'd need to use a while loop to monitor if the required amount of time has passed. Also, you'll want to use < to check if the time has elapsed because the loop condition isn't guaranteed to be evaluated every femtosecond and therefore it's never going to be exact.
function wait(time_in_sec)
tic
while toc < time_in_sec
end
% Do thing after 3 seconds
fprintf('Has been %d seconds!\n', time_in_sec)
end
The unfortunate thing about a while loop approach is that it prevents you from running multiple "timers" at once. For example, in the following case, it will wait 3 seconds for the first task and then wait 5 seconds for the second task requiring a total time or 8 seconds.
wait(3)
wait(5)
Also, while the while loop is running, nothing else will be able to execute within MATLAB.
A much better approach is to setup multiple timer objects and configure them with callbacks so that they can run at the same time and they will not block any operations within MATLAB while they run. When you need multiple timer objects (which you consider to be a pain) is exactly when you have to use timer objects.
If it's really that cumbersome, write your own function which does all of the boilerplate stuff for you
function tmr = wait(time_in_sec)
tmr = timer('StartDelay', time_in_sec, ...
'ExecutionMode', 'SingleShot', ...
'TimerFcn', #(s,e)status(time_in_sec));
tmr.start()
function status(t)
fprintf('Has been %d seconds!\n', t);
end
end
wait(3)
wait(5) % Both will execute after 5 seconds
Also since the timers are non-blocking (when the callback isn't running), I can execute commands immediately after starting the timers
wait(3)
disp('Started 3 second timer')
wait(5)
disp('Started 5 second timer')
If you try this with your while loop, you'll see the while loop's blocking behavior.
you need a while loop or something to wait for time to be 3 seconds.
something like this
time=tic;
while 1
if(abs(toc(time)))==3 %% if 3 second past
my function
break;
end
end
If you want to call my function every 3 seconds, then you should do something like this:
time=tic;
while 1
if mod((abs(toc(time))),3) == 0 %% if 3 second past
my function
end
end
Please make sure that you have some way of telling once you are done and then break the while loop.
You could set it to >= instead of ==. That will catch it if it misses the exact value.
I have a MATLAB GUI that looks as shown in:
MATLAB GUI image
What I am trying to achieve is that MATLAB keep checking for midnight continuously, except for the pauses when the user makes any change to the interface. Hence, I am running a while loop in the background continuously as I need to check if it is midnight. If it is, I perform some functions. The function that contains this while loop is called after any user input change is detected i.e. at the end of all the callback functions for the pop-up menus,pushbuttons, textboxes etc. This is the reason I had the drawnow in the while loop, so that if the user makes any changes and wants to run some calculations, that will be detected. After the completion of the calculations, I again call the function which has this while loop.
The issue is, even though I use drawnow and pause in my while loop, sometimes, not always, MATLAB still hangs-up on me and the GUI becomes unresponsive and does not recognize any of the user inputs. Here is the while loop part of my code:
while 1
pause(0.1);
drawnow;
pause(0.1);
current_time=clock;
if current_time(4)==0
post_mortem;
end
end
I know the above code is not efficient as it will call post_mortem continuously in the midnight hour, that however is not my issue right now. My issue is that it hangs up on me at times even at noon for example. Does anybody have any solution to this? On searching for answers to previous similar questions, the solution seemed to be to use drawnow and pause, but that doesn't seem to be working for me either.
Any guidance would be appreciated.
Thank you
Since MATLAB is not multi-threaded, using a while loop to continuously check something (such as the time) is going to cause all sorts of blocking of other functionality. While drawnow and pause can potentially help with this, there are still some potential issues that can crop up.
A more elegant and reliable approach would be to use a timer object to check the time at a pre-specified interval. This way, any user interaction with the GUI will automatically be registered and any callbacks will execute without you having to call either pause or drawnow.
You can create and start the timer as soon as you create your GUI.
% Create the timer object
handles.mytimer = timer('ExecutionMode', 'FixedRate', ...
'Period', 1/5, ...
'BusyMode', 'drop', ...
'TimerFcn', #(s,e)timerCallback());
% Start the timer
start(handles.mytimer)
function timerCallback()
% Callback that executes every time the timer ticks
current_time = clock;
if current_time(4) == 0
post_mortem;
end
end
I am working with a script that takes user input. I want the user to answer on the input within 4 seconds otherwise the code shall do something (right now without using a GUI). What I have done so far is to start a tic and prompt a input testInput = input('Enter the number: '); Meanwhile the prompt is open (waiting for input) I am checking if time has not runed out:
elapsed_time = toc;
if elapsed_time > 4
%do something
end
To the problem:
From what I have learned, there's no way to programmatically terminate a running command in MATLAB, or even let the code do something while the prompt is open. Therefore I can not check if 4 sec has passed before the user inputs something. I've read (here) that this might be possible to solve by using a GUI. I have although no idea how to implement this, since I am totally new.
So, would this be possible with a GUI? Because with the command window it is not.
I would really appreciate to see how something simple like this could look like as a GUI (just something very simple, a window with a input box):
%Start a time ticker
tic;
testInput = input('Enter the number: ');
elapsed_time = toc;
if elapsed_time > 4
%do something
end
Here is a small example of a custom GUI where the user should enter a number before a maximum time is reached. Please read some about callbacks, handles, figure options and uicontrol to better understand it.
Note that you might need to do som more fault handling of the input string (check that number is valid)
function EnterNumber()
% Create figure
inDlg = figure('NumberTitle','off','MenuBar','none','ToolBar','none');
% Create timer, set it to run TimerFcn after 4 s
maxTime = 4;
tH = timer('TimerFcn', {#closeFig inDlg}, 'StartDelay', maxTime);
% Create text and input box for number in figure
uicontrol(inDlg,...
'Style','Text','FontSize',14, ...
'String',sprintf('Please enter a number within %d seconds:', maxTime),...
'Units','Normalized','Position',[0.1 0.6 0.8 0.2]);
editBox = uicontrol(inDlg,...
'Style','Edit','Units','Normalized','Position',[0.1 0.5 0.8 0.2], ...
'FontSize',14,'Callback',#returnEditValue);
% Start timer
start(tH);
% Set focus on the edit box so a number could be entered instantly
uicontrol(editBox)
% Wait for figure to be closed (in any way)
waitfor(inDlg)
fprintf('Moving on ...\n');
% Callback function to save number
function returnEditValue(hObject,~)
% Get the number
number = str2double(get(hObject,'String'));
% Example of how to display the number
fprintf('The entered number is %d\n', number);
% Example of saving the number to workspace
assignin('base','number', number);
% Close figure
close(get(hObject,'Parent'));
% Calback function for timer timeout
function closeFig(~,~,figH)
% If figure exist, close it
if ishandle(figH)
close(figH)
fprintf('Too slow!\n')
end
For example if there is a MATLAB program as follows:
duration = 1 % minute
i=1
while i<1000
[X,Y] = ginput(1)
i = i+1;
end
Is there any way to terminate the execution of this program or getting out of the loop when it reaches to the assigned amount of time (1 minute in this case) in such a situation in which continuation of the loop needs the user intervention (in this case clicking on any point on the plotted figure)?
If you want to do a process within your loop for a minimum of a minute, you can do it like this:
start = now
while now - start < 60/60/24
%do something for a minute
end
As a commenter above said, ginput will wait indefinitely so don't plan on that working.