Often in Matlab we would plot a figure with many subplot axes, but they are all tiny.
In the prevalent UX paradigm, you would expect to be able to double-click such a small plot to have a closer look using the entire screen space.
Typically this is the reason I avoid using subplot, but plot many individual figures instead — so I can move them around on the screen and double-click on their titlebars which (on Windows) maximises the figure to full screen. (Double-click again, and it returns to its normal size.)
However, the advantage of subplot is that the set of plots is grouped in one panel. When I'm plotting many such groups (each with a dozen separate subplot axes), having that many individual figures becomes hard to work with.
So, is there a way to enable this functionality in Matlab already?
Combining parts of these three posts, here's what I have so far:
h = subplot(2,2,1);
line(1:10, rand(1,10));
set(h, 'buttondownfcn', ['h=gca; hc = copyobj(h, gcf);' ...
'set(hc, ''Units'', ''normal'',' ...
' ''Position'', [0.05 0.1 0.8 0.85],' ...
' ''buttondownfcn'', ''delete(gca)'');']);
It's not perfect, but it works.
Click on the axes:
Click on the expanded axes, and it disappears:
Note that this still allows you to pan, zoom, and "rotate 3D" the resulting axes. Selecting the arrow tool actually enters "Edit Mode", so it's better to unselect the tool you are using instead. For example: if you were zooming in, click on the zoom-in icon again to deselect the tool. Clicking will then "collapse" the blow-up of the axes.
The limitation so far is that you can sometimes see parts of the underlying little subplot axes underneath. If someone can recommend an elegant way to hide them, that would be a nice improvement.
EDIT Learning from this answer (using a uipanel to prevent other contents from showing through), I now have turned the solution into this:
gcaExpand.m:
function gcaExpand
set(copyobj(gca, uipanel('Position', [0 0 1 1])), ...
'Units', 'normal', 'OuterPosition', [0 0 1 1], ...
'ButtonDownFcn', 'delete(get(gca, ''Parent''))');
end
gcaExpandable.m:
function gcaExpandable
set(gca, 'ButtonDownFcn', [...
'set(copyobj(gca, uipanel(''Position'', [0 0 1 1])), ' ...
' ''Units'', ''normal'', ''OuterPosition'', [0 0 1 1], ' ...
' ''ButtonDownFcn'', ''delete(get(gca, ''''Parent''''))''); ']);
end
The first one expands the current plot immediately. The second one adds the functionality where clicking onto the plot expands it. In both cases clicking again returns things back to normal.
I've placed them into the directory with all my other custom Matlab functions that I'm using on a day-to-day basis. The above can also be included in functions to be sent out.
Initially, I was going to write a custom version of subplot that applied gcaExpandable automatically, but that didn't work, because commands like plot erase the ButtonDownFcn property (as well as all other properties, except the position). According to this answer, we can avoid resetting those properties by changing the NextPlot to 'replacechildren', but that has side-effects. For example, plot no longer automatically rescales the axes. Therefore, the cleanest solution so far seems to be as above.
Related
MATLAB 2016a introduced Live Scripts, allowing to show plotting output next to the script. Is it somehow possible to show animations? For example, the following code in a regular script will plot a few points and then rotate the axes:
x = rand(10, 3);
plot3(x(:, 1), x(:, 2), x(:, 3), 'o')
for ii = 1:360
camorbit(1, 10*cos(ii/90*pi)*pi/45)
drawnow
pause(0.01)
end
If this is embedded in a Live Script, the initial plot is shown, then seemingly nothing happens while the loop is running, then the last aspect (which is the same as the original plot) is shown in a new display item.
Alternatively, is there an option to interact with the plots in a live script (other than double-clicking to open the plot in a new figure)? E.g. rotate3d does not have an effect.
Edit: As of release 2019a, animations are possible as per release notes.
It seems the answer is no - Live Scripts are too young to be that feature rich yet. The fact alone that they're undebuggable would make me stay away from them for 1-2 versions more.
Have you looked into Matlab Notebooks? If you're after pretty formatting and some basic interactivity, It might be what you seek.
Release 2016b added the option to manipulate axes with controls that come into view when hovering over the axes. Note that this does not work for axes that are invisible (Visible='off'). Instead, the rulers and background have to be hidden:
ax = axes;
x = rand(9, 3);
plot3(ax,x(:, 1), x(:, 2), x(:, 3), 'x');
% Hide rulers and background color
ax.Color = [1 1 1 0];
ax.XAxis.Visible ='off';
ax.YAxis.Visible ='off';
ax.ZAxis.Visible ='off';
Axes arranged with subplot can be manipulated individually as well.
The example code as posted in the question produces a rotating plot as of MATLAB 2019a. It does not work yet in 2018b. The release notes for 2019a mention that
You can enable for-loop animations in the Live Editor to show changes
in plotted data over time. To enable animations in the Live Editor,
set the matlab.editor.AllowFigureAnimations setting to true:
s = settings;
s.matlab.editor.AllowFigureAnimation.PersonalValue = true;
Running these two lines before the example script will yield the expected behaviour.
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!
I'm making a plot and using the legendflex function off of the File Exchange and I keep running into a problem where I have an element of the figure expanding off one side. Is there a way to expand the figure window so I can see everything being plotted?
A trivial example would be as follows:
bar(rand(10, 2))
legendflex({'First Series', 'Second Series'}, 'anchor', [4 8])
I've noticed that there's a section to the documentation in legendflex that talks about a resize function callback, but when I set that as empty and resize the figure, the legend ends up in the middle of the plot. I've tried altering the Position and OuterPosition properties but the plot always expands within the figure and the legend moves with it and continues off the edge of the figure (or doesn't move at all if I mess with the resize function).
Does anyone have a way to expand the size of a figure or "zoom out" without altering the underlying plots?
While not exactly answering the original question, the following code does produce the desired effect:
bar(rand(10, 2));
ax = gca;
ax.Position = [ax.Position(1:2) ax.Position(3)*.75 ax.Position(4)];
legendflex({'First Series', 'Second Series'}, 'anchor', [4 8]);
This resizes the axes before adding the legend so that when it is added there's space to the right of the figure.
The following commands produce some very strange results -
plotyy(1:3,2:4,3:5,4:6)
hold on
plotyy(1:3,2.1:4.1,3:5,4.1:6.1)
I basically want to plot two different series on the left y axes and two more series on the right y axes. The above commands work fine for the left series, but produce weird results for the right one. The second green line doesn't look like it should.
The problem that you are having is related to the way that the plotyy creates they plot. plotyy creates two different axes that it plots on, and then mounts them into a single figure. When you issue the hold on command, you are only freezing one of the axes. To fix this, you need to hold each one individually, and then plot back onto them using the plot command.
[ax,hl,hr] = plotyy(1:3,2:4,3:5,4:6);
hold(ax(1), 'on')
hold(ax(2), 'on')
plot(ax(1), 1:3,2.1:4.1)
plot(ax(2), 3:5,4.1:6.1)
Indeed pretty weird behavior. For fun, select the 'hand' tool in the plot window and then drag the graph around, you see that only one of the two green curves moves and that on the right side there are two sets of labels drawn on top of each other. I would qualify this as a bug in matlab (far from the only ugly behavior in Matlab's plots). This seems to be a workaround for what you want to achieve:
[AX, H1, H2] = plotyy(1:3, [2:4;2.1:4.1], 3:5,[4:6;4.1:6.1]);
>> set(H1, 'color','b')
>> set(H2, 'color','g')
Note that this only works if the two left plots have the same set of x-values, and similar for the right plots, like in your case. A=[4000;0;1]. But this is a workaround, the real solution is given by slbass.
This should be a problem with a trivial solution, but still I wasn't able to find one.
Say that I have 2 matlab figures fig1.fig, fig2.fig which I want to load and show in the same plotting window.
What should I do?
I mean, I am pretty sure that I can accomplish the task using some low(er) level graphic command which extracts contents from one image and put them in the second one, nonetheless I cannot believe that there is not any high level function (load fig2 on top of fig1) that does this...Comparing 2 plots (unfortunately already saved) is a very common task, I'd say.
Its not clear if you want to extract data from the figures and compare the data, or if you want to combine the plots from two figures into a single figure.
Here is how you combine two figures into one (if thats what you want to do)..
First load the figures:
fig1 = open('FigureFile1.fig');
fig2 = open('FigureFile2.fig');
Get the axes objects from the figures
ax1 = get(fig1, 'Children');
ax2 = get(fig2, 'Children');
Now copy the hangle graphics objects from ax2 to ax1. The loop isn't neccesary if your figures only have a single axes
for i = 1 : numel(ax2)
ax2Children = get(ax2(i),'Children');
copyobj(ax2Children, ax1(i));
end
Note This example assumes that your figures have the same nubmer of axes and that you want to copy objects from the first axes in the second figure to the first axes on the first figure. Its up to you to figure out the proper indexing if the axes indices aren't lined up.
The answer slayton gave is good. Here's another tip: If you have two plots opened in two separate Matlab figure windows, don't forget you can point-and-click copy the proper plots. Do this by clicking the arrow pointer in the Matlab figure window, and then clicking on the plotted line. Copy the (plotted line, textbox, etc...) object. Then, similarly select the axis in the other Matlab figure window and paste it.
I give this 'silly' solution because it has proven to be useful in in collaboration meetings. Point-and-click copying in front of someone (like your adviser) communicates exactly what curves are being compared, and it prevents you from having to fire up code in front of others.
You can also go to File in the menu, Generate Code, for each plots.
Then copy and paste both in the same mfile, with a "hold on" in between and changing details related to the appearance.
Then run the new m-file.