Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Axes Limits – Scream Louder, I Can’t Hear You!

NOTE: This functionality was added as the LimitsChangedFcn in R2021a.

Today's guest post comes from Sean de Wolski, one of Loren's fellow Application Engineers. You might recognize him from MATLAB answers and the pick of the week blog!

I arrived at work early one day and immediately got a call from Brett, who doesn't face the same commute I do, wondering why his listeners were not working on axes' 'XLim', 'YLim' and 'ZLim'.

This seemed a little surprising, surely a listener pointed at the wrong property? Here's a minimal working example

% Plot something
ax = gca;
plot(humps);
% Add listener to axes' 'XLim' 'PostSet' event (so it fires after axes
% change).  Update the title so it's a visible change.
addlistener(ax,'XLim','PostSet',@(~,~)title('Listener Fired'));

% Change Plot
plot(sin(1:0.1:100))

Huh? No Title! What happened?

What's interesting is that it allowed me to set the listener on the 'XLim'. Usually, if a property is not 'SetObservable', it would error.

try
    % Add listener to non-SetObservable property.
    addlistener(ax,'TightInset','PostSet',@(~,~)disp('hello world'))
catch ME
    fprintf(2,'\n%s\n',ME.message);
end
While adding a PostSet listener, property 'TightInset' in class 'matlab.graphics.axis.Axes' is not defined to be SetObservable.

So what's happening?

When MATLAB versions R2014b and newer are automatically calculating the limits, the limits frequently change several times as MATLAB inspects each member of the axes and updates the limits. Allowing listeners to be triggered during the automatic calculation (even if no listener was attached) would have caused performance degradation for all plot updates even if there are no listeners. Thus the ability to trigger listeners with automatically calculated property value changes was disabled for performance reasons.

So how do we listen for limit changes?

The answer I provided to Brett was to write my own events class that has events for each of the limits changing.

classdef LimitsChangedNotifier < handle
% This class has events of limits changes to be used for updating when axes
% limits change.

    events
        % Events for each axes limit property
        XLimChanged;
        YLimChanged;
        ZLimChanged;
    end
end


Now every time I update the plot, or call x/y/zlim('auto'), I would notify afterward.

% Build the events class
limnotifier = LimitsChangedNotifier;

% Attach a listener to it
addlistener(limnotifier,'XLimChanged',@(~,~)title('Listener Fired'));

% Draw a new plot and let any listeners know you did
plot(sin(linspace(-2*pi,2*pi,100)));
notify(limnotifier,'XLimChanged')

Note: By doing this, I retain control of letting the listeners know the limits changed. This wouldn't work if the user did this on their own, say within plottools.

For a more detailed and advanced explanation of events and listeners, please see this documentation page.




Published with MATLAB® R2015b


  • print