Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Plot legend title

April 1, 2015 11 Comments

This blog post was supposed to be a piece of cake: The problem description was that we wish to display a text title next to the legend box in plot axes. Sounds simple enough. After all, in HG1 (R2014a and earlier), a legend was a simple wrapper around a standard Matlab axes. Therefore, we can simply access the legend axes’s title handle, and modify its properties. This works very well in HG1:

hold all;
hLine1 = plot(1:5);
hLine2 = plot(2:6);
hLegend = legend([hLine1,hLine2], 'Location','NorthWest');
hTitle = get(hLegend,'title');
set(hTitle, 'String','Plot types:', 'VerticalAlignment','middle', 'FontSize',8);

hold all; hLine1 = plot(1:5); hLine2 = plot(2:6); hLegend = legend([hLine1,hLine2], 'Location','NorthWest'); hTitle = get(hLegend,'title'); set(hTitle, 'String','Plot types:', 'VerticalAlignment','middle', 'FontSize',8);

Matlab HG1 legend with title
Matlab HG1 legend with title

HG2

How hard then could a corresponding solution be in HG2 (R2014b+), right?
Well, it turns out that hard enough (at least for me)…

In this blog I’ve presented ~300 posts so far that discuss solutions to problems. Readers of this blog always hear the success stories, and might mistakenly think that every problem has a similarly simple solution that can be hacked away in a few lines of nifty code.
Well, the truth must be told that for each investigation that yields such a success story, there is at least one other investigation in which I failed to find a solution, no matter how hard I tried or countless hours spent digging (this is not to say that the success stories are easy – distilling a solution to a few lines of code often takes hours of research). In any case, maybe some of these problems for which I have not found a solution do have one that I have simply not discovered, and maybe they don’t – in most likelihood I will never know.
This is yet another example of such a spectacular failure on my part. Try as I may in HG2, I could find no internal handle anywhere to the legend’s axes or title handle. As far as I could tell, HG2’s legend is a standalone object of class matlab.graphics.illustration.Legend that derives from exactly the same superclasses as axes:

>> sort(superclasses('matlab.graphics.axis.Axes'))
ans =
    'JavaVisible'
    'dynamicprops'
    'handle'
    'matlab.graphics.Graphics'
    'matlab.graphics.GraphicsDisplay'
    'matlab.graphics.internal.GraphicsJavaVisible'
    'matlab.mixin.CustomDisplay'
    'matlab.mixin.Heterogeneous'
    'matlab.mixin.SetGet'

>> sort(superclasses('matlab.graphics.axis.Axes')) ans = 'JavaVisible' 'dynamicprops' 'handle' 'matlab.graphics.Graphics' 'matlab.graphics.GraphicsDisplay' 'matlab.graphics.internal.GraphicsJavaVisible' 'matlab.mixin.CustomDisplay' 'matlab.mixin.Heterogeneous' 'matlab.mixin.SetGet'

>> sort(superclasses('matlab.graphics.illustration.Legend'))
ans =
    'JavaVisible'
    'dynamicprops'
    'handle'
    'matlab.graphics.Graphics'
    'matlab.graphics.GraphicsDisplay'
    'matlab.graphics.internal.GraphicsJavaVisible'
    'matlab.mixin.CustomDisplay'
    'matlab.mixin.Heterogeneous'
    'matlab.mixin.SetGet'

>> sort(superclasses('matlab.graphics.illustration.Legend')) ans = 'JavaVisible' 'dynamicprops' 'handle' 'matlab.graphics.Graphics' 'matlab.graphics.GraphicsDisplay' 'matlab.graphics.internal.GraphicsJavaVisible' 'matlab.mixin.CustomDisplay' 'matlab.mixin.Heterogeneous' 'matlab.mixin.SetGet'

This make sense, since they share many properties/features. But it also means that legends are apparently not axes but rather unrelated siblings. As such, if MathWorks chose to remove the Title property from the legend object, we will never find it.

So what can we do in HG2?

Well, we can always resort to the poor-man’s solution of an optical illusion: displaying a an invisible axes object having the same Position as the legend box, with an axes title. We attach property listeners on the legend’s Units, Position and Visible properties, linking them to the corresponding axes properties, so that the title will change if and when the legend’s properties change (for example, by dragging the legend to a different location, or by resizing the figure). We also add an event listener to destroy the axes (and its title) when the legend is destroyed:

% Create the legend
hLegend = legend(...);  % as before
% Create an invisible axes at the same position as the legend
hLegendAxes = axes('Parent',hLegend.Parent, 'Units',hLegend.Units, 'Position',hLegend.Position, ...
                   'XTick',[] ,'YTick',[], 'Color','none', 'YColor','none', 'XColor','none', 'HandleVisibility','off', 'HitTest','off');
