I've got a listener on an axes object that listens for changes to either XLim or YLim and fires a callback based on the new axes limits. The callback executes correctly, but I've noticed that if both XLim and YLim are updated simultaneously that the callback executes twice (once when XLim is updated, and again when YLim is updated), regardless of axes property assignment syntax. I'd like to avoid this behavior – that is, if both XLim and YLim are updated, I'd only like the callback to fire once.
Axes Listener Class:
classdef dasAxesPropEvent < handle properties(SetObservable, GetObservable, AbortSet) XLim YLim end methods %constructor
function obj = dasAxesPropEvent(hAx) %hAx is a handle object to the parent axes
obj.XLim = hAx.XLim; obj.YLim = hAx.YLim; addlistener(hAx,{'XLim','YLim'},'PostSet',@obj.handlePropEvent); end %gettor
function propval = get.XLim(obj) propval = obj.XLim; end %gettor function propval = get.YLim(obj) propval = obj.YLim; end %settor
function set.XLim(obj,val) obj.XLim = val; end %settor function set.YLim(obj,val) obj.YLim = val; end %overload disp
function disp(obj) disp(class(obj)) end %updator
function handlePropEvent(obj,src,evnt) switch src.Name case 'XLim' obj.XLim = evnt.AffectedObject.XLim; case 'YLim' obj.YLim = evnt.AffectedObject.YLim; otherwise %?
end end endend
In another object, I add a listener to a dasAxesPropEvent to fire the callback if the axes is updated. I use the listener class instead of a listener directly on the axes in order to use the AbortSet property.
hObj.axesListener = addlistener(hDasPropEvent,{'XLim','YLim'},'PostSet',@hObj.updateDasImage);
Here's an example of the behavior. I added some debug/print lines (dbstack, XLim, and YLim) to @hObj.updateDasImage.
> In dasImage/updateDasImage (line 205) In dasImage>@(varargin)hObj.updateDasImage(varargin{:}) (line 182) In dasAxesPropEvent/handlePropEvent (line 48) In dasAxesPropEvent>@(varargin)obj.handlePropEvent(varargin{:}) (line 16) In matlab.graphics.internal.LinkProp/processUpdate (line 30) In localUpdateListeners>@(varargin)hLink.processUpdate(varargin{:}) (line 54) In dtsDasWaterfall01>aufbauschen (line 3793) In gui_mainfcn (line 95) In dtsDasWaterfall01 (line 44) In matlab.graphics.internal.figfile.FigFile/read>@(hObject,eventdata)dtsDasWaterfall01('aufbauschen',hObject,eventdata,guidata(hObject))XLim01-Nov-2017 12:33:2401-Nov-2017 14:17:36YLim 14.3882 985.0474> In dasImage/updateDasImage (line 205) In dasImage>@(varargin)hObj.updateDasImage(varargin{:}) (line 182) In dasAxesPropEvent/handlePropEvent (line 50) In dasAxesPropEvent>@(varargin)obj.handlePropEvent(varargin{:}) (line 16) In matlab.graphics.internal.LinkProp/processUpdate (line 30) In localUpdateListeners>@(varargin)hLink.processUpdate(varargin{:}) (line 54) In dtsDasWaterfall01>aufbauschen (line 3793) In gui_mainfcn (line 95) In dtsDasWaterfall01 (line 44) In matlab.graphics.internal.figfile.FigFile/read>@(hObject,eventdata)dtsDasWaterfall01('aufbauschen',hObject,eventdata,guidata(hObject))XLim01-Nov-2017 12:33:2401-Nov-2017 14:17:36YLim 15.6671 984.4080
The dbstack is nearly identical for both calls – the XLim listener callback fires before YLim is assigned, then the YLim listener callback fires.
The line where XLim & YLim are assigned is this:
set(handles.axes1,'XLim',newXLim,'YLim',newYLim);
The same behavior happens if I do serial assignment (except the dbstack entry point for aufbauschen is +1 line):
handles.axes1.XLim = newXLim;handles.axes1.YLim = newYLim;
and also the same thing happens if I use deal:
[handles.axes1.XLim,handles.axes2.YLim] = deal(newXLim,newYLim);
In this scenario (XLim & YLim both updated) is it possible to make the callback execute only once? It is possibly relevant information that there is also a LinkProp listener on the same axes, which is why there's a call to LinkProp ahead of the call to handlePropEvent in the dbStack.
Best Answer