Save data structure in existing .mat-file without nesting - matlab

I have created a simple GUI that when I press a button (SAVE), the system takes the value of some fields, calculates a certain function and saves the results in a data structure.
I would like this data structure to be external to the program, that is to say it remains at the exit of Matlab and at the next opening of the program this data structure must be available and upgradable.
To do this I used a global data variable that I save and load when needed.
The problem is that it doesn't work properly, the data structure is filled strangely.
I show you a gif that is perhaps more explanatory:
The saved structure is this:
As you can see, there is a structure within the other and not a list of elements. Why?
I would like to have a data structure that contains n elements (where n is the number of images) and each element consists of 9 fields (name, category, siftOctaves, siftLevels, siftPeak, siftEdge, numFeatures, siftFeatures, siftDescriptors).
This is a piece of code:
%% SAVE BUTTON
function pushSiftSave_Callback(hObject, eventdata, handles)
% hObject handle to pushSiftSave (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global data;
try
% Vector of string = name of all possible images
imgs = createListOfImages('../img/');
% Get selected image
imgName = get(handles.listbox, 'Value');
imgPath = strcat('../img/', imgs(imgName));
imgPath = imgPath{1};
I_or = imread(imgPath);
I = single(rgb2gray(I_or));
% Get some parameters enter by user
[siftOctaves, siftLevels, siftPeak, siftEdge] = takeSiftParameters(handles.editSiftOctaves, handles.editSiftLevels, handles.editSiftPeakTh, handles.editSiftEdgeTh, I_or);
% Sift function
[f, d] = vl_sift(I, 'Octaves', siftOctaves, 'Levels', siftLevels, 'PeakThresh', siftPeak, 'EdgeThresh', siftEdge);
% Number of features
perm = randperm(size(f, 2));
numFeatures = size(perm, 2);
% Check if file exists
if exist('../data/data.mat', 'file') == 2
data = load('../data/data');
else
data = struct;
end
% Insert information in data structure
data = saveSiftInformation(data, imgs, imgPath, siftOctaves, siftLevels, siftPeak, siftEdge, f, d, numFeatures);
catch
ErrorMessage = lasterr;
msgbox(ErrorMessage);
disp(ErrorMessage);
end
function [data] = saveSiftInformation(data, imgs, imgPath, siftOctaves, siftLevels, siftPeak, siftEdge, features, descriptors, numFeatures)
imgPath = imgPath(8 : end);
% Find index of image
i = find((ismember(imgs, imgPath)));
% Update data structure
data(i).name = imgPath;
data(i).category = imgPath(1 : end-6);
data(i).siftOctaves = siftOctaves;
data(i).siftLevels = siftLevels;
data(i).siftPeak = siftPeak;
data(i).siftEdge = siftEdge;
data(i).numFeatures = numFeatures;
data(i).siftFeatures = features;
data(i).siftDescriptors = descriptors;
% Save data
save('../data/data', 'data');
end
%% SAVE & QUIT BUTTON.
function pushQuit_Callback(hObject, eventdata, handles)
% hObject handle to pushQuit (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global data;
assignin('base', 'data', data);
Thanks!

The problem you're facing is generated when you load the data.mat file.
Also, using data as the name of variable reurned by load contributes to generating confusion.
The instruction
data = load('../data/data')
reads the data.mat and stores the "data" it contains in a struct named data therefore, your struct is actually a filed of the struct data returned by load.
You can test it by setting a breakpoint just after the load call and inspecting the varialbe data.
You can fix the problem by extracting the data field from the structure when loading the .mat file.
if(exist('data.mat', 'file') == 2)
% data = load('data');
tmp = load('data');
data=tmp.data
else
data = struct;
end
Hope this helps,
Qapla'

By using the output variable of the load function you are storing the variable data from your .mat-file to a struct called data - so it gets nested. Just use load without any output and it will work.
if exist('data.mat', 'file') == 2
%// data = load('data'); % does not work!
load('data'); % does work!
else
data = struct;
end
data(1).a = 42;
data(2).a = 3;
data(1).b = 41;
data(2).b = 4;
%// make sure you just store what you want to store, in this case "data"
save('data.mat','data')
Also I would avoid declaring data a global variable. Instead you could use the fact, that all functions of your GUI are part of the same figure window and therefore have the figure handle available:
hFig = gcf;
It is further allowed to add dynamic properties do your instance of figure, so just store your data in the figure handle itself:
hFig = gcf;
addprop(hFig,'globalData')
data = struct;
hFig.globalData = data;
% ...
and in the next function you just do:
data = hFig.globalData

Related

How to get a character in a matrix using uitable

I'm creating a function that lets an user add or remove his/her input to the list. In this case, uitable. (Matlab)
In fact, the following code was a draft and just thinking about ideas.
Does anybody know a similar example?
(edit_com : add, delete_com : remove)
% to add an input to a list
function edit_com_Callback(hObject, eventdata, handles)
value = get(handles.insert_com, 'String'); %user input (char)
data = get(handles.uitable1, 'Data') % read table matrix
data(end+1,:) = 0; % add below the data matrix sequently.
% this is for test and I want to put an char input instead of numbers.
set(handles.uitable1, 'Data',data);
% cell selection function before a delete function
function uitable1_CellSelectionCallback(hObject, eventdata, handles)
% no selection
if ( numel(eventdata.Indices) == 0 )
% only one selected
elseif ( numel(eventdata.Indices) == 1 )
set(handles.delete_com, 'Enable', 'on'); % "delete" buttion activate
selected_com = eventdata.Indices(1); % read currently selected row
set(handles.edit_com, 'UserData', selected_com);
% more than 2 selected
else
set(handles.delete_come, 'Enable', 'on'); % "delete" buttion activate
selected_com = eventdata.Indices(:,1); % read currently selected rows
set(handles.edit_com, 'UserData', selected_com);
end
% deletion part
function delete_com_Callback(hObject, eventdata, handles)
if get(handles.edit_com, 'UserData') ==0 % none selected
else if get(handles.edit_com, 'UserData') ==1 % one selected
data = get(handles.uitable1, 'Data') %
data(row_1,:)=[]; % delete
else % more than one selected
data = get(handles.uitable1, 'Data')
data(row_2,:)=[]; % delete them
end
first it separates several cells from the cell and then separates them (separated by user data). I'm just trying to figure out which column is selected, and I try to erase it from a function. It's hard to deal with it
because it consists of characters and I don't know how to handle cells. What's the best way to get started?
If you want to use strings (character arrays) inside pass to your uitable a Data parameter being of type cell, for example;
data = cell(10,10); % a 10-by-10 empty cell matrix
set(handles.uitable1,'Data',data);
or:
data = repmat({'hi'},10,10); % a 10-by-10 matrix of cells containing the string "hi"
set(handles.uitable1,'Data',data);
For what concerns the main function:
% Here you should disable your delete button when nothing is selected,
% and enable it back when something is selected. Your indices data should
% always be kept up-to-date. You don't need to handle too many cases,
% "something selected" and "nothing selected" is enough.
function uitable1_CellSelectionCallback(hObject,eventdata,handles)
if (isempty(eventdata.Indices))
set(handles.delete_com,'Enable','off');
rows = [];
else
set(handles.delete_com,'Enable','on');
rows = eventdata.Indices(:,1);
end
set(handles.edit_com,'UserData',rows);
end
Now, for what concerns the addition of rows:
function edit_com_Callback(hObject, eventdata, handles)
value = get(handles.insert_com,'String');
data = get(handles.uitable1,'Data');
data(end+1,:) = {value};
set(handles.uitable1,'Data',data);
end
And for what concerns the removal of rows:
function delete_com_Callback(hObject, eventdata, handles)
data = get(handles.uitable1,'Data');
rows = get(handles.edit_com,'UserData');
idx = (1:size(data,1))';
idx(rows) = [];
data = data(idx,:);
set(handles.uitable1,'Data',data);
end

MATLAB GUI: updating variables to mat file

I have program where at the end a GUI is launched. I built it using guide. I load 4 variables from my program into 4 GUI text boxes using a mat file called n.mat (and a pushbutton feature).
In the program
n = [nuno, ndue, ntre, nquattro];
save n.mat
In the GUI interface pushbutton
% --- Executes on button press in upload.
function upload_Callback(hObject, eventdata, handles)
% hObject handle to upload (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
S = load('n.mat');
handles.v1 = S.nuno;
handles.v2 = S.ndue;
handles.v3 = S.ntre;
handles.v4 = S.nquattro;
set(handles.initial1,'String',num2str(handles.v1));
set(handles.initial2,'String',num2str(handles.v2));
set(handles.initial3,'String',num2str(handles.v3));
set(handles.initial4,'String',num2str(handles.v4));
guidata(hObject, handles);
Then I have other 4 text boxes where I change the value of the variables and save them in another mat file. I'm not sure if I'm doing this correctly.
In the program (before calling myGUI) I initialize the m vector for the updated variables.
nunof = 0;
nduef = 0;
ntref = 0;
nquattrof = 0;
m = [nunof, nduef, ntref, nquattrof];
save m.mat
In the program (after calling myGUI) I try and load the m.mat file and extract the variables from it so I can use them in some calculations further in the program.
load m.mat;
nunof = m.nunof;
nduef = m.nduef;
ntref = m.ntref;
nquattrof = m.nquattrof;
Before this, in the GUI interface 'done' button I try and save my inputs into the m.mat file like this:
function done_Callback(hObject, eventdata, handles)
% hObject handle to done (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% save the parameters to file
load('m.mat');
m = [nunof, nduef, ntref, nquattrof];
nunof = str2num(get(handles.final1,'String'));
nduef = str2num(get(handles.final2,'String'));
ntref = str2num(get(handles.final3,'String'));
nquattrof = str2num(get(handles.final4,'String'));
save('m.mat','-append');
I want to know why this isn't working and how can I change it. Thanks a lot.
You can't use save('m.mat','-append');. You're missing an option to get to append.
In order to use append you have to declare a filename, the variable and then append.
save(filename,variables,'-append')
Taken from - https://au.mathworks.com/help/matlab/ref/save.html
Also, from your code you're not redefining the variables in your m struct.

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.

matlab gui handles for unknown matrix size

I am writing a GUI now which contains a button that load a matrix:
function Load_Profile_Callback(hObject, eventdata, handles)
% hObject handle to Load_Profile (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
[FileName PathName] = uigetfile('*.mat','MATLAB Files');
handles.matrix=importdata([PathName FileName]);
next, i want to define each column of this matrix to be different channel for example:
handles.Ch01Gr01=handles.matrix.Data(:,2);
handles.Ch01Gr02=handles.matrix.Data(:,3);
handles.Ch01Gr03=handles.matrix.Data(:,4);
handles.Ch01Gr04=handles.matrix.Data(:,5);
handles.Ch01Gr05=handles.matrix.Data(:,6);
handles.Ch01Gr06=handles.matrix.Data(:,7);
handles.Ch01Gr07=handles.matrix.Data(:,8);
In case i dont know how many columns there are in this matrix,
is there any option to solve this in for loop (or anyother idea will be good aswell )to run on this matrix dimension?
You can create a cell array for channel:
numch = size(handles.matrix, 2);
for i = 1:numch
handles.Ch01Gr{i} = handles.matrix.Data(:, i);
end
You can check for the number of column before entering the loop and then create dynamic fields to name them according to the loop index (Check here).
Here is a sample GUI in which pressing the button results in loading matrix A (a magic 4x4 matrix...A = magic(4)), which I stored in file 'A.mat'.
function LoadDataGUI
clc
clear
hfigure = figure('Position',[200 200 100 100]);
handles.LoadButton = uicontrol('Style','push','Position',[50 50 50 20],'String','Load','Callback',#(s,e) LoadDataCllbck);
guidata(hfigure,handles);
function LoadDataCllbck
handles = guidata(hfigure);
%// Load matrix. A is actually a magic(4) matrix.
handles.Data = load('A.mat');
%// Check # of columns
NumCol = size(handles.Data.A,2);
for k = 2:NumCol
%// Create dynamic field name
CurrField = sprintf('Ch01Gr%i',k-1);
%// Assign it to the handles structure.
handles.(CurrField) = handles.Data.A(:,k);
end
guidata(hfigure,handles);
end
end
Here, CurrField look like this at every iteration:
CurrField =
Ch01Gr1
CurrField =
Ch01Gr2
CurrField =
Ch01Gr3
You can customize the format as you want with sprintf of course.
After pressing the button, here is the content of the handles structure:
LoadButton: 329.0085
Data: [1x1 struct]
Ch01Gr1: [4x1 double]
Ch01Gr2: [4x1 double]
Ch01Gr3: [4x1 double]
Don't forget the good practice of pre-allocating memory, especially if your data is large.
Hope that helped!

How to save data ceated in for loop to an array? Matlab

I am using a for loop to save the path and names of multiple files I wish to process using a GUI in matlab. My code is as follows:
% --- Executes on button press in MultiSelect.
function MultiSelect_Callback(hObject, eventdata, handles)
% hObject handle to MultiSelect (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
[FileName,PathName,FilterIndex] = uigetfile('*.txt*','Study Files','MultiSelect','on');
Cols = size(FileName,2);
numfiles = Cols;
for i = 1:numfiles
file{i} =fullfile(PathName,FileName{i});
end
handles.gettinginfo = file ;
guidata(hObject,handles)
Fid = fopen(file{i});
data1 = handles.gettinginfo(2);
% for ii = 1:numfiles
B = [];
fid = fopen(data1);
tline = fgets(fid);
while ischar(tline)
parts = textscan(tline, '%f;');
if numel(parts{1}) > 0
B = [ B ; parts{:}' ];
end
tline = fgets(fid);
end
I'm having an issue when I try to use data1 with fopen, it gives me the error:
Error using fopen
First input must be a file name of type char, or a file identifier of type double.
But data1 = handles.gettinginfo(2); gives me the path and name of the file of the second selected file so I don't really understand why it fopen does not work.
I would like to save the selected files that is created in the for loop to a global array. What is the best way to do this? When I try to use file{i} to import the data from the file etc, it overwrites the other files and only produces info on the last file but I think the global array may help with this issue.
Many thanks in advance.