Why does copyobj() fail to copy over title fontsizes (and also colorbars and colormaps)?

I am attempting to copy a set of subplot axes from one figure to another with copyobj. Everything seems to copy over quite nicely except for the title FontSizes, as illustrated in the example below. Does anyone know why?
h1=figure;
ax(1)=subplot(1,2,1);
plot(1:5,'rx:'); axis square
title('A','FontSize',15);
ax(2)=subplot(1,2,2);
plot(rand(1,5),'sb--'); axis square
title('B','FontSize',15);
h2=figure;
for i=1:2
copyobj(ax(i),h2);
end

4 commentaires

I don't know why this happens but I suspect that there are some under-the-hood linkages in the axes object between various properties that control the title font size and maybe those linkages break or don't get copied over correctly. Did you try not setting TitleFontSizeMultiplier instead of setting the FontSize? I tried it for a full size axes (as opposed to a subplot where FontSizeMode might come into play?) and it seemed to work ok.
I think @Paul is on the right track. The fontsize multipliers of the new axes seem to take precedence over the fontsize properties of nested text objects.
The same problem happens if you set xlabel(ax(2),'X','fontsize',20) since the axes contain a LabelFontSizeMultiplier property.
One way to get around this is by copying the titles which are just text objects, but you'll need to remove the initial copy of the titles containing the wrong fontsize.
h1=figure;
ax(1)=subplot(1,2,1);
plot(1:5,'rx:'); axis square
title('A','FontSize',15);
ax(2)=subplot(1,2,2);
plot(rand(1,5),'sb--'); axis square
title('B','FontSize',15);
h2=figure;
for i=1:2
newax = copyobj(ax(i),h2);
newax.Title.String = ''; % clear copied title
copyobj(ax(i).Title,newax) % copy original title & props
end
Question potentially related to this one: link
To add a further example, colormaps and colorbars also don't copy over gracefully:
h1=figure;
for i=1:2
ax(i)=subplot(1,2,i);
imshow(rand(100));
colormap hot
colorbar
title(char('A'+(i-1)),'FontSize',20);
end
h2=figure;
for i=1:2
copyobj(ax(i),h2);
end

Connectez-vous pour commenter.

 Réponse acceptée

Matt,
The issue with copyobj and the FontSize on axes is a known bug. The issue relates to there being two sources of truth: the FontSize on the axes and the FontSize on the title. When you set the FontSize on the axes, MATLAB force that change down to the Title, whether you've manually specified the FontSize on the title or not.
ax = axes;
ax.Title.String = 'Title';
ax.Title.FontSize = 30;
ax.FontSize = 15; % This resets the FontSize on the Title.
ax.Title.FontSize % This has been updated to reflect the new FontSize * TitleFontSizeMultiplier
ans = 16.5000
The issue with copyobj is the sequence in which MATLAB is (internally) copying properties from the old axes to the new axes. MATLAB copies the title's FontSize before we copy the axes FontSize, so when the axes FontSize value is copied it replaces the value copied from the old title to the new title.
Unfortunately, I don't have any good suggestions for workarounds, except to separately copy the text object (not a great workaround, but it works):
h1 = figure;
ax = axes;
title('A','FontSize',15);
h2 = figure;
ax2 = copyobj(ax,h2);
h3 = figure;
ax3 = copyobj(ax,h3);
ax3.Title = copyobj(ax.Title,ax3); % Separately copying the text object after copying the axes will preserve title properties.

3 commentaires

The issue with colormap and colorbar is more subtle and is behaving correctly.
There are two issues:
  1. The colormap in this case is owned by the figure not the axes. The fix for this issue is to set the colormap on the axes instead of the figure.
  2. The code you wrote is only copying the axes, not the axes and colorbar. The fix for this issue is to copy both the axes and colorbar together.
For the first issue, consider this code, where I create an axes in one figure and move it to another figure. I've changed the colormap on the second figure. When I copy the axes into the new figure the new axes adopts the second figure's colormap.
f1 = figure;
ax = axes(f1);
surf(ax,peaks)
colorbar(ax)
f2 = figure;
colormap(f2,hot);
copyobj(f1.Children, f2); % The new axes adopts the colormap from the figure.
This code resolves both issues:
  • When calling colormap I specify I want to change the colormap on the axes (not the figure, which is the default behavior). By doing that, the axes owns its own colormap and it is preserved over a copy.
  • When calling copyobj I copy all the axes children (including the axes and colorbars), not just the axes.
h1 = figure;
for i = 1:2
ax(i) = subplot(1,2,i);
imshow(rand(100));
colormap(ax(i), hot) % UPDATED THIS LINE
colorbar
end
h2 = figure;
copyobj(h1.Children, h2) % Copy all the figure's children, including axes and colorbars.
Matt J
Matt J le 30 Juin 2022
Modifié(e) : Matt J le 30 Juin 2022
Thanks, Ben. But it makes me wonder (and I believe Yair also asked this at the online MAB) why colorbars are figure children rather than axes children (or tiledchartlayout children)? It seems natural that if you copy an axes, you would want the colorbar to go with it.
"why colorbars are figure children rather than axes children": That's a good question. I'm not sure why that design was chosen back when it was decided (it long predates my time at MathWorks), but if I had to guess it is due in large part to the fact that prior to R2014b colorbars and legends were just axes in disguise, and there was no functionality that allowed you to make an axes the parent of another axes.
I also agree that the colorbars (and legends) should be children of the axes. If there were an easy way to change that design, I would.
However, when you put an axes + colorbar into a TiledChartLayout, the colorbar is a child of the TiledChartLayout. Basically, in all scenarios currently, axes and colorbars (and legends) are siblings that share a parent, whether that parent is a figure, panel, or TiledChartLayout.

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur Color and Styling dans Centre d'aide et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by