I am trying to save the User input in app designer(MATLAB). I have created an empty struct, but I am unable to figure out where the items in the Listbox are being saved. With Matlab, a matrix is usually created and one can make certain UI components persistent so that, but that doesnt seem to be the case for App designer. I have attached a copy of the code, this isnt the complete code but rather the area for the listbox
properties (Access = public)
myStruct = struct()
end
% Callbacks that handle component events
methods (Access = private)
% Callback function: NEXTButton_4, WelcomeTab
function ButtonPushed(app, event)
app.TabGroup.SelectedTab = app.AgeTab
end
% Value changed function: ListBox
function ListBoxValueChanged(app, event)
value = app.ListBox.Value;
if strcmp(app.ListBox.Value ,'18-28')||strcmp(app.ListBox.Value ,'29-39')||strcmp(app.ListBox.Value,'40-50')||strcmp(app.ListBox.Value,'51-61')...
||strcmp(app.ListBox.Value,'62-72');
set(app.NEXTButton,'Enable','on');
Age = app.myStruct.Age;
end
Age = app.ListBox.Value;
% save('Age.mat',"Age")
% save('Dummyfile.mat', '-struct', myStruct)
% for i = 1:numel(app.ListBox.Items)
% index(i) = isequal(app.ListBox.Value{1}, [app.ListBox.ItemsData{i}]);
% end
% idx = find(index); % Find indice of nonzero element
% ItemName = app.ListBox.Items{idx}
i have tried to create an index, but it didn't work.
I'm not sure I understand the question correctly, but, if you want to save the values of all selected items you can:
Add two properties to the app:
a cellarray in which to store the value of the selected items; if you need to save only it, you don't need a struct
a counter to be used as index for the cellarray
properties (Access = public)
Selected_items = {} % Description
n_ages;
end
In the startupFcn:
Initialize the cellarray to empty
Set the listbox value to an empty array to specify non selection at the beginning (othewise, the first item will appear as selected by default when the app is created and the callback ListBoxValueChanged will not catch the selection of it)
Iniitalize the counter to 0
function startupFcn(app)
app.Selected_items = {};
app.ListBox.Value = {};
app.n_ages = 0;
end
In the ListBoxValueChanged callback:
Get the value od the selected item
Increment the counter
Store the the value in the app cellarray you have added; in this way the list of selected items will be available to other callbacks and functions of your app without the need to store them in a ".mat file"
Copy the content of the cellarray
Save the cellarray in a ".mat" file
function ListBoxValueChanged(app, event)
value = app.ListBox.Value;
app.n_ages = app.n_ages + 1;
app.Selected_items{app.n_ages} = value;
Selected_Items=app.Selected_items;
save('SEL_ITEMS.mat','Selected_Items');
end
Note: you can easily adapt the code of the ListBoxValueChanged callback above to take into acount the if conditions
Related
I am working on a App in MATLAB and I use the app design to build it. I have added a text area element in which I display messages to the user (similar use as the command window). In the the app the user can press buttons, which trigger functions to be executed and within those functions, I would like to be able to display some messages in this text area element.
This is an example of the code I use to display the text in this text area. I use a counter to add text in the list and simulate display without overwriting the previous messages.
% display execution message
app.nb_Text_stock = app.nb_Text_stock + 1;
app.OutputStatusTextArea.Value(app.nb_Text_stock) = {'My test here'};
As you can see, I need the app element. I could then pass it to the function all the way to the level where I need to display the text but my real question is, can I access the app element from within the function without passing it as an argument? The reason I want to do that is I have also a non-GUI version of the script where I would not be able to pass app as argument. So to make things simpler, I would like to have a parameters GUI = 1 or 0, and then based on that display either in the command window if GUI = 0 or in the text area in the GUI if GUI = 1. But for that I need to access the app element from inside my function. Is there a proper way to do that? Or do you have any suggestion for another approach for this problem?
You can store app object using setappdata, and get the object using getappdata:
Store app in startupFcn function (Code that executes after component creation):
Add startupFcn by adding callback in "Code View".
% Code that executes after component creation
function startupFcn(app)
% Store app in the root object (setappdata(groot, 'my_app', app) also works).
setappdata(0, 'my_app', app)
end
Read app object from any function:
app = getappdata(0, 'my_app');
Note:
This is not a good coding practice.
What you supposed to do:
function NonGuiFun()
app = app1();
app.func();
What you are asking to do:
function NonGuiFun()
% Get app object (assuming `app` GUI is already open)
app = getappdata(0, 'my_app');
if ~isempty(app)
app.func();
end
Here is the entire code of app1 class, that I used for testing (most of it is automatically generated):
classdef app1 < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
UIFigure matlab.ui.Figure
Button matlab.ui.control.StateButton
TextAreaLabel matlab.ui.control.Label
OutputStatusTextArea matlab.ui.control.TextArea
end
properties (Access = private)
nb_Text_stock = 0; % Description
end
methods (Access = public)
function results = func(app)
app.nb_Text_stock = app.nb_Text_stock + 1;
app.OutputStatusTextArea.Value(app.nb_Text_stock) = {num2str(app.nb_Text_stock)};
end
end
% Callbacks that handle component events
methods (Access = private)
% Code that executes after component creation
function startupFcn(app)
setappdata(0, 'my_app', app)
end
% Value changed function: Button
function ButtonValueChanged(app, event)
value = app.Button.Value;
func(app);
end
% Close request function: UIFigure
function UIFigureCloseRequest(app, event)
setappdata(0, 'my_app', [])
delete(app)
end
end
% Component initialization
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create UIFigure and hide until all components are created
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100 100 640 480];
app.UIFigure.Name = 'UI Figure';
app.UIFigure.CloseRequestFcn = createCallbackFcn(app, #UIFigureCloseRequest, true);
% Create Button
app.Button = uibutton(app.UIFigure, 'state');
app.Button.ValueChangedFcn = createCallbackFcn(app, #ButtonValueChanged, true);
app.Button.Text = 'Button';
app.Button.Position = [214 295 214 85];
% Create TextAreaLabel
app.TextAreaLabel = uilabel(app.UIFigure);
app.TextAreaLabel.HorizontalAlignment = 'right';
app.TextAreaLabel.Position = [210 211 56 22];
app.TextAreaLabel.Text = 'Text Area';
% Create OutputStatusTextArea
app.OutputStatusTextArea = uitextarea(app.UIFigure);
app.OutputStatusTextArea.Position = [281 175 150 60];
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = app1
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
% Execute the startup function
runStartupFcn(app, #startupFcn)
if nargout == 0
clear app
end
end
% Code that executes before app deletion
function delete(app)
% Delete UIFigure when app is deleted
delete(app.UIFigure)
end
end
end
Note that UIFigureCloseRequest executes: setappdata(0, 'my_app', []).
If you have the handle of any graphical object, you can find pretty much any other object in the same figure using the .Parent and .Children fields (e.g. hObject.Parent.Parent.Children(3).String = 'foo'), optionally using ancestor. If you don't have any object handles, you can use findall, but this would require some means of identifying the correct figures/controls. This can be done using their Tag field, but it would require specifying it beforehand.
I have a function that is part of an object that I have name TestData. Here are the properties and its constructor:
classdef TestData
properties
metaData = []; % stores meta data in Nx2 array
data = []; % stores data in PxQ array
colLabels = []; % labels columns
colUnits = []; % provides units
temp = []; % temporary to bypass setters while structuring data
end
methods
%% Constructor
function this = TestData(varargin) % Will take in a variable number of inputs
if (nargin == 0)
return;
end
if (nargin == 1 && ischar(varargin{1})) % Case of the input being a file (.txt, .s*p, etc..)
this = readDataFromFile(this, varargin{1});
return;
elseif (nargin == 4) % Case of the input being raw data. Note that the order matters.
this.metaData = varargin{1};
this.colLabels = varargin{2};
this.colUnits = varargin{3};
this.data = varargin{4};
return
else
error('Not enough input arguments.');
end
end
Now, let us say that I have 42 text files that I want to pass into the object. I have created a function for this called extractData that sifts through the data and finds a user input x-axis and y-axis. It does so by first calling a function readDataFromFile, which sets the property arrays that are outlined in the code above. It is a fairly long function and I won't include it for the time being, but I will say that I do know it is working.
The problem: I make an object of the class called myTestData and perform this operation in the CW, myTestData=myTestData.extractData('Tip Type','Tip S/N'). This will look for the Tip Type and Tip S/N. As you can see in the code, the type and S/N are in column 3 of the matrix. If the string is found, it takes the row it was found on, accesses column 3 of that row, and places that value into the temp array. In my case, the type is passive and the S/N is AM66191. It will write this to the temp array, but then on the second iteration of the for loop it will write something else over the first row and the passive and AM66191 will be displayed on the second row. It will do this all the way until the end, where on row 42 I will see the type and S/N, but all the other rows are just garbage. I have attached an image of what the temp array looks like after the second iteration of the for loop. Here is the code for the extractData function:
function this = extractData(this, xAxis, yAxis)
s = dir('*.txt'); % Gather all text files
disp(length(s))
for i=1:length(s) % Loop through and gather data until last element of strcuct
j = 1;
fid = s(i).name; % Open file in read-only mode
this = this.readDataFromFile(fid);
disp(fid)
x = this.metaData(find(contains(this.metaData(:,1), xAxis)),3);
this.temp(i,j) = x;
disp(this.temp(i,j))
j = j+1;
y = this.metaData(find(contains(this.metaData, yAxis)),3); %#ok<*FNDSB>
this.temp(i,j) = y;
disp(this.temp(i,j))
end %for
end %extractData
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)
This case is, I know how to transfer all the data from one listbox to another one, and then clear listbox1. I did in this way:
function pushbutton1_Callback(hObject, eventdata, handles)
StringInLB1 = get(handles.listbox1,'string');
set(handles.listbox2,'string',StringInLB1);
set(handles.listbox1,'string','');
Now my question is: How can I transfer some selected data to listbox2 ? I "ctrl+ single click" multiple data in listbox1, but how can I use thoes data?
Thanks a lot.
It looks like you are looking to assign some function to the listbox callback. i.e. each time the selection is changed by the user, you want to do something with the data. Anyhow that's what I understood from your comment to #James answer.
If it's the case, here is a sample code generating a simple GUI in which the color of a plot is changed by the user directly by clicking in the listbox:
function DummyListBox
global hFig hListBox hPlot
ScreenSize = get(0,'ScreenSize');
hFig = figure('Visible','off','Position',[ScreenSize(3)/2,ScreenSize(4)/2,450,285]);
ColorString = {'Red';'Green';'Blue'}; % Define string populating the listbox
hListBox = uicontrol('Style','Listbox','String',ColorString,'Position',[315,150,70,50],'max',3,...
'min',1,'Callback',#ListBox_Callback); %%// added 'min' and 'max' properties to select multiple items
hText = uicontrol('Style','Text','Position',[315,220,70,35],'String','Empty now'); %%// Add text box
hAxes = axes('Units','Pixels','Position',[50,60,200,185]);
set(hFig,'Visible','on')
x = 1:5*pi;
hPlot = plot(x,sin(x),'-r','Parent',hAxes); %display some data
% Listbox callback: each time the selection changes, the color of the
% plot changes accordingly.
function ListBox_Callback(~,~)
SelectedValues = get(hListBox,'Value'); % Get the values selected
set(hText,'String',SelectedValues); % Uptdate the string in the textbox
NewColor = ColorString{get(hListBox,'Value')};
set(hPlot,'Color',NewColor)
end
end
The output now looks like this:
As you see the function in which the change in color takes place is the listbox's callback. Hope this is what you want. If not please be more specific as to what you would like. Thanks!
EDIT: As you can see, I added a text box to show you what it looks like when you select multilpe items in the listbox. In order to do so, you must add a 'min' and a 'max' property when you create the listbox. I set them to 1 and 3, respectively. The 'Value' property of the listbox corresponds to the actual number of the item selected from the list. So if you have 3 items as is my exmample,the Value can be 1,2,3 or any combination. They are displayed in the text box. You can get the corresponding data from those values, stored in vectors.
So to answer your question, you could write:
Data1 = ValuesVector(1)
Data2 = VectorValues(2)
... and so on
If you use a
tmp = get(handles.listbox1,'string');
You'll see what the variable is like. Then you can use something like
set(handles.listbox1,'string',tmp{2});
I have built a calculation software in MATLAB GUIDE. What I want to do is to fill out all my calculation data in different edit fields and some dropdowns and when I press calculate a "listbox" should be populated with the text "Calculation 1".
If I then change some data in some of the input fields and press calculate again I want to populate the listbox with "Calculation 2" beneath "Calculation 1" etc...
But then I would want to be able to highligt "calculation 1" again in the listbox and press a "load input parameters" button to populate all the edit input fields with the data that was used when "calculation 1" was calculated.
I have looked all over the place for this but can't find anything.
//Robin
Here is some code which is very basic but performs what you are looking for. There are a lot of tweaks possible but I'll let you play around with them. I put explanations as comments. You can copy past into Matlab and change the GUI as you like.
function CalculatorGUI
% Dummy GUI to calculate A*B + C...
clc
clear
close all
global hTestResult hEditA hEditB hEditC CalculationList CalculationStrings
% Set up controls
CalculationList = nan(10,3); % Create array in which we store the parameters. 1st column is A, 2nd is B and 3rd is C.
CalculationStrings = cell(10,1);
ScreenSize = get(0,'ScreenSize');
hFig = figure('Visible','off','Position',[ScreenSize(3)/2,ScreenSize(4)/2,450,285]);
hCalculateButton = uicontrol('Style','Pushbutton','Position',[350,150,80,30],'String','Calculate!','Callback',#CalculateCallback);
hTitle = uicontrol('Style','Text','Position',[100,250,100,25],'String','Calculate (A * B) + C');
hTextA = uicontrol('Style','Text','Position',[125,220,70,25],'String','A');
hEditA = uicontrol('Style','Edit','Position',[125,200,70,25],'String','1');
hTextB = uicontrol('Style','Text','Position',[200,220,70,25],'String','B');
hEditB = uicontrol('Style','Edit','Position',[200,200,70,25],'String','2');
hTextC = uicontrol('Style','Text','Position',[275,220,70,25],'String','C');
hEditC = uicontrol('Style','Edit','Position',[275,200,70,25],'String','3');
hResultHeader = uicontrol('Style','Text','Position',[350,220,70,25],'String','Result');
hTestResult = uicontrol('Style','Text','Position',[350,200,70,25],'String','');
hTextCalcu = uicontrol('Style','Text','Position',[100,140,100,50],'String','Calculations');
hListCalcu = uicontrol('Style','Listbox','String','','Position',[100,120,200,50],'max',10,...
'min',1,'Callback',#ListBox_Callback);
set(hFig,'Visible','on')
%======================================================================
%======================================================================
% Callback of the pushbutton
function CalculateCallback(~,~)
% Get the values in the edit boxes. There is no ckechup to make
% sure the user entered a correct value...
A = str2double(get(hEditA,'String'));
B = str2double(get(hEditB,'String'));
C = str2double(get(hEditC,'String'));
Calculation = A*B+C;
% Display the result.
set(hTestResult,'String',sprintf('The result is %0.2f',Calculation));
% Find how many calculations have been performed and append the
% parameters to the current list
[x,~] = find(~isnan(CalculationList));
CurrentCalc = numel(unique(x)); % Get number of rows which are NOT NaNs.
CurrentValues = [A B C];
CalculationList(CurrentCalc+1,:) = CurrentValues;
CurrentString = sprintf('A = %0.2f B = %0.2f C = %0.2f',A,B,C);
% Assign the parameters to the cell array.
CalculationStrings(CurrentCalc+1) = {CurrentString};
set(hListCalcu,'String',CalculationStrings)
end
% Listbox callback: When the selection changes, the corresponding
% parameters in the edit boxes change.
function ListBox_Callback(~,~)
SelectedCalc = get(hListCalcu,'Value');
CalculationList(SelectedCalc,1)
CalculationList(SelectedCalc,2)
CalculationList(SelectedCalc,3)
set(hEditA,'String',CalculationList(SelectedCalc,1));
set(hEditB,'String',CalculationList(SelectedCalc,2));
set(hEditC,'String',CalculationList(SelectedCalc,3));
end
end
The actual interface looks like this:
Of course you can make it much more complex, but this should help you get started and understand how the different callbacks work together. Have fun!