% Add the axes title (will appear directly above the legend box)
hTitle = title(hLegendAxes, 'Plot types:', 'FontWeight','normal', 'FontSize',8);  % Default is bold-11, which is too large
% Link between some property values of the legend and the new axes
hLinks = linkprop([hLegend,hLegendAxes], {'Units', 'Position', 'Visible'});
% persist hLinks, otherwise they will stop working when they go out of scope
setappdata(hLegendAxes, 'listeners', hLinks);
% Add destruction event listener (no need to persist here - this is done by addlistener)
addlistener(hLegend, 'ObjectBeingDestroyed', @(h,e)delete(hLegendAxes));

% Create the legend hLegend = legend(...); % as before % Create an invisible axes at the same position as the legend hLegendAxes = axes('Parent',hLegend.Parent, 'Units',hLegend.Units, 'Position',hLegend.Position, ... 'XTick',[] ,'YTick',[], 'Color','none', 'YColor','none', 'XColor','none', 'HandleVisibility','off', 'HitTest','off'); % Add the axes title (will appear directly above the legend box) hTitle = title(hLegendAxes, 'Plot types:', 'FontWeight','normal', 'FontSize',8); % Default is bold-11, which is too large % Link between some property values of the legend and the new axes hLinks = linkprop([hLegend,hLegendAxes], {'Units', 'Position', 'Visible'}); % persist hLinks, otherwise they will stop working when they go out of scope setappdata(hLegendAxes, 'listeners', hLinks); % Add destruction event listener (no need to persist here - this is done by addlistener) addlistener(hLegend, 'ObjectBeingDestroyed', @(h,e)delete(hLegendAxes));

Matlab HG2 legend with title
Matlab HG2 legend with title

Yes, this is indeed a bit of an unfortunate regression from HG1, but I currently see no other way to solve this. We can’t win ’em all… If you know a better solution, I’m all ears. Please shoot me an email, or leave a comment below.
Update: As suggested below by Martin, here is a more elegant solution, which attaches a text object as a direct child of the legend’s hidden property DecorationContainer (we cannot add it as a child of the legend since this is prevented and results in an error):

hLegend = legend(...);
hlt = text(...
    'Parent', hLegend.DecorationContainer, ...
    'String', 'Title', ...
    'HorizontalAlignment', 'center', ...
    'VerticalAlignment', 'bottom', ...
    'Position', [0.5, 1.05, 0], ...
    'Units', 'normalized');

hLegend = legend(...); hlt = text(... 'Parent', hLegend.DecorationContainer, ... 'String', 'Title', ... 'HorizontalAlignment', 'center', ... 'VerticalAlignment', 'bottom', ... 'Position', [0.5, 1.05, 0], ... 'Units', 'normalized');

The title appears to stay attached to the legend and the Parent property of the text object even reports the legend object as its parent:

hLegend.Location = 'southwest';  % Test the title's attachment
hlt.Parent % Returns hLegend

hLegend.Location = 'southwest'; % Test the title's attachment hlt.Parent % Returns hLegend

– thanks Martin!
Happy Passover/Easter everybody!
Addendum: As pointed out by Eike in a comment below, Matlab release R2016a has restored the Title property. This property holds a handle to a Text object, for which we can set properties such as String, Color, FontSize etc.

Related posts:

  1. Plot legend customization – Matlab plot legends and their internal components can be customized using a variety of undocumented properties that are easily accessible. ...
  2. Bug and workaround in timeseries plot – Matlab's internal hgconvertunits function has a bug that affects timeseries plots. Luckily there is a simple workaround....
  3. Transparent legend – Matlab chart legends are opaque be default but can be made semi- or fully transparent. ...
  4. Multi-column (grid) legend – This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  5. Bar plot customizations – Matlab bar charts can be customized in various nifty ways. ...
  6. Plot LimInclude properties – The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
