I am making a GUI programmatically and have run into a small question regarding the uicontrol listbox and the data each 'value' or 'string' refers to. I feel this question will best be illustrated with some code. The code at the end of this question illustrates my question.
If you run this example, select all 5 files from the left listbox and press 'Button1', you will see them go into the right listbox. You can then select 1, or several of the files in the right list box and press 'Button2' and MATLAB will output the correct files names. All is fine here.
If you close and re run the program, and just select file1, file3, and file5, pressing Button1 will make them go into the right list box again. This is where my problem is: If you select all of the files now in the right list box (file1, file3, file3), and press Button2, matlab outputs file1, file2, and file3... not file1, file3 and file5 as I would like.
Now I understand why this is happening, because in a list box its value property starts at 1 and increases, and the underlying data in the Cell DataSet remains ordered file1 to file5... So value 1 2 3 refers to DataSet{1} DataSet{2} DataSet{3}...
What would be the best way to overcome this problem? I would like to add that for my actual GUI the file names might not always be so obviously named.
classdef example < handle
properties
Figure;
Button1;
Button2;
ListBox1;
ListBox2;
DataSet = {};
end
methods
function obj = example()
create(obj);
makeUpData(obj);
end
function create(obj)
obj.Figure = figure('Position',[300 300 640 640]);
obj.Button1 = uicontrol('Style','pushbutton','String','Button1',...
'Position',[240 260 80 40],'Callback',#obj.button1CB);
obj.Button2 = uicontrol('Style','pushbutton','String','Button2',...
'Position',[510 260 80 40],'Callback',#obj.button2CB);
obj.ListBox1 = uicontrol('Style','listbox','String','',...
'Position',[50 250 145 100],'Max',3);
obj.ListBox2 = uicontrol('Style','listbox','String','',...
'Position',[350 250 145 100],'Max',3);
end
function makeUpData(obj)
obj.DataSet = {'file1' 'file2' 'file3' 'file4' 'file5'};
obj.ListBox1.String = obj.DataSet;
end
function button1CB(obj,hObject,eventdata)
CurrentNum = get(obj.ListBox1,'Value');
NameListBox1 = get(obj.ListBox1,'String');
NewName = obj.ListBox2.String;
for i = 1:numel(CurrentNum)
NewName{end+1} = [NameListBox1{CurrentNum(i)}];
end
obj.ListBox2.String = NewName;
end
function button2CB(obj,hObject,eventdata)
CurrentNum = get(obj.ListBox2,'Value')
for i = 1:numel(CurrentNum)
obj.DataSet{CurrentNum(i)}
end
end
end
end
For this application I would recommend storing your data in a structure rather than a cell array. This will allow you to utilize dynamic field references to access your data and not worry about converting from a file name to an index in your cell array.
I've modified your properties definition:
properties
Figure;
Button1;
Button2;
ListBox1;
ListBox2;
DataSet = struct;
end
Your makeUpData definition:
function makeUpData(obj)
dummydata = {'file1' 'file2' 'file3' 'file4' 'file5'};
for ii = 1:length(dummydata)
obj.DataSet.(dummydata{ii}) = dummydata{ii};
end
obj.ListBox1.String = fieldnames(obj.DataSet);
end
And your button2CB definition:
function button2CB(obj,hObject,eventdata)
listboxvalues = get(obj.ListBox2, 'String');
CurrentNum = get(obj.ListBox2,'Value');
for i = 1:numel(CurrentNum)
obj.DataSet.(listboxvalues{CurrentNum(i)})
end
end
Now your selection returns:
ans =
file1
ans =
file3
ans =
file5
As expected.
You can change callback function of Button2 as follows, using ismember to get the corresponded index in the obj.DataSet:
function button2CB(obj,hObject,eventdata)
selected = cellstr(get(obj.ListBox2, 'String'));
referred = ismember(obj.DataSet, selected);
obj.DataSet(referred)
obj.DataSet{referred}
end
Related
I am developing a GUI where I want the input in text or excel format from user. When he will click on "Upload File" button from my GUI, the file browser will open and he will select the text file. Once he clicks open the file should be in workspace so my next code will take the value give results.
What I did is: Have this code under the push button
[filename,pathname] = uigetfile('*.txt')
loaddata = fullfile(pathname,filename)
data = load(loaddata)
A = data(:,1)
B = data(:,2)
C = data(:,3)
D = data(:,4)
handles.input1 = A;
handles.input2 = B;
handles.input3 = C;
handles.input4 = D;
Now when the Browser opens, I can select .txt file which is having 4 columns and 2000 rows of data. But when I go back to workspace, I can't see anything in workspace but all values from 2nd column in command window!
You can use setappdata and the associated getappdata to store application-defined data and retrieve it from elsewhere, for example from a GUI or the base workspace.
In your case, you could store A, B, C and D in the base workspace so they would be available from your other scripts. You can also store the handles structure if you want.
As an example, your code in the GUI could look like this:
[filename,pathname] = uigetfile('*.txt')
loaddata = fullfile(pathname,filename)
data = load(loaddata)
%// Store in the base workspace (i.e. the "0")
setappdata(0,'AllData',data);
Note that you could write anything you want as a name, as long as you retrieve the variable with the same name using getappdata. Here I used AllData but you could leave that as data just as well.
Therefore, in the other script you are running, use getappdata like so:
DataInScript = getappdata(0,'AllData');
A = AllData(:,1)
B = AllData(:,2)
C = AllData(:,3)
D = AllData(:,4)
So now, depending on whether you already assigned A, B, C and D you can access your data directly from AllData.
In listbox1, I have a number of data, now I select some of them and click push button, those selected data can be shown in listbox2. Then I go on selecting data in listbox1 and clicking push button, the old data in listbox2 disappear and new data is shown in listbox2. here comes the code in callback funcion:
function pushbutton1_Callback(hObject, eventdata, handles)
list_entries = get(handles.listbox1,'String');
index_selected = get(handles.listbox1,'Value');
Length = length(index_selected);
Newlist = list_entries;
for i=1:Length
n = index_selected(i);
handles.element(i) = list_entries(n)
if i==1
Newlist(n) = []
else
Newlist(n-1) = []
end
end
set(handles.listbox1,'String',Newlist);
set(handles.listbox2,'String',handles.element);
Help me to solve this, thanks a lot.
If you simply want to replace the data in a listbox (i.e. the String) by some other string, you don't have to go through the process of actually deleting the old data. You can simply assign the String from listbox 1 to listbox 2 and you're good to go.
function pushbutton1_Callback(hObject, eventdata, handles)
set(handles.listbox2,'String',get(handles.listbox1,'String'));
EDIT Following your comment, it seems that you want to append new data/string (from listbox 1) to the existing string in listbox 2. You can concatenate the cell array of strings easily as follows:
Let's say the strings in listbox 1 and listbox 2 are obtained as usual:
String1 = get(handles.listbox1,'String');
String2 = get(handles.listbox2,'String');
For example String1 = {'A' 'B' 'C'} and String2 = {'D' 'E' 'F'}
then you can concatenate the cell arrays and set the string property of listbox2 like so:
NewString = [String1 String2]
giving this:
NewString =
'A' 'B' 'C' 'D' 'E' 'F'
So then use this
set(handles.listbox2,'String',NewString);
and you're good to go.
Hope it's okay now.
I am writing a program and I am using listdlg. I want for each selection of the list to do the same thing BUT it will save them in different part(so that every option will have it's own - let's say- sub folder with its text files and they can be accessed for another function.
So this is my listdlg
global fileCount
F = listdlg('PromptString','Different types', 'SelectionMode',...
'single', 'ListString',{E}, 'Name','Select a type','ListSize',[230 130]);
where {E} is user's input, which might be 3 rows or 6 rows, how many he likes.
So I want if he uses the first row to ask for input and then save it for the first type
if F == 1
[file,path] = uigetfile ('*.txt','Select your text files',...
'MultiSelect','on');
file = cellstr(file);
for k = 1:length(file)
fileCount = length (file);
z = importdata(fullfile(path, file{k}));
end
end
the same will be done for the following types, meaning if he chooses the 2nd then the files will be saved for the 2nd file, but the files of the first type will not be overwrote. So he now has let's say Orange-10files; Pink-2files and Yellow-4files.
Is there a way I can do that? except using if and elseif for every of his choice?
I hope I was clear enough!
Thanks!
Assign E as a cell array rather than inserting it as a cell array in the listdlg call. I'm not entirely clear on your end goal, but this will take the user's selection of E's elements, open whatever files the user selects, and return the path and file name of those files with the added "color" folder:
E = {'Orange','Pink','Yellow'};
F = listdlg('PromptString','Different types', 'SelectionMode',...
'single', 'ListString',E, 'Name','Select a type','ListSize',[230 130]);
[files,path] = uigetfile ('*.txt','Select your text files',...
'MultiSelect','on');
files = cellfun(#(x) fullfile(path,E{F},x),files,'UniformOutput',false);
I have a code for "for loop"
for i=1:4
statement...
y=sim(net, I);
end
now i need to export the value of y to excel sheet. for that i used..
xlswrite('output_data.xls', y, 'output_data', 'A1')
but my problem is that the ID of excel i.e. "A1" should change according to each iteration... in my case for iteration 1-> A1, iteration-> A2 and so on..
anybody please help me out ..
thanks in advance. for any assistance.. or suggestion..
You can store sim outputs in a vector (y(ii)) and save in the sheet with a single write. This is also more efficient since you perform a single bulk-write instead of many small writes.
Specify the first cell and y will be written starting from there.
last = someNumber;
for i=1:last statement... y(i)=sim(net, I); end
xlswrite('output_data.xls', y', 'output_data', 'A1');
If you prefer specify the range write ['A1:A',num2str(last)] instead of A1.
If you really want to write within the loop try:
for ii=1:last
...
y=sim(net, I);
xlswrite('output_data.xls', y, 'output_data', sprintf('A%d',ii));
end
You can also do for yourself what xlswrite does internally, which is interact using COM. I prefer to do this when I have a frequently used excel template or data file, because it allows for more control (albeit with more lines of code).
Excel = actxserver('Excel.Application');
Workbook = Excel.Workbooks.Open('myExcelFile.xlsx');
MySheet = Excel.ActiveWorkBook.Sheets.Item(1);
set( get(MySheet,'Range','A1:A10'), 'Value', yourValues);
...
invoke(Workbook, 'Save');
invoke(Excel, 'Quit');
delete(Excel);
This would allow you to save new data to new ranges without re-opening excel each time.
Even better would be to define an oncleanup function (as does xlswrite) to prevent lost file locks (especially when you're doing things like exiting out of debug mode):
...
myWorkbook = Excel.Workbooks.Open(filename,0,true);
cleanUp = onCleanup(#()xlsCleanup(Excel, filename));
function xlsCleanup(Excel,filepath)
try
Excel.DisplayAlerts = 0; %// Turn off dialog boxes
[~,n,e] = fileparts(filepath); %// Excel API expects just the filename
fileName = [n,e];
Excel.Workbooks.Item(fileName).Close(false);
end
Excel.Quit;
end
You can put xlswrite after for loop.You just want to do is save you result in a matrix.This function can write a matrix.
also,you can use [] to combine string to change the range.
>> for i=1:4
Range=['A' num2str(i)]
end
Range =
A1
Range =
A2
Range =
A3
Range =
A4
But,this is a bad way.You should open and write Excel file every time.
I have written a function that takes the names and values of the input variables and writes them to a file. eg.
a = 10;
b = 100;
writevars('file.txt',a,b);
gives me a file file.txt that contains:
\def\a{\num{10}}
\def\b{\num{100}}
It would now like to be able to pass on all variables that are found using the who command. Eg if who returns:
a b z
I would like to be able to use writevars as if I called writers('file.txt', a, b, z).
The main problem I have is that writevars makes use of inputname... (temporary variables won't work e.g. writevars('file.txt', 100) doesn't work since there is no name to be given in the file).
ANSWER
var_names = who;
for i = 1 : length(var_names)
evalin('caller',['writevars(''file.txt'', ' char(var_names(i)) ' )']);
end
You can use EVALIN to run who from within writevars, e.g.
function writevars(filename,varargin)
%# get a list of variable names in the calling workspace and read their values
if isempty(varargin)
listOfVars = evalin('caller','who');
values = cell(size(listOfVars));
for i=1:length(listOfVars)
values{i} = evalin('caller',listOfVars{i});
end
else
%# use inputname to read the variable names into listOfVars
end
%# --- rest of writevars is here ---
It can be used by using the return value of whos command:
function GetAllVars
a = 45;
x = 67;
ff = 156;
z = who();
for i=1:numel(z)
if ~isequal(z{i},'z')
fprintf(1,'%s = %f\n',z{i},eval(z{i}));
end
end