How to align axes in AppDesigner

Hi Folks,
In AppDesigner, is there a simple way to code up a PlotAll(app) function that can easily align vertically the x-axes for vertically stacked axes that have the same "Position" coordinates? For example, below is an example where I have three aligned UIAxes that have the same pixel horizontal positions and thicknesses, but when I plot, the axes labels and the magnitude of the y-axis data values end up causing the plot internal axes to reposition themselves and be mis-aligned.
Thank you,
Kris

4 Comments

Can you post example code so we can reproduce it here? That will make playing around with the properties a lot easier.
I’ve had this problem several times with different GUIs in AppDesigner. In this case, I’m using yyaxis to plot left and right y-axes. I use cla to reset the axes before I plot a new time series. For example:
Cla(app.UIAxis1, “reset”)
Plot(app.UIAxis1, x, y)
Hold(app.UIAxis1, “on”)
Plot(app.UIAxis1, x2, y2)
Then repeat for the other two axes above
If there were only a function or property I could call on that would position only the axes box frame (ignoring labels, color bars, etc), that would be perfect.
Here is some code that shows how I am trying to use "PositionConstraint", but it does not seem to be working as I am expecting. To put it precisely, "PositionConstraint" has not impact on alignment at all in my code below. If I remove the "reset" calls, the alignment improves a little bit, but the vertical lines that constitute the y-axes in the plots that have the same X Position coordinates and widths are clearly misaligned. Any tricks or usage models out there that have worked for people in AppDesigner I would appreciate hearing, because this is the one thing that keeps making my GUIs look unprofessional.
% In Startup Callback:
app.UIAxesBaro.PositionConstraint = "innerposition";
app.UIAxesNETEN.PositionConstraint = "innerposition";
app.UIAxesNWTEN.PositionConstraint = "innerposition";
% In my PlotAll(app) function that is called when I update the
% GUI plots
% Plot barometric pressure axes
h = app.UIAxesBaro;
cla(h, "reset");
h.PositionConstraint = "innerposition";
yyaxis(h, "left");
h2=plot(h, x, y,sty,'linewidth', ...
app.WidthEditField.Value,'tag','Baro'); % in kPa
if (~app.BaroCheckBox.Value) h2.Visible = "off"; end;
ylabel(h, 'Barometric Pressure (kPa)');
[tOut, dOut, tOut_UTC] = FindPressTimeWindow(app, yr, jd, hr, mn, sc, tdur);
yyaxis(h, "right");
h2 = plot(h, tOut_UTC, dOut(:,15), 'tag','press','linewidth', app.WidthEditField.Value);
if (~app.PressCheckBox.Value)
for j = 1:length(h2)
h2(j).Visible = "off";
end
end
yyaxis(h, "right");
ylabel(h, 'Injection Pressure (kPa)');
xlabel(h, 'Time');
title(h, 'Site Data');
h = app.UIAxesNWTEN;
cla(h, "reset");
h.PositionConstraint = "innerposition";
yyaxis(h, "right");
h2=plot(h, x2, y2,sty,'linewidth', app.WidthEditField.Value,'tag','TiltTideE','color',lineClrs(iClr,:));
hold(h, "on");
yyaxis(h, "right");
h2=plot(h, x3, y3,sty,'linewidth', app.WidthEditField.Value,'tag','TiltTideE','color',lineClrs(iClr,:));
hold(h, "on");
xlabel(h, 'Time');
title(h, 'NE Strain Meter');
yyaxis(h, "left"); ylabel(h, 'Uncalibrated Strain (n\epsilon)');
yyaxis(h, "right"); ylabel(h, 'Tilt (nrad)');
h2 = legend(h, legendH, legendStr, 'location', 'northeast','tag', 'legendNE'); if (~app.LegendCheckBox.Value) h2.Visible = "off"; end
xlim(app.UIAxesNWTEN,"tight");
xlim(app.UIAxesBaro,"tight");

Sign in to comment.

 Accepted Answer

The solution to this is to recognize that the property of interest to make consistent across all the vertically stacked axes is the first element of the UIAxes InnerPosition properties. This refers precisely to the box of interest. Note that on this website, the 'blue box' in the figure outlining the InnerPosition is incorrectly labeled the "Position" property in the caption.
So for me, at the end of all my plot updates, I simply need to choose one of the vertically stacked axes as the reference, and then set the other's to have its InnerProperty(1) value:
app.UIAxes1.InnerPosition(1) = app.UIAxes0.InnerPosition(0);
app.UIAxes1.InnerPosition(2) = app.UIAxes0.InnerPosition(0);
etc.

More Answers (1)