Handle graphics HG2 Optical illusion Pure Matlab Undocumented feature
Print Print
« Previous
Next »
11 Responses
  1. Fred April 1, 2015 at 15:34 Reply

    The workaround I use is to plot the first point of my first line with ‘w.’, then include it as the first handle in my call to legend, with its text as the desired legend title. In this example the handles for the legend are left implicit.

    figure
    hold on;
    plot(r(1),'w.');
    plot(r);
    legend('Traces','First','Second')

    figure hold on; plot(r(1),'w.'); plot(r); legend('Traces','First','Second')

    Cheers,

  2. Martin April 1, 2015 at 15:44 Reply

    I haven’t really tested for any undesired side effects, but I have the feeling that this qualifies as a more elegant solution:

    hLegend = legend(...)
    hlt = text(...
        'Parent', hLegend.DecorationContainer, ...
        'String', 'Title', ...
        'HorizontalAlignment', 'center', ...
        'VerticalAlignment', 'bottom', ...
        'Position', [0.5, 1.05, 0], ...
        'Units', 'normalized');

    hLegend = legend(...) hlt = text(... 'Parent', hLegend.DecorationContainer, ... 'String', 'Title', ... 'HorizontalAlignment', 'center', ... 'VerticalAlignment', 'bottom', ... 'Position', [0.5, 1.05, 0], ... 'Units', 'normalized');

    The title appears to stay attached to the legend and the Parent property of the text object even reports the legend object as its parent:

    hLegend.Location = 'southwest';  % Test the title's attachment
    hlt.Parent % Returns hLegend

    hLegend.Location = 'southwest'; % Test the title's attachment hlt.Parent % Returns hLegend

    I tested this with R2015a. The DecorationContainer was simply the first in a rather long list of properties that sounded like it could be useful.

    • Yair Altman April 1, 2015 at 16:42 Reply

      @Martin – thanks! this is indeed more elegant. I tried setting a text object’s Parent directly to the legend and received an error (“Text cannot be a child of Legend”); I then tried to set the DecorationContainer’s title property and got another error (since it’s not an axes and has no such property); but I didn’t think to simply try what you did, which is a variant of my failed attempts…

      I guess it shows us that we should never give up hope, maybe just take a step back and rethink about the problem.

      Thanks again!

    • Martin April 3, 2015 at 08:36 Reply

      @Yair – thanks for improving my comment and fixing the minor error I included! I see you read those comments very carefully. 🙂

      R2015a made it easier to to explore HG2 (and the many undocumented properties/methods of the related classes) by adding the new (and hidden) Type property to the meta.property class and the InputTypes and OutputTypes properties to the the meta.method class. For example:

      mc = ?matlab.graphics.axis.Axes;
      mc.PropertyList(1).Name % Camera
      mc.PropertyList(1).Type % matlab.graphics.axis.camera.Camera
      mc.MethodList(49).Name % addlistener
      mc.MethodList(49).InputNames % [sources, propertyname, eventname, callback]
      mc.MethodList(49).InputTypes % [handle, asciiString, char vector, function_handle scalar]

      mc = ?matlab.graphics.axis.Axes; mc.PropertyList(1).Name % Camera mc.PropertyList(1).Type % matlab.graphics.axis.camera.Camera mc.MethodList(49).Name % addlistener mc.MethodList(49).InputNames % [sources, propertyname, eventname, callback] mc.MethodList(49).InputTypes % [handle, asciiString, char vector, function_handle scalar]

      Knowing the type of a property can be especially helpful to find out what can be assigned to it. Sometimes one even gets a list of valid values (e.g. the CameraMode property of the above Axes class).

      • Yair Altman April 8, 2015 at 13:15

        @Martin – thanks, interesting. I have a hunch that this is related to the undocumented mechanism of setting class-property types using the so-called “@-filter”:

        properties
            Name@char = 'abc'
            Width@double = 10
        end

        properties Name@char = 'abc' Width@double = 10 end

        Unfortunately, I tried this @-filter on method input/output args and it croacks. So either it’s not implemented yet, or MathWorks may have changed the syntax, or maybe I’m just misreading the situation…

    • Martin April 8, 2015 at 23:29 Reply

      @Yair – Yes, I’d say this is related. Obviously, there is internal support for data types for both properties and function/method arguments. But only the former got the (undocumented) @-syntax in the MATLAB language, as described by your linked post. The latter seems to be reserved to built-in functions and methods of built-in classes, that are implemented in C++ and are thus not restricted by what is exposed to the MATLAB language. (I also tried and failed to find a syntax that would work for arguments.)

      I wonder why MathWorks decided to expose only “half” of this data type feature to MATLAB programmers. And I wonder why they added “full” support for querying this information directly from MATLAB in R2015a. (Not that I’m complaining, it provides interesting insights.)

      Finally, I wonder what those data types really are. (I think we had something similar in the UDD class system.) They are not real classes, as can be seen in many instances in HG2. But classes can be used as data types. Will data types be fully exposed to the MATLAB language? Will we be able to create and use custom data types in the future?

  3. Robert Cumming April 2, 2015 at 08:22 Reply

    @Yair,

    Interesting as always – Adding a legend title is one of the functions in my GUI Toolbox and I uploaded this particular code as a FEX submission which covers HG1 & HG2.

    @Martin – Nice solution! 🙂

  4. Tom Heine March 9, 2016 at 00:01 Reply

    I have found one downside to the Martin’s approach. For some reason, when I save the figure as a .fig, the legend title isn’t saved.

  5. Eike Blechschmidt June 6, 2017 at 16:53 Reply

    In Matlab 2016a it works if you use:

    hLegend = legend();
    hLegend.Title.String = 'My Legend Title'.

    hLegend = legend(); hLegend.Title.String = 'My Legend Title'.

  6. EBH June 23, 2017 at 16:51 Reply

    You might want to see another issue with the new legend title that I solved here

    • Yair Altman June 25, 2017 at 00:54 Reply

      ???

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top