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 customization

July 12, 2018 8 Comments

Three years ago I explained how we can use a couple of undocumented hidden properties of the legend in order to add a legend title (the legend object had no Title property back then – this was only added in a later Matlab release, perhaps as a result of my post). Today I will expand on that article by explaining the plot legend’s internal graphics hierarchy, how we can access each of these components, and then how this information could be used to customize the separate legend components. Note that the discussion today is only relevant for HG2 legends (i.e. R2014b or newer).
Let’s start with a simple Matlab plot with a legend:

hold all;
hLine1 = plot(1:5);
hLine2 = plot(2:6);
hLegend = legend([hLine1,hLine2], 'Location','SouthEast');
hLegend.Title.String = 'MyLegend';

hold all; hLine1 = plot(1:5); hLine2 = plot(2:6); hLegend = legend([hLine1,hLine2], 'Location','SouthEast'); hLegend.Title.String = 'MyLegend';

Standard Matlab legend
Standard Matlab legend
This legend is composed of the following visible internal components, which can be customized separately:
Matlab legend components



Id in screenshot Accessed via Object type Description Important properties
1 hLegend.Title Text Title of the legend Visible, String, Color, FontSize, FontWeight.
2 hLegend.TitleSeparator LineStrip Separator line between title and legend entries. Only appears when title is set. Visible, LineStyle, LineWidth, ColorData (4×1 uint8)
3 hLegend.BoxEdge LineLoop Box (border) line around the entire legend (including title) Visible, LineStyle, LineWidth, ColorData (4×1 uint8)
4 hLegend.EntryContainer.NodeChildren(2) LegendEntry Entry row in the legend, corresponding to hLine1 Icon, Label, Object (line object in main axes)
5 hLegend.EntryContainer.NodeChildren(1) LegendEntry Entry row in the legend, corresponding to hLine2 Icon, Label, Object (line object in main axes)
6 hLegend.EntryContainer.NodeChildren(1).Label Text Label of legend entry Visible, String, Color, FontSize, FontWeight
7 hLegend.EntryContainer.NodeChildren(1).Icon LegendIcon Icon/marker of legend entry Visible, Transform.Children.Children (LineStrip object)

A pivotal object of the legend group are the LegendEntry items, one per legend row:

>> hLegendEntry = hLegend.EntryContainer.NodeChildren(1);
>> get(hLegendEntry)
              Children: [3×1 Graphics]
                 Color: [0 0 0]
                 Dirty: 0
             FontAngle: 'normal'
              FontName: 'Helvetica'
              FontSize: 8
            FontWeight: 'normal'
      HandleVisibility: 'on'
               HitTest: 'on'
                  Icon: [1×1 LegendIcon]
                 Index: 0
           Interpreter: 'tex'
                 Label: [1×1 Text]
            LayoutInfo: [1×1 matlab.graphics.illustration.legend.ItemLayoutInfo]
                Legend: [1×1 Legend]
              Listener: [1×1 event.listener]
                Object: [1×1 Line]
               Overlay: [1×1 TriangleStrip]
          OverlayAlpha: 0.65
                Parent: [1×1 Group]
           PeerVisible: 'on'
         PickableParts: 'visible'
              Selected: 'off'
    SelectionHighlight: 'on'
               Visible: 'on'
       VisibleListener: [1×1 event.proplistener]

>> hLegendEntry = hLegend.EntryContainer.NodeChildren(1); >> get(hLegendEntry) Children: [3×1 Graphics] Color: [0 0 0] Dirty: 0 FontAngle: 'normal' FontName: 'Helvetica' FontSize: 8 FontWeight: 'normal' HandleVisibility: 'on' HitTest: 'on' Icon: [1×1 LegendIcon] Index: 0 Interpreter: 'tex' Label: [1×1 Text] LayoutInfo: [1×1 matlab.graphics.illustration.legend.ItemLayoutInfo] Legend: [1×1 Legend] Listener: [1×1 event.listener] Object: [1×1 Line] Overlay: [1×1 TriangleStrip] OverlayAlpha: 0.65 Parent: [1×1 Group] PeerVisible: 'on' PickableParts: 'visible' Selected: 'off' SelectionHighlight: 'on' Visible: 'on' VisibleListener: [1×1 event.proplistener]

Each LegendEntry contains a back-reference to the original graphics object. In my example above, hLegend.EntryContainer.NodeChildren(2).Object == hLine1, and hLegend.EntryContainer.NodeChildren(2).Object == hLine1. Note how the default legend entries order is the reverse of the order of creation of the original graphics objects. Naturally, we can modify this order by creating the legend py passing it an array of handles that is ordered differently (see the documentation of the legend function).
To get all the original graphic objects together, in a single array, we could use one of two mechanisms (note the different order of the returned objects):

