MATLAB Answers

How can I save a figure within app designer?

552 views (last 30 days)
J. Webster
J. Webster on 27 Apr 2016
Edited: Adam Danz on 21 Apr 2020
I've developed an application using the new App Designer. I'd like to have users be able to click a button and save a figure to a .fig file, or some other image format.
function ButtonSaveFigureButtonPushed(app)
newfigure = figure;
copyobj(app.UIAxesAP, newfigure);
hgsave(newfigure, 'testFIgure.fig');
end
But that gave me the error...
Error using matlab.ui.control.UIAxes/copyElement (line 1219)
Functionality not supported with UIAxes. For more information, see Graphics Support in App Designer.
What's the best way to go about this?

  1 Comment

Devin Crawford
Devin Crawford on 4 Oct 2017
Based on the number of recent views this is still an open issue. I have the same problem/question using R2017a.

Sign in to comment.

Accepted Answer

David
David on 4 Apr 2018
Edited: David on 4 Apr 2018
I spent some time on this for my own purposes. The trick is to save the individual values from the figure into local variables to be used in a figure as follows:
h = figure;
h.Visible = 'off';
x = UIAxes.XAxis.Parent.Children.XData;
y = UIAxes.XAxis.Parent.Children.YData;
plot(x,y)
lgndName1 = UIAxes.Legend.String{1};
lgd = legend(lgndName1);
lgd.Box = UIAxes.Legend.Box;
lgd.Location = UIAxes.Legend.Location;
h.CurrentAxes.YLabel.String = UIAxes.YLabel.String;
h.CurrentAxes.YLabel.FontSize = UIAxes.YLabel.FontSize;
h.CurrentAxes.XLabel.String = UIAxes.XLabel.String;
h.CurrentAxes.XLabel.FontSize = UIAxes.XLabel.FontSize;
h.CurrentAxes.Title.String = UIAxes.Title.String;
h.CurrentAxes.Title.FontSize = UIAxes.Title.FontSize;
h.CurrentAxes.XLim = [0 max(x)];
h.CurrentAxes.XLim = [0 max(y)+1];
saveas(h,SaveName,'jpg')
savefig(h,SaveName)
delete(h)
Notice I have the visibility off, because I don't want the user to see this going in the background.
Note: You will have to change "UIAxes" to whatever you've named your UI figure
You could also add more properties, but you will have to go into your figure and find the object name to use.
I hope this helps.
Edit: I should also mention that to get this to work as function, I had to make it a call function in a separate .m file. I made a function SaveFigures(UIAxes, SaveName) that is called when ButtonSaveFigureButtonPushed. I have multiple possible plots, which is why I allow the UIAxes to vary. (I use a switch case to make sure I save the correct plot.)

  6 Comments

Show 3 older comments
Eric Long
Eric Long on 4 Jun 2018
David, would it be possible to see more of this code? I am close to getting my app to do the same thing (I believe) that you wrote yours to do, but am missing something fairly minor.
David
David on 15 Oct 2018
Eric, it might be easier to see your code. (Sorry for the delay, I did not get an email update on your comment.)
NE
NE on 5 Mar 2019
Just a quick remark on otherwise very useful reply:
h.CurrentAxes.XLim = [0 max(y)+1];
Shouldn't that be
h.CurrentAxes.YLim = [0 max(y)+1];

Sign in to comment.

More Answers (12)

Joost
Joost on 2 Oct 2018
Edited: Joost on 3 Oct 2018
Inspired by David's answer , I came up with this solution which I believe is more generic. My UIAxes contains a lot of graphics objects (mostly patch objects) which are all copied to a temporary figure and then saved. I took over some UIAxes properties (axes limits and data aspect ratio), but you can add any other property you need there. Specify fileName yourself. I put a button called 'Snapshot' in the app with a callback that contains the code below. Matlab R2018a was used.
% Create a temporary figure with axes.
fig = figure;
fig.Visible = 'off';
figAxes = axes(fig);
% Copy all UIAxes children, take over axes limits and aspect ratio.
allChildren = app.UIAxes.XAxis.Parent.Children;
copyobj(allChildren, figAxes)
figAxes.XLim = app.UIAxes.XLim;
figAxes.YLim = app.UIAxes.YLim;
figAxes.ZLim = app.UIAxes.ZLim;
figAxes.DataAspectRatio = app.UIAxes.DataAspectRatio;
% Save as png and fig files.
saveas(fig, fileName, 'png');
savefig(fig, fileName);
% Delete the temporary figure.
delete(fig);

  12 Comments

Show 9 older comments
Joost
Joost on 23 Oct 2019
Thank you Steven.
Ans thanks for the praise Jichen.
Adam, nice work! Good that we can learn from each other and keep improving in this community.
jichen guo
jichen guo on 23 Oct 2019
Hi Stevan,
Okay, I understand and sorry for the trouble.

