I am trying to create a GUI for my script in Matlab. At the moment I am opening this dialog boxes in my script to get the inputs I need.
prompt = {'Mass [kg]:','Power [kW]:','Drag Coeff:','Lift Coeff:','Area Front [m^2]:'};
dlgtitle = 'Vehicle Parameters';
dims = [1 35];
definput = {'752','650','1','3','1'};
vehicle = inputdlg(prompt,dlgtitle,dims,definput);
prompt = {'Friction Coeff:','Air Density [kg/m^3]:'};
dlgtitle = 'External Parameters';
dims = [1 35];
definput = {'1.4','1.19'};
ambient = inputdlg(prompt,dlgtitle,dims,definput);
prompt = {'Length [m]:','Corner Radius [m]:'};
dlgtitle = 'Track Parameters';
dims = [1 35];
definput = {'1000','150'};
track = inputdlg(prompt,dlgtitle,dims,definput);
Here the code continues
laptime = formula used to get the laptime;
However I would like to create something similar to what this image shows, where I can write the parameters, push the run button to run the script and get the laptime printed. Any suggestions on how I could achieve it?
Thanks a lot in advance!
steeven:
From inspecting your image, you essentially will need:
A figure object.
Ten(10) field objects.
Ten(10) Static text objects.
A pushbutton object
A callback function.
Here's a sample script:
clc
clear
close all %Very important when making GUI's in MATLAB!
%figure object that contains GUI.
f = figure('units','normalized');
%Uicontrols devoted to static text:
m_text = uicontrol('Parent',f,'units','normalized','Style','Text');
P_text = uicontrol('Parent',f,'units','normalized','Style','Text');
FS_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CD_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CL_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CF_text = uicontrol('Parent',f,'units','normalized','Style','Text');
rho_text = uicontrol('Parent',f,'units','normalized','Style','Text');
len_text = uicontrol('Parent',f,'units','normalized','Style','Text');
rad_text = uicontrol('Parent',f,'units','normalized','Style','Text');
%Uicontrols devoted to editable fields:
m_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
P_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
FS_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CD_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CL_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CF_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
rho_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
len_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
rad_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
% MLG Cell array strat.
ui_texts = {m_text,P_text,FS_text,CD_text,CL_text,CF_text,rho_text,...
len_text,rad_text};
strings = {'mass','power','Front Section','Drag Coefficient',...
'Lift Coefficient','Friction Coefficient','Air Density','Length',...
'Radius'};
ui_edits = {m_edit,P_edit,FS_edit,CD_edit,CL_edit,CF_edit,rho_edit,...
len_edit,rad_edit};
defaults = {'10','100','0.5','0.05','1.2','0.0005','1.225','5','3'};
xi = 0.05; %X-coordinate of Bottom-Left corner.
yi = 0.85; %Y-coordinate of Bottom-Left corner.
width = 0.15; %Width of object.
height = 0.075; %Height of object
deltaH = 0.3; %Horizontal Spacing between objects.
deltaV = -0.25; %Vertical Spacing between objects.
offset = 0.15;
rows = 3; %Number of rows.
cols = 3; %Number of columns.
k = 0; % Counter for number of uicontrols that populate grid pattern.
for i = 1:rows
for j = 1:cols
k = k + 1;
if k <= length(ui_texts)
x = xi + deltaH*(j-1);
y = yi + deltaV*(i-1);
xo = x + offset; %horizontally offset the editable field.
set(ui_texts{k},'Position',[x, y, width, height],'String',strings{k})
set(ui_edits{k},'Position',[xo, y, width, height],'String',defaults{k})
end
end
end
%Uicontrol for output object:
out = uicontrol('Parent',f,'Style','text','units','normalized',...
'position',[xo-deltaH,y+deltaV,width,height],'String','Val = ');
%Uicontrol for button execution:
exe = uicontrol('Parent',f,'Style','pushbutton','units','normalized',...
'position',[xo-2*deltaH,y+deltaV,width,height],'String','Calculate',...
'Callback',{#computations,ui_edits,out});
%Function executed by the "exe" uicontrol.
function computations(obj,~,ui_edits,out)
no_inputs = length(ui_edits);
summation = 0;
for i = 1:no_inputs
in = get(ui_edits{i},'String');
val = str2double(in);
summation = summation +val;
end
set(out,'String',['Val = ',num2str(summation)])
end
The above script produces:
Here's the gist:
The figure is a graphical object needed to contain the editable fields and text descriptions.
The fields are objects that enable user input (These are uicontrol objects).
The static texts establish to the user the correspondence between field and input (These are also uicontrol objects).
The button is an object that executes the callback function (also a uicontrol).
The callback function is a user-defined function that collects inputs from uicontrol objects and uses them to run code.
In this example, the "calculate" button simply sums up all the numeric values of the inputs. For your applications, you must modify the programmer-defined function at the end to suit your needs.
This kind of GUI lets users input information as strings. Said information must be converted from the string type to the double type. Other GUI objects allow for direct numerical inputs (such as sliders or buttons with underlying logical structure, ie. "if this button, then this value"). When working with editable fields, the programmer must be conscious that the inputs are strings until run through a function like str2double.
Other nuances with GUI's:
Every object that the programmer intends to be interactive must have an associated programmer-defined function.
In this case, the only interactive object is the button.
Personally, my biggest challenge when making MATLAB GUI's is dealing with the positioning of the UI controls. I have used a few for loops and cell arrays to generate the grid pattern for the texts and buttons. This is quality of life on my part, more elaborate GUI's may require more sophisticated coding.
MATLAB has a feature called "GUIDE" which helps automate the uicontrol generation. This is worth a try if the programatic method I have shared is too tedious.
I want to create a layer that make two copies of the previous layer (input layer), here is my code:
classdef CopyLayer < nnet.layer.Layer
methods
function layer = CopyLayer(numOutputs,name)
% Set number of inputs.
layer.NumOutputs = numOutputs;
% Set layer name.
layer.Name = name;
% Set layer description.
layer.Description = "Make " + numOutputs + ...
" copies of the input layer";
end
function [varargout] = predict(X)
% Layer forward function for prediction goes here.
numOutputs = layer.NumOutputs;
[h,w,c] = size(X);
Z = zeros(h,w,c,numOutputs);
for i= 1 : numOutputs
Z(:,:,:,i) = X;
end
varargout = Z;
end
% function [] = backward()
% end
end
end
and when I try to create the layer by:
layer = CopyLayer(2,'copy');
the following error appears:
Abstract classes cannot be instantiated. Class 'CopyLayer' inherits
abstract methods or properties but does not implement them.
See the list of methods and properties that 'CopyLayer' must implement
if you do not intend the class to be abstract.
Error in SplitLayer (line 1)
layer = CopyLayer(2,'copy');
and I think it's because of the no existing of backward function. Is that correct? How can I resolve this error?
The error message is clear -- if you're used to reading error messages, and you're familiar with abstract classes.
A class is abstract if it contains elements that are not yet defined in a manner one could instantiate. They are intended particularly to be templates that the user can fill out to have the desired implementation details. nn.layer.layer is one such abstract class.
When you instantiate an abstract class, but fail to define every template element, then the result is another abstract class -- just with fewer "template" elements. That is what you've done: CopyLayer does not implement backward, so it's still abstract. When you instantiate with layer = CopyLayer(2,'copy'), you still have open elements, so you cannot make a concrete object from the class. Hence the error message.
You can work around this in a couple of ways:
Implement your own layer class, one that doesn't even mention a back-prop function. You'll have extra connection work to do, since you give up your inheritance from nn.layer.
Implement backward, but leave the body non-functional (empty). This will satisfy the compiler, and give you a very boring (but fast) back-prop stage.
thanks to Mr #Prune
the code after correction becomes as follow:
classdef CopyLayer < nnet.layer.Layer
properties
% (Optional) Layer properties.
NumOutputs
end
methods
function layer = CopyLayer(numOutputs,name)
% Set number of inputs.
layer.NumOutputs = numOutputs;
% Set layer name.
layer.Name = name;
% Set layer description.
layer.Description = "Make " + numOutputs + ...
" copies of the input layer";
end
function varargout = predict(layer,X)
numOutputs = layer.NumOutputs;
[h,w,c] = size(X);
Z = zeros(h,w,c,numOutputs);
for i= 1 : numOutputs
Z(:,:,:,i) = X;
end
varargout = Z;
end
function [dLdX] = backward(~,~,~,~,~)
end
end
end
I'm trying to solve differential equation using the ode45 function. Consider the following code,
[t1,X2] = ode45(#(t,x)fun(t,x,C1,C2,C3,C4),t0,X01);
where parameters C1, C2, C3 and C4 are column vectors, which should be available to the function that ode45 is referring to (fun.m). I want the values to change after every iteration, so for example, at the beginning the entry of C1 I want in is C1(1), in the next iteration it's C1(2), etc.
How can I implement that?
You may have noticed that the official docs are not too helpful in this scenario (as they pretty much force you to use global variables - which is doable, but discouraged). Instead, I'll show you how this can be done with classes and function handles. Consider the following:
classdef SimpleQueue < handle
%SIMPLEQUEUE A simple FIFO data structure.
properties (Access = private)
data
position
end
methods (Access = public)
function obj = SimpleQueue(inputData)
%SIMPLEQUEUE Construct an instance of this class
obj.data = inputData;
rewind(obj);
end % constructor
function out = pop(obj, howMany)
%POP return the next howMany elements.
if nargin < 2
howMany = 1; % default amount of values to return
end
finalPosition = obj.position + howMany;
if finalPosition > numel(obj.data)
error('Too many elements requested!');
end
out = obj.data(obj.position + 1 : obj.position + howMany);
obj.position = finalPosition;
end % pop
function [] = rewind(obj)
%REWIND restarts the element tracking
% Subsequent calls to pop() shall return elements from the beginning.
obj.position = 0;
end % rewind
end % methods
end % classdef
How to use this? Simple:
C1q = SimpleQueue(C1);
C2q = SimpleQueue(C2);
C3q = SimpleQueue(C3);
C4q = SimpleQueue(C4);
[t1,X2] = ode45(#(t,x)fun(t,x,#C1q.pop,#C2q.pop,#C3q.pop,#C4q.pop),t0,X01);
As you can see, inside fun we use C1q() instead of C1.
I have a list of parameters and I need to evaluate my method over this list. Right now, I am doing it this way
% Parameters
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};
params.k = {3,4,5,6};
params.constraintP = {'identity', 'none'};
params.Npoints_perJ = {2, 3};
params.sampling = {'hks', 'fps'};
% Select the current parameter
for corrAs_iter = params.corrAs
for size_iter = params.size
for density_iter = params.density
for k_iter = params.k
for constraintP_iter = params.constraintP
for Npoints_perJ_iter = params.Npoints_perJ
for sampling_iter = params.sampling
currentParam.corrAs = corrAs_iter;
currentParam.size = size_iter;
currentParam.density = density_iter;
currentParam.k = k_iter;
currentParam.constraintP = constraintP_iter;
currentParam.Npoints_perJ = Npoints_perJ_iter;
currentParam.sampling = sampling_iter;
evaluateMethod(currentParam);
end
end
end
end
end
end
end
I know it looks ugly and if I want to add a new type of parameter, I have to write another for loop. Is there any way, I can vectorize this? Or maybe use 2 for loops instead of so many.
I tried the following but, it doesn't result in what I need.
for i = 1:numel(fields)
% if isempty(params.(fields{i}))
param.(fields{i}) = params.(fields{i})(1);
params.(fields{i})(1) = [];
end
What you need is all combinations of your input parameters. Unfortunately, as you add more parameters the storage requirements will grow quickly (and you'll have to use a large indexing matrix).
Instead, here is an idea which uses linear indicies of a (never created) n1*n2*...*nm matrix, where ni is the number of elements in each field, for m fields.
It is flexible enough to cope with any amount of fields being added to params. Not performance tested, although as with any "all combinations" operation you should be wary of the non-linear increase in computation time as you add more fields to params, note prod(sz)!
The code I've shown is fast, but the performance will depend entirely on which operations you do in the loop.
% Add parameters here
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};
% Setup
f = fieldnames( params );
nf = numel(f);
sz = NaN( nf, 1 );
% Loop over all parameters to get sizes
for jj = 1:nf
sz(jj) = numel( params.(f{jj}) );
end
% Loop for every combination of parameters
idx = cell(1,nf);
for ii = 1:prod(sz)
% Use ind2sub to switch from a linear index to the combination set
[idx{:}] = ind2sub( sz, ii );
% Create currentParam from the combination indices
currentParam = struct();
for jj = 1:nf
currentParam.(f{jj}) = params.(f{jj}){idx{jj}};
end
% Do something with currentParam here
% ...
end
Asides:
I'm using dynamic field name references for indexing the fields
I'm passing multiple outputs into a cell array from ind2sub, so you can handle a variable number of field names when ind2sub has one output for each dimension (or field in this use-case).
Here is a vectorized solution :
names = fieldnames(params).';
paramGrid = cell(1,numel(names));
cp = struct2cell(params);
[paramGrid{:}] = ndgrid(cp{:});
ng = [names;paramGrid];
st = struct(ng{:});
for param = st(:).'
currentParam = param;
end
Instead of nested loops we can use ndgrid to create the cartesian product of the cell entries so we can find all combinations of cell entries without loop.