BytesAvailableFcn callback not receiving updated handles - callback

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 ...

Related

How to call another button's callback from a button created by code

Suppose I have two pushbuttons on MATLAB GUI. When I press on pushMain, I want to call the callback of pushChild button.
I might have put simply pushChild_Callback(hObjects, eventdata, handles) in the pushMain's callback but my problem is I created pushChild object by code so there is no seperate callback function on the code.
handles.pushChild = uicontrol('Style','pushbutton');
handles.pushChild.Callback = {#printA, 1}
% this means when I press on pushChild button, it will print "A = 1"
function printA(src, event, j)
handles = guidata(src);
fprintf('A = %d\n',j);
Now when I pressed on pushMain, I want to see "A = 1" on the screen.
I tried:
function pushMain(hObject, eventdata, handles)
fprintf('1\n');
fprintf('2\n');
fprintf('3\n');
handles.pushChild.Callback(); % option 1 -> no error but nothing happens.
handles.pushChild.Callback(hObject, eventdata, handles) % option 2 -> nothing happens
Regards.

MATLAB callback function: undefined function?

I have a button group with 3 radio buttons inside, as well as a display section.
The effect I want is that once choosing a radio button, the display section changes as well.
I implement it using a class, and add the SelectionChangedFcn when creating those components:
app.ControlButtonGroup.SelectionChangedFcn = {#controlBtnGroupSelectionChanged, app}
I define the function controlBtnGroupSelectionChanged(obj, eventData, app) at the same file where the createComponents.m function file is saved.
But when I click a different radio, error messages are as following:
Undefined function 'controlBtnGroupSelectionChanged' for input
arguments of type 'matlab.ui.container.ButtonGroup'. Error while
evaluating ButtonGroup SelectionChangedFcn
I also declare the controlBtnGroupSelectionChanged function as a private method in my class.
The controlBtnGroupSelectionChanged function is as following:
function controlBtnGroupSelectionChanged(obj, eventData, app)
% Update display section as the radio button in control section is changed
%new = app.ControlButtonGroup.NewValue;
new = obj.SelectedObject.String;
switch new
case 'Transfer Path'
% display the transfer path
imagesc(app.curImage,'Parent',app.DisplayPath);
app.DisplayPath.Visible = 'off';
case 'Store'
% display the store text
text(curStore,'Parent',app.DisplayPath);
case 'Mill'
% Display the Mill text
text(curMill,'Parent',app.DisplayPath);
end
end
The part of that defined in class is as :
methods (Access = private)
% Create UIFigure and components
createComponents(app);
% Callback function for Control Button Group Selection Changed
controlBtnGroupSelectionChanged(obj, eventData, app);
end
And The part related to that in createComponents function is:
app.ControlButtonGroup = uibuttongroup(app.UIFigure);
app.ControlButtonGroup.Title = 'Control';
app.ControlButtonGroup.FontSize = 16;
app.ControlButtonGroup.Units = 'Normalized';
app.ControlButtonGroup.Position = [0.45 0.75 0.45 0.2];
app.ControlButtonGroup.SelectionChangedFcn = {#controlBtnGroupSelectionChanged,app};
Why does it say that my function is undefined?
UPDATE:
I have gone through some other demos, and changed the callback as app.ControlButtonGroup.SelectionChangedFcn = #(h, e)controlBtnGroupSelectionChanged(app). And define(also prototype) it just as controlBtnGroupSelectionChanged(app). It works then. so I guess the problem should be about the number of parameters, but I still don't find much detailed information on that. Any hints are appreciated!!
You actually need to supply the object as the first argument to the function so that MATLAB knows to look for it as a method of app.
app.ControlButtonGroup.SelectionChangedFcn = #(s,e)controlBtnGroupSelectionChanged(app, s, e);
% Or this way which implicitly passes the two input parameters
app.ControlButtonGroup.SelectionChangedFcn = #app.controlBtnGroupSelectionChanged;
Then your method would look like this
function controlBtnGroupSelectionChanged(obj, source, event)
If you don't need the source (the handle to the control button group) or event (the eventdata associated with the selection event), then you can have your callback "ignore" these second and third inputs by doing something like
app.ControlButtonGroup.SelectionChangedFcn = #(s,e)app.controlBtnGroupSelectionChanged();
And your method would look like
function controlBtnGroupSelectionChanged(app)

Growing a struct with GUI

I've been working on a little custom made database in MATLAB.
I have a GUI with a bunch of 'Edit Text' boxes and buttons.
The key is that I should be able to register an undefined number of students with some information like names, surnames, code etc. I've managed to store only one student (i.e the first time i push the 'Submit Button') but when i enter another student's information, MATLAB just overwrites the information from the previous registration.
Here's the Callback for the 'Submit' button
function Submit_Callback(hObject, eventdata, handles)
global n
n=n+1
% hObject handle to Submit (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
name1 = get(handles.name,'String'); %edit1 being Tag of ur edit box
name2=get(handles.name2,'String');
name3=get(handles.name3,'String');
major=get(handles.major,'String');
labavg=num2str(get(handles.labavg,'String'));
finalgrade=num2str(get(handles.finalgrade,'String'));
email=num2str(get(handles.email,'String'));
code=num2str(get(handles.code,'String'));
for ii=1:numel(n)
student_information(ii).name=name1
student_information(ii).surname1=name2
student_information(ii).surname2=name3
student_information(ii).code=code
student_information(ii).major=major
student_information(ii).final_grade=finalgrade
student_information(ii).laboratory_average=labavg
student_information(ii).email=email
end
assignin('base', 'student_information', student_information)
end
I've declared the counter 'n' as a global variable set to '0' in the workspace.
I'm not sure if my code isn't looping properly. Maybe the mistake is in there but I can't see how to fix it.
Can you please help me with my code?
Thanks!
I'm not sure what you were trying to achieve with your loop, but I don't see the need for it. Also, by using assignin, you are overriding the contents of student_information in your workspace. You are better off making student_information global in Submit_Callback in addition to n, then construct a new_student structure using your information and append it to student_information as follows:
name1 = get(handles.name,'String'); %edit1 being Tag of ur edit box
name2=get(handles.name2,'String');
name3=get(handles.name3,'String');
major=get(handles.major,'String');
labavg=num2str(get(handles.labavg,'String'));
finalgrade=num2str(get(handles.finalgrade,'String'));
email=num2str(get(handles.email,'String'));
code=num2str(get(handles.code,'String'));
new_student.name = name1;
new_student.surname1 = name2;
new_student.surname2 = name3;
new_student.major = major;
new_student.laboratory_average = labavg;
new_student.final_grade = finalgrade;
new_student.email = email;
new_student.code = code;
student_information(n) = new_student;
n = n + 1;
and that should append the new entry at the end of the struct array.

Loop in GUI for many trials

Initially my prog is like that (playing sound and judge what kind of sound is) :
n_repetition=10
for i=1:n_repetition
playsound(strcat(i,'wav'));
answer=input(' answer q/z/e ?','s');
switch answer
case 'q'
asw="bird";
case 'z'
asw="water";
case 'e'
asw="wind";
otherwise
disp('error');
end
end
Now I'm trying to make it more interactive with GUI, I'm using GUIDE and i've generate a .fig which contain 4 buttons: OK button, BIRD, WATER, WIND
I've also my callbacks which are empty now
What I want to do is:
-Initially all buttons are inactive
-Participant should press on ok to begin
-play sounds
-activate the buttons (sound bird, water, wind)
-catch response
-deactivate button
-wait for press ok for new trial
How could I adapt my initial code the the callback, where should I put my loop ? Thanks
Add these at the start of guiname__OpeningFcn -
handles.song_count = 0;
handles.asw = cell(10,1);
Edit your button callbacks to these -
% --- Executes on button press in ok_button.
function ok_button_Callback(hObject, eventdata, handles)
handles.song_count = handles.song_count +1;
filename = strcat(num2str(handles.song_count),'.wav');
[y,~] = audioread(filename);
%%// Use soundsc or your custom playsound function to play the sounds
soundsc(y); %playsound(strcat(i,'wav'));
guidata(hObject, handles); %%// Save handles data
return;
% --- Executes on button press in bird_button.
function bird_button_Callback(hObject, eventdata, handles)
asw = 'bird'; %%// Do something with 'asw'
handles.asw(handles.song_count) = {asw}; %%// Store the 'asw' values as a cell array
guidata(hObject, handles); %%// Save the handles data
return;
% --- Executes on button press in water_button.
function water_button_Callback(hObject, eventdata, handles)
asw = 'water'; %%// Do something with 'asw'
handles.asw(handles.song_count) = {asw}; %%// Store the 'asw' values as a cell array
guidata(hObject, handles); %%// Save the handles data
return;
% --- Executes on button press in wind_button.
function wind_button_Callback(hObject, eventdata, handles)
asw = 'wind'; %%// Do something with 'asw'
handles.asw(handles.song_count) = {asw}; %%// Store the 'asw' values as a cell array
guidata(hObject, handles); %%// Save the handles data
return;
Note: At any point of time view handles.asw to look at the button clicks history.
Suggestion: If it's okay to show the choices made by the GUI user as a list, you might consider adding a Table into the GUI. You can put the data from handles.asw into such a Table.

Matlab: Timer encounters error on non-structure array

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.