Sign in to comment.


Adam Danz
Adam Danz on 24 Mar 2019
Edited: Adam Danz on 6 Dec 2019
update: try out copyUIAxes() from the file exchange
copyUIAxes() recreates a UIAxes in a new figure. It copies the UIAxes children and most of its properties including the legend and colorbar if requested.
Download it here:
It's based on the ideas in my original post below but has several improvements. Once you have the function, to recreate the UIAxes in a new figure, just run this line (assuming the axis handle is app.UIAxes).
copyUIAxes(app.UIAxes)
[original post]
Here's a more general solution that 1) copies all objects onto new external axes and then 2) copies most of the UIAxes properties on to the new axes. This last step is what's missing from the solutions proposed to date.
Some axes properties are read-only so we can't copy those over to the new axes. Also, there are some axes properties that you don't want to copy over such as parent, children, X/Y/ZAxis, which will cause errors. In addition to those, you'll see in the code below where I added the "Position" and "OuterPosition" properties to the list of properties not to copy. In your app, the axes are probably small and if you want your new axes to also be the same size, you could remove the two positoin properties from the 'badFields' list.
% Create new figure and new axes
figure
axNew = axes;
% Copy all opjects from UIAxes to new axis
copyobj(app.UIAxes.Children, axNew)
% Save all parameters of the UIAxes
uiAxParams = get(app.UIAxes);
uiAxParamNames = fieldnames(uiAxParams);
% Get list of editable params in new axis
editableParams = fieldnames(set(axNew));
% Remove the UIAxes params that aren't editable in the new axes (add others you don't want)
badFields = uiAxParamNames(~ismember(uiAxParamNames, editableParams));
badFields = [badFields; 'Parent'; 'Children'; 'XAxis'; 'YAxis'; 'ZAxis';'Position';'OuterPosition'];
uiAxGoodParams = rmfield(uiAxParams,badFields);
% set editable params on new axes
set(axNew, uiAxGoodParams)
For trouble shooting, or to loop through each property rather then setting them all at once, replace the last line of the code above with this for-loop below which indicates the property being edited in the command window.
% Set new axis params
allf = fieldnames(uiAxGoodParams);
for i = 1:length(allf)
fprintf('Property #%d: %s\n', i, allf{i});
set(axNew, allf{i}, uiAxGoodParams.(allf{i}))
end
One alternative worth mentioning: Instead of your app hosting the axes, you could program your app to plot the data onto an external figure and then you wouldn't have this problem.
[addendum] : copying legend
In app designer, the legend handle is stored within the axis handle structure in app.UIAxes.Legend. The legend has its own set of properties so to copy the legend, you follow the same process as above but there are a different set of problematic properties to remove prior to copying.
% Create new legend
legNew = legend(axNew);
% Save all parameters of the old legend
legParams = get(app.UIAxes.Legend);
legParamNames = fieldnames(legParams);
% Get list of editable params in new legend
editableLegParams = fieldnames(set(legNew));
% Remove the legend params that aren't editable in the new legend (add others you don't want)
badLegFields = legParamNames(~ismember(legParamNames, editableLegParams));
badLegFields = [badLegFields; 'Parent'; 'Children'; 'Position'; 'UIContextMenu'];
legGoodParams = rmfield(legParams,badLegFields);
% set editable params on new legend
set(legNew, legGoodParams)

  6 Comments

Show 3 older comments
Adam Danz
Adam Danz on 24 Oct 2019
@jichen guo, If you're using the approach from my answer, I suggest using the copyUIAxes() function I created which is an improved version of my answer. I'd be glad to get your feedback.
Ramu Pradip
Ramu Pradip on 21 Apr 2020
copyUIAxes was doing the trick! Thanks, but with 2 Y axes only one is copied to the new figure.
Adam Danz
Adam Danz on 21 Apr 2020
Thanks for the feedback, Ramu Pradip. I'll think about implementing yyaxis functionality in copyUIAxes.
In the mean time, here's a simple way to use copyUIAxes with yyaxis plots. It copies the left and right axes individually (creating two axes on top of eachother).
% Create yyaxis plot within uiaxes
fig = uifigure();
ax = uiaxes(fig);
yyaxis(ax, 'left')
plot(ax, 1:5, rand(1,5), '-')
ylabel(ax, 'left')
yyaxis(ax, 'right')
plot(ax, 1:.2:5, rand(1,21), 'o')
ylabel(ax, 'right')
xlabel(ax, 'x axis')
title(ax, 'Title')
% Copy left and right axes to the same figure
newFig = figure;
yyaxis(ax, 'left')
axLeft = copyUIAxes(ax, newFig);
yyaxis(ax, 'right')
axRight = copyUIAxes(ax, newFig);
% Make some changes to the right axes
axRight.axes.Color = 'none'; % make transparent
axRight.axes.XTick = []; % remove duplicate x-ticks
axRight.axes.XLabel = []; % remove duplicate x-label
axRight.axes.Title = []; % remove duplicate title

