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.
Get the MATLAB code
Published with MATLAB® R2015b
Comments are closed.
6 CommentsOldest to Newest
% Change Plot plot(sin(1:0.1:100)) xlim(ax.XLim); % <-- calling xlim invokes the XLim listenerThis will invoke the listener without changing the new x-axis limits. Kind regards,
ax.XLim = ax.XLimdoes fire the listener because 'AbortSet' is not true for the 'XLim' property. Thus, your solution is probably better (!). It also takes care of Brett's second question below. @Brett: 1) To my knowledge, this is not currently possible. 2) You can always create your own event data for any event you make. Here's an example class that I would use with my above example: Custom Event Data Class
classdef (ConstructOnLoad) CustomEventData < event.EventData % CustomEventData class % More information here: % https://www.mathworks.com/help/releases/R2015b/matlab/matlab_oop/events-and-listeners--syntax-and-techniques.html#brb6i_k properties AffectedObject Message end methods function obj = CustomEventData(AffectedObject,Message) obj.AffectedObject = AffectedObject; obj.Message = Message; end end endAnd using it:
% Build the events class limnotifier = LimitsChangedNotifier; % Attach a listener to it addlistener(limnotifier,'XLimChanged',@(~,evt)disp(evt)); % Draw a new plot and let any listeners know you did plot(sin(linspace(-2*pi,2*pi,100))); notify(limnotifier,'XLimChanged',CustomEventData(gca,'Updated With plot()'))Results:
CustomEventData with properties: AffectedObject: [1x1 Axes] Message: 'Updated With plot()' Source: [1x1 LimitsChangedNotifier] EventName: 'XLimChanged