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
Related
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.
I created an interface which automatically reads in data through the serial port, hence the reason I implemented the BytesAvailableFcn Callback
handles.fileID.BytesAvailableFcnMode = 'terminator';
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
The data that is read is displayed in a table chosen by the user (through use of radio buttons in the GUI). When an option is chosen a callback occurs to save the selected radio button to a variable which is saved in the handles struct. I have followed the program step for step and I am sure this callback does occur and that the variable is saved. However when the serial callback occurs the handles struct still has the old option value.
Here is the serial callback code:
function handles = streamData_fastTrak(hObject, eventdata, handles)
handles.num = handles.num + 1;
%receive data through serial
line = transpose(fscanf(handles.fileID, ' %f ' ));
table_data = get(handles.uitable1, 'data');
table_data_style = get(handles.uitable4, 'data');
display(handles.butt_state);
display(handles.num);
if(fix(line(1)) == 1 && strcmp(handles.butt_state, 'style_button'))
table_data_style(handles.select_Indices(1), 2:(length(line)+1)) = num2cell(line);
set(handles.uitable4, 'data', table_data_style);
display(handles.select_Indices);
elseif(fix(line(1)) > 1 && strcmp(handles.butt_state, 'stat_button'))
table_data(line(1)-1, 1:length(line)) = num2cell(line);
set(handles.uitable1, 'data', table_data);
if(line(1) == countStates(handles))
streamSensor_1_2_3(hObject, handles);
handles.time_step = handles.time_step + 1;
end
end
And the radio button callback:
function uipanel2_SelectionChangeFcn(hObject, eventdata, handles)
handles.butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
display(handles.butt_state);
guidata(hObject, handles);
The way I see it there are 2 ways to approach the problem:
The first way (I don't recommend this as much as the second one) is to pass the data you want updates to a string control and have it read back by your serial port function.
The other way that i recommend is to include a dummy button with a call back that calls
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
Again - this will update the new "handles" data to the callback function
For example
Assuming a dummy push button with tag PB1
function handles = streamData_fastTrak(hObject, eventdata, handles)
%% do stuff here
%% update handles data
PB1_Callback (handles.PB1,event,dat)
guidata(handles.PB1,handles) %% function ends
%% dummy button callback function%%
function PB1_Callback(hObject,event,handles)
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
guidata(hObject,handles) %% dummy button function ends
You can make the dummy button invisible by making the background color of the button same as that of the UI.
When you first declare your callback function for the ByteAvailableFcn in the line:
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
Matlab assign the function handle to the event and also pass the handles stucture at this point of time. This is now frozen into the private workspace of the callback. If you change the handles structure later on in your code (as you do when you try to attach the variable handles.butt_state), the callback doesn't know it, it still use the handles structure that was passed when you declared the callback.
There are several ways of getting this value correctly but I'll give 2 of them:
1) get the value from the radio button when needed
in your streamData_fastTrak callback function, query the button state directly from the uicontrol (instead of checking for a saved value)
handles.butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
This way you are sure to get the latest state of the radio button.
2) Store value in appdata
Every time the button state is changed, store the value somewhere, but you still have to query this value when your callback want to execute. A good place to save values are in the appdata (accessed using setappdata and getappdata).
So in your button callback:
function uipanel2_SelectionChangeFcn(hObject, eventdata, handles)
butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
setappdata(handles.uipanel2, 'butt_state' , butt_state );
display(butt_state);
And in your streamData_fastTrak callback:
function handles = streamData_fastTrak(hObject, eventdata, handles)
butt_state = getappdata(handles.uipanel2, 'butt_state' );
%// and the rest of your code ...
I am working with serial port and multiple list boxes to read data in serial port. The listboxes are used to read specific data (suppose the data is coming from 3 different sources, so I have 3 listboxes).
Now the problem:
I want to read all the data separately and continuously. I mean, when I am clicking one listbox (e.g. listbox#2) after reading listbox#1's data, the listbox#1 automatically stops, so again I need to click listbox#1 to read the data for listbox#1. But I want to get the data continuously in all the listboxes without clicking (clicking for first time to initializing) every time. How can I achieve this? I have tried other ways but not working.
Additional comment: It worked as per the below comments.
Sample code [Edited and OpeningFcn has been added]:
function main_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;
handles.timer = timer(...
'ExecutionMode', 'fixedRate', ... % Run timer repeatedly
'Period', 1, ... % Initial period is 1 sec.
'TimerFcn', {#listbox1_Callback, handles});
guidata(hObject, handles);
function listbox1_Callback(hObject, eventdata, handles)
%serConn is the connection to COM port
out = fscanf(handles.serConn);
string = 'degrees1';
t=1;
while(t < 15)
if strncmp(out,string,8)
rxtext = out(14:15);
currList = get(handles.listbox, 'String');
set(handles.listbox,'String',...
[currList ; [temp]]);
end
pause(5);
t = t+1;
end
Trying to use Timer in GUI. While attempting in following code it is showing error.
function main_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;
handles.timer = timer(...
'ExecutionMode', 'fixedRate', ... % Run timer repeatedly
'Period', 1, ... % Initial period is 1 sec.
'TimerFcn', {#send_Callback,hObject});
guidata(hObject, handles);
function send_Callback(hObject, eventdata, handles)
comma = get(handles.Tx_send, 'String');%Tx_send is a text field
TxText = char(comma);
sf = rc4e2(TxText,key);%rc4e2 is an encryption
key = TxText;
DBC = char(sf);
disp(DBC);
fwrite(handles.serConn, DBC);%serConn is COM port
The error: Error while evaluating TimerFcn for timer 'timer-1'. Attempt to reference field of non-structure array.
Try changing your timerFcn to {'send_Callback',handles}.
In your version (as written in the original question), you have to write
'TimerFcn', {#send_Callback,handles});
The reason MATLAB is showing this error is because when it calls the Callback function, it automatically passes the timer handle as first argument and an empty event structure as second argument. The argument you provide by using the cell array is the third one. This means, your send_Callback is called with the argument list handles.timer,event,hObject (in this case, hObject is the window handle).
Then in the Callback function, you try to access handles.Tx_send, but since handles is the third argument and you provided just the window handle as the third argument, MATLAB will try to access handles.output.Tx_send, which does not exist.
Passing handles as described above should solve your problem because then the callback will have access to the handles object.
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.