How to link different scaled axes for zooming in App Designer?

조회 수: 24 (최근 30일)
Hamid Kilic
Hamid Kilic 2021년 9월 21일
댓글: Adam Danz 2021년 9월 28일
In App Designer, I have two axes. On the first axis, I load an image and show it with imagesc. On the second axis, I plot the intensity plot on the y-direction of the image.
Now I would like to link the axis and when I zoom in on the image I would like to zoom the second axis too. I used linkaxes but it didn't work for me because the scales of the two axes are different and it doesn't zoom in on the intended area. Also, one problem with linkaxes is that it connects the axis two ways so I want the second axis to be affected when I zoom in on the image axis but I don't want it the other way around. I only need to link the two axes in the x-direction.

채택된 답변

Adam Danz
Adam Danz 2021년 9월 21일
편집: Adam Danz 2021년 9월 27일
linkaxes has a second input that allows you to link the x,y,or both axes. However, since your y-axes have different scales, the linkaxes function will not be useful to you.
Linking axes with different scales is demonstrated in this answer but since that answer uses overlaid axes and "links" both x and y axies, I've made some adjustments to that demo which is shown below which links the y-axes between two plots.
The only "input" in the entire code is ax which is a 1x2 vector of axis handles [ax1,ax2] where ax1 is the left axes and ax2 is the right-axes handles.
The process is not straight-forward since you have to manually scale the axes, use a listener, and adjust the behavior of the "restore view" toolbar button. If needed, see the answer mentioned above for a detailed description and comments. Tested in R2020b and R2021a.
Attachments
  • linkedYAxesDiffScale.m - Full demo of this answer.
  • linkedYAxesDiffScale_UI.m - Full demo of this answer modified to use UIFigure and UIAxes.
0. Set up demo figure
The goal will be to link the y-axes which have different scales .
figure()
tiledlayout(1,3)
ax = gobjects(1,2);
ax(1) = nexttile(1:2);
x = randn(1,500)*100;
y = randn(1,500)*50+500;
plot(x,y,'o')
grid on
ax(2) = nexttile(3);
histogram(y-mean(y),20,'Orientation','Horizontal')
grid on
Important: before proceeding, the yscales should be set using ylim() in both axes (not shown). The y-limits are already ideally set in the simple demo using auto Y-limit-mode.
1. Turn off interactions for axes #2 (right)
All zooming / panning should be done in axes #1 on the left.
ax(2).Interactions = [];
ax(2).Toolbar.Visible = 'off';
2. Set up a listener that will respond to y-axis limit changes in axes #1 (left)
% Compute scaling factor to convert ax2 y-scale from ax1 y-scale
xyscale = range(ax(2).YLim) / range(ax(1).YLim);
% Store original y-axis limits for both axes
axBaseLim = [ax(1).YLim; ax(2).YLim];
% Assign listener to ax1
ax(1).UserData.Listener = addlistener(ax(1),{'YLim'}, 'PostSet', ...
@(~,~)axisLimitListener([], [], [ax(1),ax(2)], xyscale, axBaseLim));
3. Address the "restore view" button problem
When you press the "restore view" button in the axis #1 toolbar, this listener will not respond. To address that, this block below and the "myRestoreButtonCallbackFcn" block below will update axes #2 when the "restore view" button is pressed in axes #1.
axTB = axtoolbar(ax(1),'default');
isRestoreButton = strcmpi({axTB.Children.Icon},'restoreview');
if any(isRestoreButton)
restoreButtonHandle = axTB.Children(isRestoreButton);
originalRestoreFcn = restoreButtonHandle.ButtonPushedFcn;
restoreButtonHandle.ButtonPushedFcn = ...
{@myRestoreButtonCallbackFcn, ax(2), originalRestoreFcn, xyscale, axBaseLim};
end
4. Define listener that responds to changes to y-axis limits in axes #1
function axisLimitListener(~,~,ax,scalingFactor,axBaseLim)
% Listener callback that responds to y-axis limit changes to ax1 and
% updates the y-axis limits to ax2.
% INPUTS
% ax: 1x2 array of axis handles to [ax1,ax2]
% scalingFactor: (see description of xyscale above)
% axBaseLim: (see description of axBaseLim above)
% Convert the ax2 ylim from ax1 to values normalized by the original axis range.
normLowerLimit = (ax(1).YLim(1) - axBaseLim(1,1))./range(axBaseLim(1,:));
% Compute the new lower limits to ax2.
newLimits = normLowerLimit.*range(axBaseLim(2,:)) + axBaseLim(2,1);
% Compute the new upper limits ax1.
newLimits(:,2) = newLimits + range(ax(1).YLim).*scalingFactor;
% Update ax1 limits
set(ax(2), 'YLim', newLimits)
end
5. Define the "restore view" callback that responds to axis #1 toolbar > Restore View
function myRestoreButtonCallbackFcn(hobj, event, ax2, originalCallback, xyscale, axBaseLim)
% Responds to pressing the restore button in the ax1 toolbar.
% originalCallback is a function handle to the original callback
% function for this button.
% xyscale and axBaseLim are defined elsewhere.
originalCallback(hobj,event) % reset ax2
axisLimitListener([],[],[event.Axes,ax2],xyscale,axBaseLim) % update ax1
end
  댓글 수: 8
Hamid Kilic
Hamid Kilic 2021년 9월 28일
Thanks, it works perfecly now but I had to use struct. The only thing I had to modify for it to work on R2020a was this line:
app.UIAxis1.UserData.Listener = addlistener(struct(app.UIAxis1).Axes,{'YLim'}, 'PostSet', ...
@(~,~)axisLimitListener(app, [], [], app.UIAxis1,app.UIAxis2], xyscale, axBaseLim));
Adam Danz
Adam Danz 2021년 9월 28일
Well that's odd 🤔
Thanks for sharing.

댓글을 달려면 로그인하십시오.

추가 답변 (0개)

카테고리

Help CenterFile Exchange에서 Develop uifigure-Based Apps에 대해 자세히 알아보기

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by