Sign in to comment.


Guilherme Salgado Braga
Guilherme Salgado Braga on 24 Feb 2018
According to the R2017b documentation on: https://www.mathworks.com/help/matlab/creating_guis/graphics-support-in-app-designer.html
Functions saveas and savefig are not yet supported.

  0 Comments

Sign in to comment.


Dharmendra Sharma
Dharmendra Sharma on 15 Jun 2018
Edited: Dharmendra Sharma on 15 Jun 2018
This is the one potential solution which works for me. First, plot the figure/figures in normal way without using uifigure for example see code below. Visibility is on in the following example-
figure('Name','Acc','NumberTitle','off','units','normalized','outerposition',[0 0 1 1])
Then create a separate matlab function file (and call that matlab function from matlab ui). the function may include code to extract figure properties and save the figures as png file. The following link explains the saving all the figures and the code as well-- see the link--
It just need some modifications and I included these in follwoing code-
function [] = handleFigures()
result = isfolder('figures');
if result==0
mkdir figures;
else
delete('figures/*.*')
end
FolderName = 'figures'; % Your destination folder
FigList = findobj(allchild(0), 'flat', 'Type', 'figure');
for iFig = 1:length(FigList)-1
FigHandle = FigList(iFig);
FigName = get(FigHandle, 'Name');
saveas(FigHandle, fullfile(FolderName, [FigName, '.png']))
end
disp('closing figures');
close all
end

  0 Comments

Sign in to comment.


Chao Gong
Chao Gong on 19 Jun 2019
Hello David:
I am using the same method answer by you David on 4 Apr 2018. However, I tried to open the saved .fig files afterwards using Matlab, but it doesn't open or respond. The .png files are saved nicely.
Do you encounter similar problem? Is there any possible reason why the saved .fig can't be open?
Thanks

  0 Comments

Sign in to comment.


Chao Gong
Chao Gong on 19 Jun 2019
Hi,
Found the issue of this problem. it's because the 'Visible' property is set 'off'. A workaroudn is as below.
--------
This is not currently possible in MATLAB.
As a workaround, please specify 'visible' option when calling 'openfig' to open the figure after it has been saved.
You can also set the "CreateFcn" property of the figure to a function which sets the "Visible" property to on. This allows you to save the figure when invisible but always make it visible when opening it later.
hFig = figure('Visible', 'off');
plot(1:10)
% Set CreateFcn callback
set(hFig, 'CreateFcn', 'set(gcbo,''Visible'',''on'')');
% Save Fig file
savefig(hFig, 'savedFigure.fig')

  0 Comments

Sign in to comment.


J. Webster
J. Webster on 28 Apr 2016
anybody??

  1 Comment

Tausif
Tausif on 10 Mar 2017
did u get a solution yet? I was looking to do the same

Sign in to comment.


Tobias Daßler
Tobias Daßler on 20 Dec 2017
I used a workaround:
saveas(gca,uiputfile({'*.png'; '*.fig'; '*.jpg'}));
close Plot;
Maybe you could adapt this for your problem.

  2 Comments

Adam Danz
Adam Danz on 24 Mar 2019
Functionality not supported with figures created with the uifigure function. So, this doesn't work with axes created in app designer.

Sign in to comment.


Craig Pearson
Craig Pearson on 8 Jan 2018
I'm having a similar problem and would like a solution. I was trying to use the print function to copy a figure to the clipboard.
Annoyingly, if I use debugging mode, once the figure is created I can quit debugging mode then use
print -dmeta
from the command window and it proceeds fine - I can then paste this into Excel from the command line (which is what I'm aiming to do from within the app).
Why can this not be achieved from within the App?

  0 Comments

Sign in to comment.


Blanca Larraga
Blanca Larraga on 4 Jun 2018
Is there a solution for saving the axes plot in a .jpeg format or not yet? I am trying to insert what I got un an axes figure within a report automatically in app designer but I don't manage to do so. Thanks.

  0 Comments

Sign in to comment.


Jyothi Karri
Jyothi Karri on 20 Jul 2018
I have the same issue. Cannot export a UIAxes from gui created from app designer. copyobj did not work either.

  1 Comment

Udo Schröder
Udo Schröder on 13 Aug 2018
I have the same issue. It would help if there would be a nice option just to map an UIAxes element into a normal (pop-up) figure. Then the figure could be saved with the common tools.

Sign in to comment.


Paramjeet Panwar
Paramjeet Panwar on 15 Oct 2018
My current workaround is identifying the pixel position of the UIFigure and UIAxes on the screen and taking a snapshot using Java libraries.
Refer to these links -
2 Using Java library for screenshot - this link

  0 Comments

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!

Translated by