Using a <https://www.mathworks.com/help/matlab/ref/tiledlayout.html tiledlayout> would do this for you without any additional position calculus.
You would only need to specify a uipanel as the parent of your tiled layout. See the example below.
Something to consider when using UI axes is that it is essientially a UIPanel and axes together (I think I read that on a mathworks answer one time). Using something like tiledlayout and regular axes is usually better performance (initial creation, and reactivity) and it can do everything a UIAxes can do.
One difference is you do have to programatically asign callbacks, unlike UIAxes that you can do in the app designer.
clearvars
% data
x1=0:.1:1;
y1=x1;
y2=100*y1;
% tiled layout
% p = some parent like a uipanel
% t = tiledlayout(p,'vertical')
t=tiledlayout('vertical');
% first axis
ax=nexttile(t);
plot(ax, x1, y1)
ylabel(ax,'Label1 left')
hold(ax,'on')
yyaxis right
plot(ax,x1,y2)
ylabel(ax,'Label1 right')
ylim(ax,[0 10]) % just to show both lines
% next axis
ax2=nexttile(t);
plot(ax2, x1, y2)
ylabel(ax2,'Label2 left')
hold(ax2,'on')
yyaxis right
plot(ax2,x1,y1)
ylabel(ax2,'Label2 right')
ylim(ax2,[0 10]) % just to show both lines
% insert push button callback to your app to add / delete axes

9 Comments

Hi! Thanks for the reply. I am away from Matlab the moment, and can’t test things. The documentation for tiledlayout only shows very simple examples of vertically stacked axes. None of those examples show different y-axis and tick labels. If one y-axis is in the 100’s, and the other from 0 to 1, will the vertical y-axes still be aligned? If not, the problem would be even worse for me because I have a right and left axis with different magnitudes and labels.
I have a strong feeling the answer is yes it will work.
I updated the answer with an example but I guess the code does not execute on a phone. I will amend it later if you do not get to it first.
I updated, let me know what you think.
Hi Strider,
Thanks for your help!
I tried this solution, but tiledlayout() does not like it when I use a uipanel as the parent. Furthermore, if I use app.UIFigure itself as the parent to pass to tiledlayout(), it also does not like that either. It results in a syntax error stating this. Here is the example code:
% tiled layout
% p = some parent like a uipanel
p = uipanel("Parent",app.UIFigure,"Position",[300 50 300 400]);
%t = tiledlayout(app.UIFigure,'vertical');
t = tiledlayout(p,'vertical');
%t=tiledlayout('vertical');
% first axis
ax=nexttile(t);
plot(ax, x1, y1)
ylabel(ax,'Label1 left')
hold(ax,'on')
yyaxis(ax,"right");
plot(ax,x1,y2)
ylabel(ax,'Label1 right')
% next axis
ax2=nexttile(t);
plot(ax2, x1, y2)
ylabel(ax2,'Label2 left')
hold(ax2,'on')
yyaxis(ax2,"right");
plot(ax2,x1,y1)
ylabel(ax2,'Label2 right')
The parent of tiledlayout should work if it is a (from the doc)
"Parent container, specified as a Figure, Panel, Tab, or TiledChartLayout object."
I tried to run your example and it was missing x1 or any of the plotting data.
I did not see an error pasted in your comment, but I tried it by replacing app.UIFigure with a regular figure and it worked for me (see below).
I did notice that uifigure does not appear to be supported in MATLAB online. Is that a source of your error?
clearvars
close all
% data
x1=0:.1:1;
y1=x1;
y2=100*y1;
% tiled layout
% f = uifigure; % this does not run on MATLAB online apaprently
f = figure; % use a regular figure instead
p = uipanel("Parent",f );
%t = tiledlayout(app.UIFigure,'vertical');
t = tiledlayout(p,'vertical');
%t=tiledlayout('vertical');
% first axis
ax=nexttile(t);
plot(ax, x1, y1)
ylabel(ax,'Label1 left')
hold(ax,'on')
yyaxis(ax,"right");
plot(ax,x1,y2)
ylabel(ax,'Label1 right')
% next axis
ax2=nexttile(t);
plot(ax2, x1, y2)
Hi Strider,
Thanks for this reply. The errors I consistently get using tiledlayout is:
Error using tiledlayout
Invalid arguments.
Error in TestStrider/startupFcn (line 25)
t = tiledlayout(app.UIFigure);
The x1, y1, and y2 were the same as you defined in your example.
Thanks again!
I am suspicious that the UI figure associated with the app would work. Can you try putting a panel on the app and then using the panel as the parent?
@Kristoffer Walker: The syntax "tiledlayout(parent)", i.e., not specifying the tiledlayout arrangement or dimensions, was introduced in R2024b (see the item here under R2024b), so that explains why
t = tiledlayout(app.UIFigure);
doesn't work for you under R2022b.
Similarly, specifying "vertical" or "horizontal" as the arrangement was introduced in R2023a, so those are also unavailable.
In R2022b, looks like your options are:
% use 'flow' arrangement
t = tiledlayout(app.UIFigure,'flow');
% or specify the dimensions
t = tiledlayout(app.UIFigure,3,2);
Strider, I tried uipanel as well. Thanks again for all your help

Sign in to comment.

Categories

Products

Release

R2022b

Community Treasure Hunt

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

Start Hunting!