Growing a struct with GUI - matlab

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.

Related

How to pass the value of a table to a matrix

Im trying to do a GUI in matlab that accepts the values in a table to converting it to a matrix, but the idea is that a user can set the number of rows and columns first.
The panel looks like this
and the code for the push button is
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
rows =str2double(get(handles.edit_rows,'String'));
cols=str2double(get(handles.edit_cols,'String'));
num_elem=cell(rows,cols);
num_elem(:,:)={"};
set(handles.uitable1,'Data',num_elem)
set(handles.uitable1,'ColumnEditable',true(1,cols))
But then, how can export or convert to a matrix so I can apply functions to it?
UPDATE
With the help of byetisener I updated the code to
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
filas=str2double(get(handles.edit_fila,'String'));
column=str2double(get(handles.edit_col,'String'));
num_elem=cell(filas,column);
num_elem(:,:)={''};
set(handles.uitable1,'Data',num_elem)
set(handles.uitable1,'ColumnEditable',true(1,column))
handles.uitable1.Data = cell(filas, column);
matrix = cell2mat(handles.uitable1.Data);
matrix
but this is giving an empty matrix
It is not taking the values of the cells, it is supposed that the button resizes and copy the values at the same time, if not how con copy in another button once the matrix is resized?
There are some problems about your code:
You do not really assign values here, you are just setting the Data of the uitable to an array of empty cells.
num_elem =
1×2 cell array
{0×0 char} {0×0 char}
If you ever succeed, you code will write everything you want to only the first column of the uitable. Because you are not iterating through rows. The pushbutton only adds to the first row.
cell2mat() function won't work if you have different data types in your table. You may think that you do not have different data types, but empty cells are type cell and the data you enter is type double, so there it is.
To solve all of this I have rewritten a callback function for you. You may directly paste this code to your callback, replacing yours. I should give you the matrix you want at the end, it does in my computer.
filas = str2double(handles.edit_fila.String);
column = str2double(handles.edit_col.String);
% This loop looks for an empty row to write new data
for i = 1:length(handles.uitable1.Data)
if isempty(handles.uitable1.Data{i,1})
handles.uitable1.Data(i,1) = {filas};
handles.uitable1.Data(i,2) = {column};
break;
else
disp('Error occured');
end
end
% This double for loop check if there are any empty cells
% if it finds one, it changes it to 0, so all the cells have the same type
for i = 1:length(handles.uitable1.Data)
for j = 1:2
if isempty(handles.uitable1.Data{i,j})
handles.uitable1.Data(i,j) = {0};
else
disp('Error occured');
end
end
end
matrix = cell2mat(handles.uitable1.Data); % The matrix you want
Just check if all the variable names are the same and do not forget to accept is as an answer. Hope it helps.
I am not sure if this answers you question but you can follow this approach.
First of all, if you are interested, using dot notation is faster in MATLAB than setter and getter methods.
So, what you can do is:
handles.uitable1.Data = cell(rows, cols);
or, of course, alternatively:
set(handles.uitable1, 'Data', cell(rows,cols));
If what you want to is to convert the data in uitable to a matrix, you can use:
matrix = cell2mat(handles.uitable1.Data);
If you table contains non-numeric values:
tableData = handles.uitable1.Data;
tableData = [str2double(tableData(:, 1)), cell2mat(tableData(:, 2))];
Hope this helps. Let me know if you solve your problem.

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)

writing elements in matlab listbox

I am trying to write a list in a listbox.
code:
function listbox1_Callback(hObject, eventdata, handles)
% hObject handle to listbox1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: contents = cellstr(get(hObject,'String')) returns listbox1 contents as cell array
% contents{get(hObject,'Value')} returns selected item from listbox1
error = getappdata(0, 'error_norm');
rows = size(error,1);
for i = 1:rows
set(handles.listbox1,'string',strcat('filt_',num2str(i)));
for j = 1:length(error)
set(handles.listbox1,'string',strcat('sig_',num2str(i),'_',num2str(j)));
for k = 1:length(error{j}.main)
set(handles.listbox1,'string',strcat('seg_',num2str(i),'_',num2str(j),'_',num2str(k)));
end
end
end
Where error is a array of structure, this array contains filters, singals in these filters, segments of these signals. based on the number of all these components, i want to write the list. I want to write something like this in the listbox:
filt_1
sig_1_1
seg_1_1_1
seg_1_1_2
sig_1_2
seg_1_2_1
seg_1_2_2
But apparently, 'set' function overwrites the elements, so all i am getting is 1 element and the last element.
Any suggestion to how to overcome this problem will be appreciated.
Yes, since set always overwrites the string, it is better to firstl build the string and then pass it to set.
Example
% Sample data
rows=4;
error=cell(1,5);
for i=1:length(error)
error{i}.main=rand(1,4);
end
% Build string
str={};
for i=1:rows
str{end+1}=sprintf('filt_%i',i);
for j=1:length(error)
str{end+1}=sprintf('sig_%i_%i',i,j);
for k=1:length(error{j}.main)
str{end+1}=sprintf('seg_%i_%i_%i',i,j,k);
end
end
end
% Set data
set(handle.listbox1,'String', str);
Depending on the size of the final string it might be a good idea to preallocate str for performance.

BytesAvailableFcn callback not receiving updated handles

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

handles are getting deleted

I have 3 row-vectors, and would like to output them out of my GUI, if I close it.
First I tried it with global variables in the GUI, which i access and put them in the output function:
global horizontalFrames;
global verticalFrames;
global blackFrames;
varargout{1} = horizontalFrames;
varargout{2} = verticalFrames;
varargout{3} = blackFrames;
But, all values are ALREADY AT THIS POINT zero, even if i set them in the code.
Why are they set zero?
Then I tried it by the use of handles like this:
handles.horizontalFrames = horizontalFrames;
handles.verticalFrames = verticalFrames;
handles.blackFrames = blackFrames;
somewhere in the code (where the values are NOT all zero)
and then write this in the output function:
varargout{1} = handles.horizontalFrames
varargout{2} = handles.verticalFrames
varargout{3} = handles.blackFrames
the handles cant be found. It seems, like the handles are not accessible from the output function, or they are deleted. I'm desperate... where is my data, why can't I simply output it?
I found it out. I had to update my handles with
guidata(hObject, handles);
"somewhere in the code" again, means, immediately after adding my variables.
Greets, hope it helps someone.