Programmatically performing "Expand axes to fill figure"?

182 Ansichten (letzte 30 Tage)
Nancy Jestel
Nancy Jestel am 8 Aug. 2017
Beantwortet: raym am 11 Dez. 2022
I need to automate generating figures and exporting them (usually to png). I can get what I need by using "File: Export Settings" & clicking "Expand axes to fill figure", but I really do not want to do this as many times as would be needed. How do I do it from a script or function? I have hunted through all of the properties options that I can think of but can't find that one - or can't get other options to work right. Thank you.

Akzeptierte Antwort

Jan
Jan am 8 Aug. 2017
To set the position as tight as possible:
figure;
AxesH = axes;
drawnow;
InSet = get(AxesH, 'TightInset');
set(AxesH, 'Position', [InSet(1:2), 1-InSet(1)-InSet(3), 1-InSet(2)-InSet(4)])
  6 Kommentare
Joshua
Joshua am 11 Jun. 2021
This doesn't work for Legends outside the plotting area, e.g. with:
legend('show','Location','Southoutside')
I used the following with good results:
% Create legend and get handle
leg = legend(h,'Location','Southoutside','Orientation','horizontal','NumColumns',10);
LegPos = leg.Position;
% Resize axis
InSet = get(gca, 'TightInset');
set(gca, 'Position', [InSet(1), InSet(2)+LegPos(2), 1-InSet(1)-InSet(3),...
1-InSet(2)-InSet(4)-LegPos(2)]);
It works as long as your Legend does not extend outside of the axes extents horizontally.
Captain Karnage
Captain Karnage am 30 Nov. 2022
Bearbeitet: Captain Karnage am 30 Nov. 2022
In my figure, InSet(1) and InSet(4) are large numbers > 1 and InSet(3) is close to 0.
So, 1-Inset(1)-InSet(3) and 1-Inset(2)-Inset(4) are both giving me negative numbers, resulting in an error for me.
Update: It is different for me, because the Axes I was trying to copy was a UIAxes object - which behaves differently than a standard Axes Object.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (4)

TastyPastry
TastyPastry am 8 Aug. 2017
You can grab the axes handle with something like
h = axes;
Now, when you finish plotting on the axes with the handle h, you can run
set(h,'position',[0 0 1 1])
This will expand your plottable area to fill the figure. However, it may also cut off any title or axis labels you have, so you need to plan accordingly.
Something like
set(h,'position',[.05 .05 .9 .9])
will allow you to see more of the text outside of the plottable area.
Note that you can also use dot operator to set axes properties, but if you're running on older versions of MATLAB you'll need to use the get() and set() functions to modify properties.
  1 Kommentar
Nancy Jestel
Nancy Jestel am 9 Aug. 2017
Thank you for your answer. I have tried to get the position settings to work but I always run into trouble predicting how much to cut off for axis labels and titles since their length changes. I gave up and switched to the interactive tool, but I think Jan's answer below will do the trick.

Melden Sie sich an, um zu kommentieren.


Captain Karnage
Captain Karnage am 30 Nov. 2022
I had to figure my own solution. This won't be applicable to everyone reading this, but I'm posting my version here in case anyone else in my situation comes across this and hopefully this will help someone.
One source of confusion on this is there are two classes of axes, Axes and UIAxes
These two are very, very similar and have very, very similar properties - but their properties are not exactly the same. One difference, in particular, is the Position property:
  • For an Axes object, Position is "normalized" to 1 and all numbers in the array must be between 0 and 1. The resulting size of the Axes will then be proportional to the available space in the figure.
  • For a UIAxes object, Position is an absolute value, in units of pixels. For this case, you have to know the size of the available space in the figure in order to expand.
