1

Problem

In a GUI I've written I've realized that the largest bottleneck in my code performance is creating/updating the legend.

Currently I delete and recreate the legend at each update of the GUI as the user needs to be able to adjust what is in the legend. I'm currently toggling what is in the legend by adjusting the setting of LINEHANDLE.Annotation.LegendInformation.IconDisplayStyle and updating the legend using legend('off');legend('show');

Testing

The following code snippet shows that the call of legend mostly is limited by the call to legend>make_legend and is fairly independent of the legend content.

close all

n=50; % number of plot lines
S=rand(n); % data
legs=cellstr(char(26*rand(n,10)+97)); % legend entries

profile on %start profiler
plot(S)
legend(legs{:})
profile viewer % view call stats

Question

Is there a better way of updating legend content without deleting it and therefore forcing it to re-call make_legend at recreation?

Furthermore I wonder if it is known why legend in general is so slow and has such odd behavior.

Purpose

I'm adding some information here to avoid the XY Problem. A minimal example of what I'm trying to do is:

I'm building a GUI which plots four lines, let's call them data1, data2, linear model 1, and linear model 2. The data lines are independent in both color and content, while the linear models both have the same appearance and are connected to respective data line.

I want there to be a legend which only has three entries: data1, data2, and linear model. So far, no problem.

I also want there to be three toggle-buttons which toggle the visibility of the four lines on the axes and legend. The buttons are:

  • data1, which toggles the visibility of both the data1, and linear model 1 data lines.
  • data2, which toggles the visibility of both the data2, and linear model 2 data lines.
  • linear model, which toggles the visibility of the linear model 1, and linear model 2 data lines.

Attempted Solutions

My first approach was to first only pass three handles to legend and then have the button callbacks adjust the visibility property of the line objects according to above.

This creates the issue that when disabling the first data line and respective linear model the legend entry for linear model also blanks out as it is connected to that specific line object, even though the other is still visible.

My current working approach instead manually sets the DisplayNameproperty of all lines and then the button callbacks adjust each lines Annotation.LegendInformation.IconDisplayStyle property. According to the documentation the user then needs to call legend to force an update.

But this is not implemented, looking at the code of legend.m it is clear that this option only returns the current legend object without any other manipulation. Therefore I'm forced to call legend('off');legend('show'); which triggers a (slow) creation of a new legend object.

This currently works but using profile I can see that the legend creation is half my computation time and has a fairly large effect on user experience when using the GUI on a slower laptop. I've already ensured that my code runs legend('off');legend('show'); only if it really has to.

The question is if any user here is able to call the unreadable, yet accessible class methods of matlab.graphics.illustration.Legend to trigger an update of an existing object without forcing it to delete and recreate. Thereby doing what the MATHWORKS documentation claims to be implemented (although it is not) in legend.

Alternatively I'm open to finding a different way of changing which line objects the current legend is tracking efficiently.

Community
  • 1
  • 1
Skogsv
  • 494
  • 3
  • 14
  • Is the legend's `'Location'` equal to `'best'`? – aschepler Aug 15 '16 at 18:33
  • For me the call to `legend` takes only 0.3 seconds. How long does it take for you? – Bernhard Aug 18 '16 at 06:38
  • 1
    I don't think the Mathworks documentation claims any functionality that is not actually implemented. From your link: "If a legend already exists and you change the IconDisplayStyle setting, then you must call legend to update the display." I read this as: "If you changed this property of a line included in the legend, you have to make a new legend" – tvo Aug 18 '16 at 14:11
  • 1
    At first sight, it doesn't seem as if there is much you can use the class methods of the Legend object for, at least regarding your problem. – tvo Aug 18 '16 at 14:19
  • If that is how it is I guess I'm stuck with my functioning code. I was hoping that I'd be able to learn a thing or two on using Matlab metaclasses. – Skogsv Aug 18 '16 at 14:37
  • @tvo Furthermore I find that the documentation can be pretty bad at times. An example is `align` which claims to align objects according to the first entry but in fact uses the extreme point instead. Another is `rf/circle` which is not able to plot all the types of gain circles that it claims. – Skogsv Aug 18 '16 at 14:44
  • I did not try to claim that Matlab's documentation is perfect or even good. I am not surprised to hear about insufficient or wrong doc's at all. I just didn't find any clear error in that piece. – tvo Aug 18 '16 at 15:01

1 Answers1

0

You can try to change the properties of the legend object directly. Look at http://www.mathworks.com/help/matlab/ref/legend-properties.html for a list of properties. The legend object can be accessed by:

% Assign the output of legend to a variable. Do this only once at creation.
l = legend(<some arguments>); 

% Example: change the location of the legend to 'north'
l.Location = 'north'; 

I think this is what you asked for, but I'm not sure about any efficiency gains.

tvo
  • 780
  • 7
  • 24
  • Sorry if wasn't clear enough before. I'm not looking to adjust any of the properties of the legend object. I'm looking to speed up the creation of legends or alternatively be able to change which plots are in the legend on the fly. – Skogsv Aug 16 '16 at 06:21
  • Looking at the code in `legend.m` it is clear that any call to `legend` will delete any old legends and create a new `matlab.graphics.illustration.Legend`object, this is the main loss of time in the computation. I was hoping to instead be able to access `doUpdate` or similar methods which are available to the legend class as seen in `?matlab.graphics.illustration.Legend`. – Skogsv Aug 16 '16 at 06:24
  • @Skogsv: I assumed incorrectly that this method would allow you to set the plots that you want to include in the legend. You may have better luck if you use the long form `[l,objh,outh,outm] = legend`. This gives you access to all the legend-related objects. – tvo Aug 16 '16 at 15:59
  • That syntax is no longerrecommended by Mathworks as it only returns a list of the objects used during creation, not a way of adjusting the list of currently used objects. Furthermore the object `l` that is returned is limited in what it can adjust when compared to the recommended form `leg=legend(...)`. – Skogsv Aug 16 '16 at 21:35
  • @Skogsv It does give you a list of the elements currently involved in the plotting of the legend. You can use it to change the appearance, and even delete items. But you are right in that repeated updating of the entire legend is at least not trivial with these tools. You could argue there are some poor design decisions by Matlab involved in this topic. But on the other hand, why would you want to frequently update a legend? And expect high performance with that? Could you maybe clarify that in your question? – tvo Aug 17 '16 at 19:19
  • added two more sections for clarifying what I'm trying to do. – Skogsv Aug 18 '16 at 06:29