% Alternative #1
>> [hLegend.EntryContainer.NodeChildren.Object]'
ans =
  2×1 Line array:
  Line    (data2)
  Line    (data1)
% Alternative #2
>> hLegend.PlotChildren
ans =
  2×1 Line array:
  Line    (data1)
  Line    (data2)

% Alternative #1 >> [hLegend.EntryContainer.NodeChildren.Object]' ans = 2×1 Line array: Line (data2) Line (data1) % Alternative #2 >> hLegend.PlotChildren ans = 2×1 Line array: Line (data1) Line (data2)

For some reason, accessing the displayed graphic line in LegendEntry‘s Icon is not simple. For example, the LineStrip object that corresponds to hLine2 can be gotten via:

hLegendEntry = hLegend.EntryContainer.NodeChildren(1);
hLegendIconLine = hLegendEntry.Icon.Transform.Children.Children;  % a LineStrip object in our example

hLegendEntry = hLegend.EntryContainer.NodeChildren(1); hLegendIconLine = hLegendEntry.Icon.Transform.Children.Children; % a LineStrip object in our example

I assume that this was done to enable non-standard icons for patches and other complex objects (in which case the displayed icon would not necessarily be a LineStrip object). In the case of a line with markers, for example, hLegendIconLine would be an array of 2 objects: a LineStrip object and a separate Marker object. Still, I think that a direct reference in a hLegend.EntryContainer.NodeChildren(1).Icon property would have helped in 99% of all cases, so that we wouldn’t need to pass through the Transform object.
Anyway, once we have this object reference(s), we can modify its/their properties. In the case of a LineStrip this includes LineStyle, LineWidth, ColorData (4×1 uint8), and VertexData (which controls position/length):

>> get(hLegendIconLine(end))  % LineStrip
          AlignVertexCenters: 'on'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4×1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'middle'
                     LineCap: 'none'
                    LineJoin: 'round'
                   LineStyle: 'solid'
                   LineWidth: 0.5
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1×1 Group]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: []
                     Texture: [0×0 GraphicsPlaceholder]
                  VertexData: [3×2 single]
               VertexIndices: []
                     Visible: 'on'
       WideLineRenderingHint: 'software'

>> get(hLegendIconLine(end)) % LineStrip AlignVertexCenters: 'on' AmbientStrength: 0.3 ColorBinding: 'object' ColorData: [4×1 uint8] ColorType: 'truecolor' DiffuseStrength: 0.6 HandleVisibility: 'on' HitTest: 'off' Layer: 'middle' LineCap: 'none' LineJoin: 'round' LineStyle: 'solid' LineWidth: 0.5 NormalBinding: 'none' NormalData: [] Parent: [1×1 Group] PickableParts: 'visible' SpecularColorReflectance: 1 SpecularExponent: 10 SpecularStrength: 0.9 StripData: [] Texture: [0×0 GraphicsPlaceholder] VertexData: [3×2 single] VertexIndices: [] Visible: 'on' WideLineRenderingHint: 'software'

and in the presense of markers:

>> get(hLegendIconLine(1))  % Marker
    EdgeColorBinding: 'object'
       EdgeColorData: [4×1 uint8]
       EdgeColorType: 'truecolor'
    FaceColorBinding: 'object'
       FaceColorData: []
       FaceColorType: 'truecolor'
    HandleVisibility: 'on'
             HitTest: 'off'
               Layer: 'middle'
           LineWidth: 0.5
              Parent: [1×1 Group]
       PickableParts: 'visible'
                Size: 6
         SizeBinding: 'object'
               Style: 'circle'
          VertexData: [3×1 single]
       VertexIndices: []
             Visible: 'on'

>> get(hLegendIconLine(1)) % Marker EdgeColorBinding: 'object' EdgeColorData: [4×1 uint8] EdgeColorType: 'truecolor' FaceColorBinding: 'object' FaceColorData: [] FaceColorType: 'truecolor' HandleVisibility: 'on' HitTest: 'off' Layer: 'middle' LineWidth: 0.5 Parent: [1×1 Group] PickableParts: 'visible' Size: 6 SizeBinding: 'object' Style: 'circle' VertexData: [3×1 single] VertexIndices: [] Visible: 'on'

An additional undocumented legend property that is of interest is ItemTokenSize. This is a 2-element numeric array specifying the minimal size of the legend entries’ icon and label. By default hLegend.ItemTokenSize == [30,18], but we can either expand or shrink the icons/labels by setting different values. For example:

hLegend.ItemTokenSize == [10,1];  % shrink legend icons and labels

