MATLAB legends list everything in a plot, including guidelines that you have put on a plot.
A fudge to get around that is to do
*Plot
*Add legend
*Add guidelines
However, MATLAB puts the most recent lines in the front, meaning the guidelines then sit over the displayed data; ugly and distracting.
Similar problems occur any time you build up a complicated plot, legend freaks out and grabs everything, and workarounds with plotting order can be ugly
Example code:
%**** Optional guidelines
figure(1)
plot([2 2],[0,1],'k--'); hold on
%**** DATA
N = 4;
y=rand(5,N);
x=1:1:5;
for plotLoop=1:N;
%* Plot
figure(1)
plot(x,y(plotLoop,:));
hold on
end
%*****LEGEND
hLegend = legend(LegTxt,...
'interpreter','latex',...
'location','eastoutside')
(move the code block order to replicate the situations mentioned above)
How to reasonably fix this?
If you want a certain graphics object to not produce a legend (and that will work even if you toggle the legend off and on again), you can modify the LegendInformation:
%# plot something that shouldn't show up as legend
handleWithoutLegend = plot(something);
%# modify the LegendInformation of the Annotation-Property of the graphical object
set(get(get(handleWithoutLegend,'Annotation'),'LegendInformation'),...
'IconDisplayStyle','off');
%# toggle legend on and off at will, and never see the something-object appear
If you try to turn off the legend on an array of handles, the best way is just to loop over them, with a try-wrapper for graphical objects that cannot produce a legend:
for h = listOfHandles(:)'
try
set(get(get(h,'Annotation'),'LegendInformation'),...
'IconDisplayStyle','off');
end
end
Craft a custom handle that you feed into the legend. Plot handles can be concatenated to form an object that legend is happy to accept as input.
The required code isn't pretty, but it does work.
%**** Optional guidelines for periodicity
figure(1)
plot([2 2],[0,1],'k--'); hold on
%**** DATA
N = 4;
y=rand(5,N);
x=1:1:5;
for plotLoop=1:N;
LegTxt{plotLoop} = num2str(plotLoop);
%* Plot
figure(1)
% if statement to construct a handle for the legend later
if plotLoop==1
htot=plot(x,y(plotLoop,:));
else
h=plot(x,y(plotLoop,:));
% Append this info to the figure handle
htot= [htot, h];
end
hold on
end
%*****LEGEND
hLegend = legend(htot,LegTxt,...
'interpreter','latex','FontSize',16,...
'location','eastoutside')
For the pedantic or curious, the loop for plotLoop=1:N; is here because I extracted the example from some rather complex code where the data is extracted from cell arrays. Obviously you could eliminate that loop for a lot of usage scenarios, I just decided to keep the code in its most flexible format!
You can also hide plot from legend in another way. Here's the sample:
figure(1)
hold on
x=1:10;
y1=x;
y2=x.^2/10;
y3=x.^3/100;
plot(x,y1);
plot(x,y2,'HandleVisibility','off');
plot(x,y3);
legend('x','x^3')
You just need to put 'HandleVisibility', 'off' to your plot that you don't want to show up in legend. That's how result looks like:
HandleVisibility is a line property so it might now work if you create plot in some other way. But for most use cases its enough and it is much simpler.
Related
I'm working on a custom progress monitor with some graphs. I've noticed that Matlab's waitbar creates a figure with some special properties so that if you do
plot(rand(100,1));
wb = waitbar(0);
plot(rand(100,1));
the second plot ends up replacing the first plot and not in wb. Is there a property I can set so that when I create my progress monitor and then plot something afterwards, the graph doesn't end up in my figure?
To be clear, I'm trying to have
plot(rand(100,1));
temp = MyProgressBar();
plot(rand(100,1));
create a figure for the first plot, create a different figure in the second line, then plot a new graph in the third line.
To protect your progress bar figure against subsequent plotting operations, I would set the 'HandleVisibility' property of its axes to 'off'. That should prevent it ever becoming the current axes, thus keeping subsequent plotting commands from modifying or adding to it. It's a good practice for stand-alone figures/GUIs in general that you turn off the handle visibility of all objects (figure, uicontrols, etc.) in this way to insulate them against being modified by outside code. This is almost certainly what is done in the code for waitbar.
As an additional aside, it's good practice to target your plots to a given axes by passing the axes handle as the first argument. You also have to make sure that, if you want new plots to be added to existing plots, you use things like the hold command first. Here's how I'd rework your example, assuming you want the two plots to appear on the same axes:
plot(rand(100,1)); % Creates new figure and axes
hAxes = gca; % Get the axes handle
hold on; % Allow subsequent plots to be added
temp = MyProgressBar();
plot(hAxes, rand(100,1)); % Will be added to the first plot axes
I am plotting a closed loop poles of a systems using pzmap.m or pzplot.m whichever you prefer. I want to see the movement of poles and zeros as I change a variable depicted by L.
The function does not have a direct handle for color. In the example you can just choose the standard colors but cannot give your own color. Since I have to plot multiple times on the same figure, I create a handle for every iteration in a for loop and use findobj to set the color of the curve. To get the colors I want to have a colorbar. So I use jet to get the color distribution for my graph. But the market size stays the same and I have one ugly looking figure.
MWE:
cb=jet(10);
s=tf('s');
L_array=5:5:50;
figure; hold on;
for i=1:length(L_array)
L=L_array(i);
G=((58.2+11.7*L)*s^2*25^2+(3996.8 + 815.7*L)*s*25+815.7*25^2)/(s^2*(s^2*25^2+126.9*s*25+(3996.8+1.9*25^2)));
CL=feedback(G,1);
pzmap(CL);
h = findobj(gcf,'type','point'); set(h,'markers',12,'color',cb(i,:));
clear L G CL h
end
grid;
c=colorbar
c.Label.String = 'L'
If you run it you'll see that the size doesn't change and graph looks crazy with the y axis on either side labelled with ticks. I want a proper colorbar from blue to red and the colors distributed properly but can't get it after multiple tries. If I can get less cluttering that would be helpful too.
The are several problems in your code
h = findobj(gcf,'type','point'); The things drawn in the screen are actually of type 'line'!
Every iteration, you catch all the points, modify them an dimdediately after delete the properties with clear.
Additionally, h = findobj(gcf,'type','line'); will not return a single thing, but a set of them, so you need to index through it to set the properties. My modified code working looks like this:
clear;clc
cb=parula(10);
s=tf('s');
L_array=5:5:50;
figure; hold on;
for i=1:length(L_array)
L=L_array(i);
G=((58.2+11.7*L)*s^2*25^2+(3996.8 + 815.7*L)*s*25+815.7*25^2)/(s^2*(s^2*25^2+126.9*s*25+(3996.8+1.9*25^2)));
CL=feedback(G,1);
pzmap(CL);
end
% lets do this in the end!
% you will have length(L_array)*2 +1 items in h
h = findobj(gca,'type','line');
for jj=2:length(h)
set(h(jj),'MarkerSize',12,'Color',cb(floor(jj/2),:));
end
grid;
colormap(parula); % I am using parula, you can use jet, but I discourage it
c=colorbar;
PD: there are tons of reasons not to use jet!! Use perceptually uniform colormaps please!
I want to plot a graph, mark a point and write legend in a loop.
labels = {}
for i = 1: size(error,1)
r = plot(handles.axes1,temp(i).time,temp(i).signal);
hold (handles.axes1, 'on')
a = %find minimum index
xmin = temp(i).time(a);
ymin = temp(i).signal(a);
plot(handles.axes1,xmin,ymin,'ro')
labels{end+1} = sprintf('Type : %s, %f,%f',error{i,1}.main(1,1).type,xmin,ymin)
end
grid on
legend(r, labels);
Labeling does not work, as it takes only 1st elements ignoring extra element. and the whole method is a mess with color code all messed up, is there elegant way of doing this where my legend colour matches the plot ones
Another way to do this is to use the DisplayName keyword.
for i = 1:N_lines
%...
r(i) = plot(handles.axes1, temp(i).time, temp(i).signal, 'DisplayName', labels{i});
%...
end
legend('show')
The advantage of doing it like this is that it attaches the name directly to the plotted point. If you are debugging, and open the plot as its being written in the plot browser, as each point get plotted the name will appear next to the point in the right-hand pane. You also don't need to keep track of a separate labels variable in case you end up later reordering your points for some reason. This way the labels always travel with their associated points.
I should add that when you call the legend command with a cell of labels, among other things it backfills 'DisplayName' so that you can still change and query it after the plot has been built even if you don't explicitly set it like I've done above. However, I find this method to be self-documenting and more convenient because it's one less thing to keep track of.
You need to make r an array. As it is, you're only storing the last plot handle so when you call legend it's only going to use the last plot and the first label.
% Create a unique color for each plot
colors = hsv(size(error, 1));
for i = 1:size(error, 1)
r(i) = plot(handles.axes1,temp(i).time,temp(i).signal, 'Color', colors(i,:));
%...
labels{i} = sprintf('Type : %s, %f,%f',error{i,1}.main(1,1).type,xmin,ymin);
end
legend(r, labels)
As a side-note it's best to not use i or j as your looping variable and also, don't name your variable error as that's the name of a MATLAB built-in.
I have the following code in Matlab that runs through a for loop, reads data from a file and plots 9 different figures, that correspond to some particular "channels" in my data, so I decided to annotate them in the for loop.
clear
clc
for i=1:9
subplot(3,3,i);
hold on
x = [4 13]; % from your example
y = ([1 1]); % from your example
y2 = ([-0.4 -0.4]);
H=area(x,y,'LineStyle','none',...
'FaceColor',[1 0.949019610881805 0.866666674613953]);
H1=area(x,y2,'LineStyle','none',...
'FaceColor',[1 0.949019610881805 0.866666674613953]);
% Create textbox
annotation('textbox',...
[0.719849840255583 0.603626943005185 0.176316293929713 0.308290155440411],...
'String',{'FABLIGHT04','Channel',i},...
'FontWeight','bold',...
'FontSize',10,...
'FontName','Geneva',...
'FitBoxToText','off',...
'EdgeColor','none');
axis([0 24 -0.4 1])
set(gca,'XTick',[0:1:24])
set(gca,'YTick',[-0.4:0.2:1])
xlabel('Time (s)');
end
Initially it was giving me 9 different figures and the annotation thing worked fine. But I wanted to be able to tile them onto a subplot for easier comparison.
Since I switched over to using subplot, it does not annotate my figure properly. On opening the editing dock and generating the code, I find that matlab is plotting everything first and then just putting the annotation boxes in the same figure, one on top of the other. Looking at the code it generated, it apparently takes this part of the code:
annotation('textbox',...
[0.719849840255583 0.603626943005185 0.176316293929713 0.308290155440411],...
'String',{'FABLIGHT04','Channel',i},...
'FontWeight','bold',...
'FontSize',10,...
'FontName','Geneva',...
'FitBoxToText','off',...
'EdgeColor','none');
and does it as:
annotation(figure1,'textbox'...)
etc etc
So for all 9 text boxes, it puts them onto the same figure. I tried to do S=subplot(3,3,i) then annotation(S,'textbox') etc etc, I have also tried S(i)=subplot(3,3,i) and then annotation(S,'textbox') etc etc but nothing seems to work.
I have also tried to change the location of the box. I can't seem to figure out how to make it smaller either.
Does anyone know how to have annotation boxes in the right subplot in a for loop?
Thanks
I'm afraid annotation objects are properties of figures and NOT axes, as such its harder to customize the position of each annotation objects because no matter how many subplots you have, they are all part of the same figure and you need to specify their position relatively to the figure coordinate system.
Therefore, you can manually set the position of each text box in your code depending on the subplot it belongs to...
Simple example:
clear
clc
close all
figure('Units','normalized'); %// new figure window
for k = 1:2
str = sprintf('Subplot %d',k);
subplot(1,2,k)
plot(rand(1,10));
%// Customize position here
hAnnot(k) = annotation('textbox', [k*.4-.2 .6 .1 .1],...
'String', str,'FontSize',14);
end
Which looks like this:
Its not very elegant but I'm personally not aware of any other option if you do need to use annotations objects. A less cumbersome alternative would be to use a simple text objects, which are properties of axes and therefore much more friendly to position :)
Hope that helps!
Usually when I plot in MATLAB, it always draws on the same figure. How do I make it draw in a new figure?
I know it is pretty elementary, but I'm not finding it using Google Search.
figure;
plot(something);
or
figure(2);
plot(something);
...
figure(3);
plot(something else);
...
etc.
While doing "figure(1), figure(2),..." will solve the problem in most cases, it will not solve them in all cases. Suppose you have a bunch of MATLAB figures on your desktop and how many you have open varies from time to time before you run your code. Using the answers provided, you will overwrite these figures, which you may not want. The easy workaround is to just use the command "figure" before you plot.
Example: you have five figures on your desktop from a previous script you ran and you use
figure(1);
plot(...)
figure(2);
plot(...)
You just plotted over the figures on your desktop. However the code
figure;
plot(...)
figure;
plot(...)
just created figures 6 and 7 with your desired plots and left your previous plots 1-5 alone.
The other thing to be careful about, is to use the clf (clear figure) command when you are starting a fresh plot. Otherwise you may be plotting on a pre-existing figure (not possible with the figure command by itself, but if you do figure(2) there may already be a figure #2), with more than one axis, or an axis that is placed kinda funny. Use clf to ensure that you're starting from scratch:
figure(N);
clf;
plot(something);
...
As has already been said: figure will create a new figure for your next plots. While calling figure you can also configure it. Example:
figHandle = figure('Name', 'Name of Figure', 'OuterPosition',[1, 1, scrsz(3), scrsz(4)]);
The example sets the name for the window and the outer size of it in relation to the used screen.
Here figHandle is the handle to the resulting figure and can be used later to change appearance and content. Examples:
Dot notation:
figHandle.PaperOrientation = 'portrait';
figHandle.PaperUnits = 'centimeters';
Old Style:
set(figHandle, 'PaperOrientation', 'portrait', 'PaperUnits', 'centimeters');
Using the handle with dot notation or set, options for printing are configured here.
By keeping the handles for the figures with distinc names you can interact with multiple active figures. To set a existing figure as your active, call figure(figHandle). New plots will go there now.
Another common option is when you do want multiple plots in a single window
f = figure;
hold on
plot(x1,y1)
plot(x2,y2)
...
plots multiple data sets on the same (new) figure.
As simple as this-
figure, plot(yourfigure);