Setting a title for a legend

1,046 ビュー (過去 30 日間)
Eyal Ben-Hur
Eyal Ben-Hur 2017 年 2 月 13 日
コメント済み: Eyal Ben-Hur 2018 年 9 月 18 日
The following code is the most minimal example that I could find. My true case is much more complicated:
x = 1:0.1:10;
y = sin(x);
subplot 211
plot(x,y)
[leg,att] = legend('show');
title(leg,'my title')
leg.Title.Visible = 'on';
subplot 212
plot(x,y)
leg = legend('show');
title(leg,'my title')
This results in:
As you can clearly see, something is wrong with the upper legend title. Somehow, asking for the att output of the legend interfere with its' title. First, for some reason, it makes it invisible, but that is already solved in the code above.
The main problem is with its position - it doesn't seem to have such a property, so once set I can't move it.
I can think of some similar hacks by myself (like using text with the position of the legend), but my situation is very complicated, and I already configure the legend a lot and have several axes in every figure. Thus, I prefer a simple and working solution that rely on the original functionality of the legend title.
I use Matlab 2016a.

採用された回答

Eyal Ben-Hur
Eyal Ben-Hur 2017 年 6 月 23 日
I found the answer after some research of the undocumented properties of the legend. The most direct answer to the problem in the question is to set the position of the legend title back to its place. This can be done with the property Position, that is part of the hidden property NodeChildren, of the legend title.
The legend box is effectively just a small axes object, so the position units are normalized relative to the axes box. If we look at it just after the creation of the legend we get:
[hleg,att] = legend('show');
title(hleg,'my title')
hleg.Title.NodeChildren.Position
ans =
0 0 0
Which is where we see the legend in the question. Now we can move it back to its correct place with:
hleg.Title.NodeChildren.Position = [0.5 1.5 0];
Here I placed it at the center of the horizontal axis (x = 0.5), above the vertical axis (y = 1.5), and the depth axis is irrelevant so it's zero (z = 0):
If you are not bothered by the option that the legend will interfere with the data, (e.g. your legend is outside the data axes) then you can stop here. If not, continue to read...
We could set the background color to white:
hleg.Title.NodeChildren.BackgroundColor = 'w';
But it doesn't look good because the background is only for the text box, and is not aligned with the legend box. So I prefer to enlarge the legend box to include the title and move the legend attributes down to make some space. It takes more effort (because we need to calculate the right position for all elements), but it can be done in the following way:
x = 1:0.1:10;
plot(x,sin(x),x,cos(x))
[hleg,icons,plots] = legend('show');
title(hleg,'my title')
hleg.Title.Visible = 'on';
% the addition in height needed for the title:
title_hight = hleg.Position(4)/numel(plots);
hleg.Position([2 4]) = [hleg.Position(2)-title_hight hleg.Position(4)+title_hight];
% calculate new position for the elements in the legeng:
new_pos = fliplr(0.5/(numel(plots)+1):1/(numel(plots)+1):1);
hleg.Title.NodeChildren.Position = [0.5 new_pos(1) 0];
% set the text to the right position:
leg_txt = findobj(icons,'Type','Text');
txt_pos = cell2mat({leg_txt.Position}.');
txt_pos(:,2) = new_pos(2:end);
set(leg_txt,{'Position'},mat2cell(txt_pos,[1 1],3));
% set the icons to the right position:
leg_att = findobj(icons,'Type','Line');
set(leg_att,{'YData'},mat2cell(repmat(repelem(new_pos(2:end).',...
numel(plots)),1,2),ones(numel(plots)*2,1),2))
The code above is also generalized for more the one data series, although I guess that there are cases where this will fail to give the right solution and will need some fine tunings.

その他の回答 (2 件)

Kelly Kearney
Kelly Kearney 2017 年 2 月 14 日
As a workaround, legendflex.m should handle this fine. With this option, you can include the second output from the legend call.
I don't currently include the title handle as output (I should correct that), but you can grab it via findall to format as desired:
x = 1:0.1:10;
y = sin(x);
subplot 211
plot(x,y)
[leg,att] = legendflex(gca, {'data 1'}, 'title', 'my title');
set(findall(leg, 'string', 'my title'), 'fontweight', 'bold');
leg.Title.Visible = 'on';
subplot 212
plot(x,y)
leg = legendflex(gca, {'data 2'}, 'title', 'my title');
set(findall(leg, 'string', 'my title'), 'fontweight', 'bold');
  1 件のコメント
Eyal Ben-Hur
Eyal Ben-Hur 2017 年 2 月 15 日
That's nice, but definitely a too complicated workaround for a simple result.

サインインしてコメントする。


KAE
KAE 2018 年 9 月 17 日
編集済み: KAE 2018 年 9 月 17 日
If you limit yourself to one output from the legend command, then the title is positioned correctly...as you noted in your original question.
x = 1:0.1:10;
y = sin(x);
subplot 211
plot(x,y)
leg = legend('show'); % Just one output here
title(leg,'my title')
subplot 212
plot(x,y)
leg = legend('show');
title(leg,'my title')
If you need the other outputs from legend, then the documentation offers a workaround using legend properties (see "Returning multiple outputs is not recommended").
  3 件のコメント
KAE
KAE 2018 年 9 月 18 日
編集済み: KAE 2018 年 9 月 18 日
Perhaps the very useful site 'Undocumented Matlab' will have some tips, starting with the blog entry here.
Eyal Ben-Hur
Eyal Ben-Hur 2018 年 9 月 18 日
You can see in my answer above that I have already used the undocumented features to solve this problem.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeLegend についてさらに検索

製品

Community Treasure Hunt

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

Start Hunting!

Translated by