MATLAB GUI User Input Update - matlab

I have a MATLAB GUI which takes values from the user input , do some calculation based on those and plot them in the GUI.
Everything is all right and working BUT when I change the values in the GUI it does not plot as expected and gives and error of dimension mismatch. However, there is no dimension mismatch and if I go to the script and press run again (the big green arrow) and then click plot again, it plots the values as expected.
I have initialized all of my values that I use but I can not see how can fix this. I need something like update command for all of the script each time I press the 'plot' button.
Any help is appreciated, many thanks!..
CODE:
a = str2double(get(handles.edit14,'String'));
b = str2double(get(handles.edit15,'String'));
c = str2double(get(handles.edit16,'String'));
d = str2double(get(handles.edit18,'String'));
e = str2double(get(handles.edit19,'String'));
f = str2double(get(handles.edit20,'String'));
for Veff=[a:b:c] %user input speeds (a,b,c) (comes from user)
q = 0.5*1.225*(Veff*1000/3600)^2;
F1=q*S; M1=q*S*Cref;
FX1=(F1*coef(:,1)); FZ1=(F1*coef(:,3)); MM1=(M1*coef(:,5));
W=[d*g:e*g:f*g]; %define mass range (d,e,f comes from user)
for lw=1:1:length(W)
XGEquation2max1 = ((((-FB*(Xa-Xb))-(((FX1(12)))*(Za-Zr))-(((FZ1(7))))*(Xa-Xr))-((max(MM1))))./W(lw)) + Xa;
CGPercent(lw,column) = (XGEquation2max1 - Cstart)/Cref * 100;
end
column=column+1;
end
speed_matrix = [a:b:c];
mass_matrix = [d:e:f];
ns = size(speed_matrix);
ns= ns(2);
count5 = 1;
for ns=1:1:ns
hold all
axes(handles.axes4)
plot(CGPercent(:,ns),transpose(mass_matrix)/1000)
count5 = count5 + 1;
end
Main problem is with the variables 'd,e,f' because when I change 'a,b,c' only (i.e speed) there is no problem.

Related

How can I loop through and process files from a directory individually?