What makes it weirder, is when you create a Figure in MATLAB, and add axes the normal way, they are of class Axes. However, if you have a GUI or, have otherwise generated an object of type UIAxes, you can copy the UIAxes object into a figure. It's nearly indistinguishable from the standard Axes object, but it behaves differently programmatically. For one, it doesn't autosize with the figure window. If you resize the figure, the UIAxes will not resize with it, whereas Axes being values relative to the size of the figure, will automatically resize.
For that, I created a function, resizeAxes, which will resize either type of axes:
function handleAxes = resizeAxes(axesSource, percentFill)
%resizeAxes Resizes the axes in a figure per the desired percent
% Adjust size of passed axes to a percentage of the parent figure
%% "Constants" (MATLAB does not have constants, unless you do constant classes
paramWidth = 3; %Element number of the "Width" in the Position property array for Axes or UIAxes
paramHeight = 4; %Element number of the "Height" in the Position property array for Axes or UIAxes
classAxes = 'matlab.graphics.axis.Axes';
classUIAxes = 'matlab.ui.control.UIAxes';
%% Check Input Parameters and set defaults
%Set to 100% if Percent not povided or invalid
if ( ( nargin < 2 ) || ( ~isnumeric(percentFill) ) || ( percentFill > 1 ) )
percentFill = 1;
end
%Assume current axes if none provided or invalid input provided
%Future: get axes by name if a string passed?
if ( ( nargin < 1 ) || ( ~isa( axesSource, classAxes ) && ~isa( axesSource, classUIAxes ) ) )
%Gets current axes (creates a new one if none exists)
thisFigure = gcf;
axesSource = gca;
else
thisFigure = axesSource.Parent;
end
%% Perform resizing procedure
%Get figure position and size
positionThisFigure = get( thisFigure, 'Position' );
thisFigureWidth = positionThisFigure(paramWidth);
thisFigureHeight = positionThisFigure(paramHeight);
marginAxes = ( 1 - percentFill ) / 2;
%Position array is left, bottom, right, top or left, bottom, width, height despite MATLAB documentation to the contrary
%Set the position array according to the Axes type
if ( isa( axesSource, classUIAxes ) )
%`UIAxes` position is absolute in units of pixels
%Calculate the pixel values required
widthAxes = percentFill * thisFigureWidth;
heightAxes = percentFill * thisFigureHeight;
leftAxes = marginAxes * thisFigureWidth;
bottomAxes = marginAxes * thisFigureHeight;
%Set up Position property array with calculated absolute values
positionAxes = [ leftAxes, bottomAxes, widthAxes, heightAxes];
else %Assuming class is standard `Axes`
%`Axes` position is normalized from 0-1
%Set up Position property array with normalized values
positionAxes = [ marginAxes, marginAxes, 1 - marginAxes, 1 - marginAxes ];
end
set( axesSource, 'Position', positionAxes );
%Make sure drawing is updated after this occurs
drawnow;
%Return handle to the axes
handleAxes = axesSource;
end
It will run with either the axes you want passed into it as either an Axes or UIAxes object - or if the passed variable is not a valid axes of either type, it will run on the current axes.
To handle automatically resizing the axes inside the figure, I also created an autoResizeAxes function, as follows:
function handleAxes = autoResizeAxes( figureSource, eventSource, percentFill )
%autoResizeAxes Callback function to automatically resizes axes when a figure size is changed
% When calling object (figureSource) is changed, automatically adjust the
% internal axes
%% "Constants" (MATLAB does not have constants, unless you do constant classes
classFigure = 'matlab.ui.Figure';
%% Check Input Parameters and set defaults
%If percentFill not passed, assume default of 100%
%NOTE: no additional validation needed as that validation will occur when
%passed to the resizeAxes function
if ( nargin < 2 )
percentFill = 1;
end
%If no figure provided, assume current figure
if ( ( nargin < 1 ) || ~isa( figureSource, classFigure ) )
figureSource = gcf; %It may appears this variable is not used later, but this ensures that when gca is ran, it is pulling it from figureSource
else
%Ensures the passed figure is the current figure
set(0, 'currentfigure', figureSource)
end
%Sets thisAxes to current axes on the current figure (which was set in prevoius block)
% or creates new axes in current figure and makes them current
%Current: Assumes only one set of axes in figure (current set)
%Future: Get number of axes and arrangement and resize each accordingly
thisAxes = gca;
handleAxes = resizeAxes( thisAxes, percentFill );
end
This is a much simpler function, but it's necessary to have it's own function so it can be used as a callback function for the figure. In order to utilize it as such, you will have to set the SizeChangeFcn callback to the autoResizeAxes function as follows:
thisFigure.SizeChangedFcn = { 'autoResizeAxes', percentFill };
Where thisFigure is the figure you've created.
To get the desired amount of fill, set percentFill to the desired value from 0 to 1 for 0 to 100%.
One caveat, these functions assume there is only ONE set of axes in the figure. If you need/have more than one, you will need to come up with your own code to separate and resize appropriately.

Salai Selvam V
Salai Selvam V am 19 Jul. 2019
Thank, ji. It helped me really great...thanks once again.

raym
raym am 11 Dez. 2022
works not very well if there are text or annotation created outside the plot area as they will be partially cropped.
axis ticklabels may also be cropped if the ticklabel is long.

Kategorien

Mehr zu Graphics Object Properties finden Sie in Help Center und File Exchange

Produkte

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by