Is it possible to link, for example, the 'XLim' property of one MATLAB axis to the 'YLim' property of another axis? I have looked into linkaxes
and linkprop
but as far as I can tell, they can only link same properties, e.g. one axis 'XLim' to another axis 'XLim' and so on.
Using MATLAB 2014b. Thanks!
I worked up a way to it, it works but it remains slightly clunky ...
The idea is obviously to attach listeners to the two properties you want to link, and update one property each time the other one is changed.
It takes a bit of fiddling around just to be able to attach a listener to the properties of the built-in axes
object, you have to retrieve the associated meta.property
. I package this in an external subfunction findPropertyHandle
.
I made the solution generic so you can link any type of property together. Now I didn't program input checks so of course it will work only on compatible properties. XLim
is obvisouly compatible with YLim
, but if you try to link XLim
with PlotBoxAspectRatioMode
you're in for a deluge of red and orange text in your console.
%% Demo data and figure
x = linspace(0,24*pi,1000) ;
y = cos(x) ;
hf = figure ;
hax1 = subplot(1,2,1) ; hp1 = plot(x,y) ;
hax2 = subplot(1,2,2) ; hp2 = plot(y,x) ;
%% Find (meta)property handle
prop1 = findPropertyHandle( hax1 , 'XLim' ) ;
prop2 = findPropertyHandle( hax2 , 'YLim' ) ;
%% Add a listener to each property
% no callback yet because we need the reference of both listeners to be part of the callback arguments
lh1 = addlistener( hax1, prop1 , 'PostSet', @() [] ) ;
lh2 = addlistener( hax2, prop2 , 'PostSet', @() [] ) ;
%% group a few things for convenience
pl.obj2link = [ hax1 ; hax2 ] ; % axes handles
pl.props2link = { 'XLim' ; 'YLim' } ; % name of properties to link
pl.listeners = [ lh1 ; lh2 ] ; % listener handle
pl.metaprops2link = [ prop1 ; prop2 ] ; % metaproperty handles
%% Set the callback of both listeners
% Have to use dot notation because the 'set' method is not defined for listener objects
lh1.Callback = @(h,e) LinkPropChangedFcn(h,e,pl) ;
lh2.Callback = @(h,e) LinkPropChangedFcn(h,e,pl) ;
The code for LinkPropChangedFcn.m
function LinkPropChangedFcn( hobj, evt, plink )
%LinkPropChangedFcn Links 2 compatible properties
% find which property was triggered
changedPropIdx = find( hobj==plink.metaprops2link ) ;
prop2changeIdx = 3-changedPropIdx ; % because cumsum([1 2])=3
% disable the other listener to not trigger the property change on the
% target object
plink.listeners(prop2changeIdx).Enabled = false ;
% retrieve property value
val = get( plink.obj2link(changedPropIdx) , plink.props2link{changedPropIdx} ) ;
% aplpy the value to the target object property
set( plink.obj2link(prop2changeIdx) , plink.props2link{prop2changeIdx} , val ) ;
% re-enable target listener
plink.listeners(prop2changeIdx).Enabled = true ;
end
The code for findPropertyHandle.m
function [ hprop ] = findPropertyHandle( hobj , propname )
%FINDPROPERTYHANDLE retrieve the handle of the meta.property object
mco = metaclass(hobj) ;
plist = mco.PropertyList;
for k=1:numel(plist)
if strcmpi(plist(k).Name,propname)
fprintf('[%s] property was found at index #%d\n',propname,k)
hprop = plist(k) ;
return
end
end
% no preperty was found if we are here
hprop = [] ;
fprintf('[%s] property was not found.\n',propname)
end
Note
The clunkiness I was talking about is when instead of "zooming out" you use the "reset to original view" menu. This function does not trigger the PostSet
event of the XLim
property, so the change is not always propagated to the other axes/property. To overcome that, just after using "reset to original view", right click again and "zoom out". This will not zoom further out in the clicked axes
(if you were already at the "original view"), but it will trigger the listener and update the second target axes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With