I'm trying to write a function that iteratively loops through a bunch of .txt files in a directory and processes them individually. After a file has been processed I would like to plot output variables, then on the next iteration plot the new variables on the same graph. My code is shown below however does not seem to be working, it returns a 'output argument 'output1' (and maybe others) not assigned during call to function2.
Currently my code looks something like this:
function [output1, output2, fig] = Processing(Folder_1,Folder_2,Folder_3)
%% Read in all the Data
% Initialise Loop to process each file independently
for i = 1 : length(Text_Files1)
Sorted_Index1 = Indices1(i);
Path1 = fullfile(Folder_1,Text_Files1(Sorted_Index1).name);
Table1 = readtable(Path1);
Sorted_Index2 = Indices2(i);
Path2 = fullfile(Folder_2,Text_Files2(Sorted_Index2).name);
Table2 = readtable(Path2);
Sorted_Index3 = Indices3(i);
Path3 = fullfile(Folder_3,Text_Files3(Sorted_Index3).name);
Table3 = readtable(Path3,'Delimiter',';');
%% Process Data through the Loop
[HR] = function1(processed_data)
[output1 output2] = function2(HR, Time);
hold on
fig = figure('Visible', false);
subplot(10,10,1:90)
plot(output2, output1, 'Color', [0.92 0.47 0.44], 'LineWidth', 1.5);
end
end
%% Function 2:
function [output1, output2] = function2(t, y, z)
segment = 1:z:max(y);
% Iterate through each time segment and calculate average t value
for x = 1:length(segment)-1
SegmentScores = find(y > segment(x) & ...
y < segment(x+1));
output1(x) = mean(y(SegmentScores));
output2(x) = Segment(x+1);
end
end
The problem is that you're calling function2(HR, Time) with two inputs, while it needs three function2(t, y, z).
For that reason, I'm assuming that inside function2 segment is an empty array, and thus the loop for x = 1:length(segment) - 1 is not entered at all.
Since the definition of output1 and output2 is only inside the loop, they will never be created and thus the error.
If you provide three inputs to your function2, the problem will be solved. Just pay attention to the order in which you provide them (for example, I'm assuming your Time input should be the same as t in the function, so it should be the first input to be passed).

Averaging over adjacency groups in a loop

I have the following code which eventually outputs a graph and a 'groups' value. The results are dependent on a random function so can provide different results every times.
function [t seqBeliefs] = extendedHK(n, tol, adj)
%extendedHK Summary of function goes here
%Detailed explanation goes here
beliefs = rand(n,1);
seqBeliefs = beliefs; %NxT matrix
converge = 0;
step = 0
t = step
while converge ~= 1
step = step+1;
t = [t step];
A = zeros (n,n);
for i=1:1:n
for j=i:1:n
if abs(beliefs(i) - beliefs(j)) < tol && adj(i,j)==1
A(j,i)=1;
A(i,j)=1;
end
end
end
beliefs = A*beliefs./ sum(A,2);
seqBeliefs = [seqBeliefs beliefs];
if sum(abs(beliefs - seqBeliefs(:,step)))<1e-12
converge = 1;
end
end
groups = length(uniquetol(seqBeliefs(:,step), 1e-10))
plot(t,seqBeliefs)
end
In command window type
adj=random_graph(n)
I usually use n as 100 then call extendedHK function with same n then tol value (I usually choose between 0.1 and 0.4) and 'adj'
e.g. adj = random_graph(100)
extendedHK(100, 0.2, adj)
What I now need help with is running this function say 100 times, and taking an average of how many 'groups' are formed.
First, include the parameter "groups" in your function output. to do so, try this instead of the first line of your code:
function [t seqBeliefs groups] = extendedHK(n, tol, adj)
then save this function in a extendedHK.m file.
open another .m file, say console.m and write this:
results = zeros(1,100);
for i = 1:100
% set n, tol and adj inputs here <=
[~,~,out] = extendedHK(n, tol, adj);
results(1,i) = out;
end
avg = mean(results)
don't forget to define "results" out of the loop. since parameters that change size every loop, will make your code slow
(suppressing unnecessary outputs with ~ added later to this reply)
It is not clear if you need the current outputs of extendedHK:[t seqBeliefs]
Anyhow, you can add "groups" to the output and then from the console
N=100;
groups_vec = zeros(1,N);
for i=1:N
adj = random_graph(100);
[~,~,groups_vec(i)] = extendedHK(100, 0.2, adj);
end
groups_avr = mean(groups_vec);
note that if you use this code you won't be able to "see" the graphs as they will be cleared every loop iteration. you can do one of the following (1) add "figure;" before the plot command and then you will have 100 figures. (2) add "pause" to wait for key press between each graph. (3) add "hold on" to print all graphs on the same figure.

Matlab GUI. Set pushbutton handles in another .m file

I have a big Matlab code and now I am trying to connect it to GUI. I have created a function
function z = interface_master(handles)
which first gets initial parameters from GUI text edit box and then runs a number of .m files using these parameters, for example
n = get(handles.n_value,'String');
n = str2num(n);
assign('base','n',n)
run('code_1')
run('code_2')
...
Within this 'codes' I create and save a number of figures. I would like now for the User to be able to display a figure of his choice within GUI. Lets say I will have 3 different push buttons (Push1, Push2, Push3) and User after pressing Push1 will get Figure_A displayed. Then after pressing Push2, he will get Figure_B replacing Figure_A, and so on. Can I somehow make it work just setting handles in my function interface_master?
I don't want to overcrowd my interface.m file and so far I managed to do everything else (reading values, displaying messages) in this additional interface_master file, by just connecting both through interface_master(handles) in the interface callback functions. But now I am stuck with these push buttons.
I would really appreciate some help here. I have never done any GUI before.
I have created a very much simplified version of what I am doing. In general code_1 and code_2 are much much bigger and the interface will be displaying more messages, while code_1 and code_2 save around 20 different figures. But what I am struggling with can be demonstrated by a simple code calculating polynomials.
%% code_1.m
x = linspace(-1,1) ;
y = x.^n ;
%% code_2.m
f = figure('visible','off');
plot(x,y);
set(f,'Visible','on')
saveas(f,'power_plot_1','fig')
delete(f)
g = figure('visible','off');
plot(x,x.^(n+1));
set(f,'Visible','on')
saveas(g,'power_plot_2','fig')
delete(g)
%%% master.m
function z = master(handles)
n = get(handles.n_value,'String')
n = str2num(n) ;
assignin('base','n',n)
if (n < 1)
message = ('small n') ;
elseif (n>10)
message = ('large n') ;
else
message=('hello world') ;
run('code_1')
run('code_2')
end
set(handles.text1,'String',message)
and here is the interface:
https://lh3.googleusercontent.com/-5zoGVwgJhoM/V1qdiyd667I/AAAAAAAACQ0/oaTQHYn13gIuLoSb42Q7N66AV102e-VjQCCo/s912/inter.png

Scatter persists after setting X & YData to fewer points

EDIT: See end of post for code to automatically reproduce the issue without the mouse/keyboard input and for pictures describing the issue.
Please let me know if any of the following explanation or code requires further clarification.
I'm allowing a user to click on an axis and scatter points appear where they click. Left clicking results in larger scatter markers, and right clicking results in smaller scatter markers. Pressing enter adds a new set (represented by scatter markers switching between yellow and red). Pressing backspace should delete the most recent marker, and consecutively delete markers back through the set and into previous sets if continued to be pressed.
This works fine if I use only one size marker, however, if I allow the size to be determined by the left/right click, then upon clicking backspace to delete previous points, it will correctly delete the points until you arrive at the first point (the earliest point, not the first you try to delete) which had a size change. The still appear on the figure as if they were a separate scatter plot, even though they are one scatter plot, and the data the plot is set from has been deleted (and the scatter data set to that deleted data set).
Here is my example code:
function CodeToReplicateIssue()
a = get(0,'ScreenSize');
set(0,'DefaultFigurePosition',[a(3)*.3,a(4)*.1,a(3)*.4,a(4)*.8]);
mainFig = figure;
set(mainFig, 'WindowButtonDownFcn', #mouseDown);
set(mainFig,'WindowKeyPressFcn', #keyPress);
pfTitle = 'Next Set: Enter Delete Previous Point: Backspace';
colormap('gray');
imagesc(zeros(512,512));
set(gca,'XTick',[]);
set(gca,'YTick',[]);
axis image;
title(pfTitle)
hold on;
cStrs2 = {'r','y'};
pfTracks = {[]};
pfTracksRef = {[]};
pfTrackI = 1;
pfScatters{1} = scatter([],[],5,cStrs2{2});
function mouseDown(source,callbackData)
%display('mouseDown');
pos = get (gca, 'CurrentPoint');
pos = pos(1,1:2);
pfTracksRef{pfTrackI}(end+1) = ~strcmp(get(source,'SelectionType'),'alt');
pfTracks{pfTrackI}(end+1,1:2) = pos;
if(numel(pfScatters)<pfTrackI)
pfScatters{pfTrackI} = scatter(pos(1),pos(2),getSizes(pfTrackI),cStrs2{mod(pfTrackI,2)+1});
else
set(pfScatters{pfTrackI},{'XData','YData','SizeData'},{pfTracks{pfTrackI}(:,1),pfTracks{pfTrackI}(:,2),getSizes(pfTrackI)});
end
end
function keyPress(source,eventdata)
%display(eventdata.Key);
if(strcmp(eventdata.Key,'return'))
if(isempty(pfTracks{pfTrackI}))
%do nothing
else
refreshScatter('pfScatters');
pfTrackI = pfTrackI+1;
pfTracks{pfTrackI} = [];
pfTracksRef{pfTrackI} = [];
end
elseif(strcmp(eventdata.Key,'backspace'))
if(isempty(pfTracks{pfTrackI}) && numel(pfTracks)>1)
pfTracks(pfTrackI) = [];
pfTracksRef(pfTrackI) = [];
if(numel(pfScatters) >= pfTrackI)
pfScatters(pfTrackI:end) = [];
end
pfTrackI = pfTrackI -1;
end
if(numel(pfTracks{pfTrackI}))
pfTracks{pfTrackI}(end,:) = [];
pfTracksRef{pfTrackI}(end) = [];
end
refreshScatter('pfScatters');
end
end
function refreshScatter(strOption)
if(nargin<1)
strOption = '';
end
if(any(strcmp(strOption,{'','pfScatters'})))
for n = 1:numel(pfScatters)
if(numel(pfTracks{n})>0)
set(pfScatters{n},{'XData','YData','SizeData','MarkerEdgeColor'},{pfTracks{n}(:,1),pfTracks{n}(:,2),getSizes(n),cStrs2{mod(n,2)+1}});
else
set(pfScatters{n},{'XData','YData'},{[],[]});
end
end
end
end
function scatterSize = getSizes(indx)
scatterSize = 9*(pfTracksRef{indx}(:)+1)-6;
end
end
Line 32:
pfTracksRef{pfTrackI}(end+1) = ~strcmp(get(source,'SelectionType'),'alt');
This is where the left or right mouse button is recorded, later used in getSizes(), with the output used for calls to scatter (or when setting the scatter series SizeData).
I can't figure out why the points created before a size change persist after deleteing the associated data and setting the scatter values to that data. It seems like the first time a size is changed a new scatter series is created.
Any light that could be shed on this issue would be appreciated, and any fixes would be doubly appreciated.
Thanks.
EDIT: One way to see what I'm talking about is to do the following:
Left click several times (lets say 5), spaced out so you can see them all.
Push Enter
Left click 5 more times, again spaced out, note they are in red (this is a desired functionality for my final product).
Press backspace until all the points are no longer visible. This is the correct funcitonality.
Incorrect:
Left click 1 time, right click 4 times.
Press enter
Left click 1 time, right click 4 times.
Backspace repeatedly (10 times). Note that the first 2 points (1 large and 1 small) from both the red and the yellow persist, even though you have clicked backspace enough times to delete everything, which is what happened when there was no size change.
EDIT: I've added code below that replicates the issue I am seeing using some loops and doing away with requiring mouse and keyboard input.
function ReplicateIssueWithLoops()
mainFig = figure;
imshow(zeros(512,512));
hold on;
axis image;
for hh = 1:2
%two runs, the first works fine, the second fails to remove scatter
%points from the figure.
cStrs = {'r','y'};
tracks = {[]};
tracksSizeRef = {[]};
trackI = 1;
scatters{1} = scatter([],[],5,cStrs{2});
for ii = 1:2
%simulates 2 sets
for jj = 1:5
%simulates 5 left clicks each
tracks{ii}(end+1,:) = [256,(ii*6+jj)*20];
if(hh ==1) %Simulates all left clicks
tracksSizeRef{ii}(end+1) = 12;
else %Simulates 1 left click and 4 right clicks
tracksSizeRef{ii}(end+1) = ((jj==1)+1)*9-6;
end
if(numel(scatters)<ii)
scatters{ii} = scatter(tracks{ii}(:,1),tracks{ii}(:,2),tracksSizeRef{ii},cStrs{mod(ii,2)+1});
else
set(scatters{ii},{'XData','YData','SizeData'},{tracks{ii}(:,1),tracks{ii}(:,2),tracksSizeRef{ii}});
end
pause(0.25);
end
tracks{ii+1}=[];
tracksSizeRef{ii+1} = [];
end
trackI = ii;
for kk = 1:10
%simulates 10 backspaces
if(isempty(tracks{trackI}) && numel(tracks)>1)
tracks(trackI) = [];
tracksSizeRef(trackI) = [];
if(numel(scatters) >= trackI)
scatters(trackI:end) = [];
end
trackI = trackI -1;
end
if(numel(tracks{trackI}))
tracks{trackI}(end,:) = [];
tracksSizeRef{trackI}(end) = [];
end
refreshScatters();
pause(0.25);
end
end
function refreshScatters()
for n = 1:numel(scatters)
if(numel(tracks{n})>0)
set(scatters{n},{'XData','YData','SizeData','MarkerEdgeColor'},{tracks{n}(:,1),tracks{n}(:,2),tracksSizeRef{n},cStrs{mod(n,2)+1}});
else
set(scatters{n},{'XData','YData'},{[],[]});
end
end
end
end
Below are pictures demonstrating how the issue looks on my end. In left to right order the pictures show 1) at the end of the first 10 points, 2) after deleting the first 10 points , 3) after the second ten points (the set with size difference), 4) after deleting the second 10 points (4 remain, the two from each set when the size was first changed).

Save multiple GUIdata sets to listbox and then load them again in MATLAB

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!