hLegend.ItemTokenSize == [10,1]; % shrink legend icons and labels

Note that regardless of the amount that we specify, the actual amount that will be used will be such that all legend labels appear.
Fun: try playing with negative values for the icon and the label and see what happens 🙂
Have you come across any other interesting undocumented aspect of Matlab legends? If so, then please share it in a comment below.

Related posts:

  1. Plot legend title – Titles to plot legends are easy to achieve in HG1 (R2014a or earlier), but much more difficult in HG2 (R2014b or newer). ...
  2. Plot line transparency and color gradient – Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
  3. Plot markers transparency and color gradient – Matlab plot-line markers can be customized to have transparency and color gradients. ...
  4. Multi-column (grid) legend – This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  5. Transparent legend – Matlab chart legends are opaque be default but can be made semi- or fully transparent. ...
  6. Plot LineSmoothing property – LineSmoothing is a hidden and undocumented plot line property that creates anti-aliased (smooth unpixelized) lines in Matlab plots...
Handle graphics Pure Matlab Undocumented property
Print Print
« Previous
Next »
8 Responses
  1. sco1 July 13, 2018 at 04:40 Reply

    Very interesting! I never explored NodeChildren when I was building legtools and was using PlotChildren instead to do things like rearrange the legend, add/remove entries, etc.

    Have you had luck utilizing NodeChildren to perform similar options in MATLAB >= R2017a?
    R2017a introduced AutoUpdate to legend objects which broke pretty much all of the functionality I got from manipulating PlotChildren.

  2. Peter Cook July 17, 2018 at 18:17 Reply

    I have recently incorporated the Legend’s ItemHitFcn callback property into a utility I built.
    You can access and edit some relevant objects in the callback function, e.g.

    hLegend.ItemHitFcn = myLegendItemHitFcn(src,evnt);
     
    % The following in the callback function:
    evnt.Peer %access line, patch, image, GraphPlot, etc... 
    evnt.Item.Label %access text object for that legend entry
    src.String{evnt.Item.Index} = 'new string'; %edit the legend entry name

    hLegend.ItemHitFcn = myLegendItemHitFcn(src,evnt); % The following in the callback function: evnt.Peer %access line, patch, image, GraphPlot, etc... evnt.Item.Label %access text object for that legend entry src.String{evnt.Item.Index} = 'new string'; %edit the legend entry name

    • Yair Altman July 18, 2018 at 10:58 Reply

      @Peter – while the legend object’s ItemHitFcn callback property is documented/supported, the Item property of the eventData object (which is passed to the callback function in run-time when you click a legend entry) is indeed hidden/undocumented. This eventData.Item property contains the LegendEntry item that was clicked. Thanks for pointing it out.

  3. Aaron March 8, 2019 at 13:38 Reply

    It would appear that if you’re using these properties in a function or loop it is important to call drawnow before attempting to utilise any of the LegendEntry objects, otherwise they will not exist yet and you will get errors.

  4. Praveen March 27, 2019 at 19:55 Reply

    Hi That’s alright, how do I set for example, marker size in the legend, when I try to set it: hLegend.EntryContainer.NodeChildren(1).Icon.Transform.Children.Children.Size = 15; I get an error saying I can’t set readonly properties, any tips how to go about that. Thanks

    • Yair Altman March 29, 2019 at 16:40 Reply

      @Praveen – first note that you have to call drawnow before accessing any of the internal legend components. In addition, note that hLegend.EntryContainer.NodeChildren(1).Icon.Transform.Children.Children returns an array of objects (Marker and LineStrip) and you can only set the Size property of the Marker, not the LineStrip (duh). Here’s a working example:

      figure; hold all;
      hLine1 = plot(1:5,'o-b'); 
      hLine2 = plot(2:6,'-sr');
      hLegend = legend([hLine1,hLine2], 'Location','SouthEast');
      hLegend.Title.String = 'MyLegend';
      drawnow;
      hLegend.EntryContainer.NodeChildren(1).Icon.Transform.Children.Children(1).Size = 15;

      figure; hold all; hLine1 = plot(1:5,'o-b'); hLine2 = plot(2:6,'-sr'); hLegend = legend([hLine1,hLine2], 'Location','SouthEast'); hLegend.Title.String = 'MyLegend'; drawnow; hLegend.EntryContainer.NodeChildren(1).Icon.Transform.Children.Children(1).Size = 15;

  5. Dev June 25, 2019 at 14:42 Reply

    Same question as sco1: Have you had luck utilizing NodeChildren to perform similar options in MATLAB >= R2017a?

  6. Gres June 15, 2022 at 07:35 Reply

    In 2018b, you can get the icons by calling [hh,icons,plots,txt] = legend({‘Line 1’});

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