Dynamically reorder subplots in GUI

1 view (last 30 days)
Lask
Lask on 28 Jun 2017
Edited: Jan on 30 Jun 2017
Hello everyone,
I'm developing a GUI using GUIDE. The functionality I want to achieve implies that when the user clicks a button, a new subplot is added to a parent figure. I have managed to structure the subplots in a square layout (for instance, if the user clicks the button 9 times, 9 plots are displayed in 3 rows and 3 columns.) However, everytime a new subplot is added, I delete all the subplots handles and re-create them to include the new one. This isn't the desired behaviour since I just want to reorder the subplots (change their position) in figure in a fair enough square layout. Any help would be appreciated.
  5 Comments
Lask
Lask on 28 Jun 2017
Thanks Adam. What do you mean "programatically"? I'm doing the re-positioning of the subplots with the code I posted before. I'm open to any alternative or hint you could give me.
Adam
Adam on 28 Jun 2017
Well, by programmatically I was mostly referring to positionining axes explicitly based on an algorithm that calculates the positions for a grid of whatever size you give it.
subplot is limited if you use that and you have little choice but to keep deleting and recreating axes. I don't fully understand what your workflow is as to what is on these axes, but if you are just wanting to add a new axes deleting and recreating the existing 9 is certainly not very efficient.

Sign in to comment.

Accepted Answer

Jan
Jan on 28 Jun 2017
Edited: Jan on 30 Jun 2017
Hm. I've written a small example which shows, that moving the existing axes is not tedious:
function Callback(hObject, EventData) % *** ERROR - WILL CRASH! ***
handles = guidata(hObject);
% Increment the number of devices (subplots) on click
handles.n_devices = handles.n_devices + 1;
n_rows = ceil(sqrt(handles.n_devices));
% Old plots must be moved:
if handles.n_rows ~= n_rows
for iDev = 1:handles.n_devices - 1
% *ERROR*: subplot deletes existing axes!
axesH = subplot(n_rows, n_rows, iDev, 'Parent', handles.uipanel1);
set(handles.subplots_handles(iDev), 'Position', get(axesH, 'Position'));
delete(axesH);
end
end
handles.n_rows = n_rows;
handles.n_cols = n_rows;
% Plot new device:
a_subplot_handle = subplot(handles.n_rows, handles.n_cols, handles.n_devices, ...
'Parent', handles.uipanel1);
handles.subplots_handles(handles.n_devices) = a_subplot_handle;
plot(1:10, rand(1, 10));
guidata(hObject, handles);
end
Unfortunately it does not run, because subplot deletes the former axes overlapping with the new one. What a pitty. All I want to get is the new position of the axes, but subplot decides smartly that I want to delete objects.
Then I have to rewrite subplot to reply the position only and to avoid any fancy stuff. Wait a little bit...
[EDITED] A working version:
function main % Just to create a dummy figure
FigH = figure;
handles.n_devices = 0;
handles.n_rows = 0;
handles.n_cols = 0;
handles.subplots_handles = matlab.graphics.axis.Axes([]);
handles.uipanel1 = uipanel('Units', 'pixels');
handles.FullPos = get(handles.uipanel1, 'Position') + [40, 40, -60, -60];
uicontrol('Style', 'PushButton', 'String', 'Add plot', ...
'Position', [5, 5, 80, 20], ...
'Callback', @Callback);
guidata(FigH, handles);
end
function Callback(hObject, EventData)
handles = guidata(hObject);
% Increment the number of devices (subplots) on click
handles.n_devices = handles.n_devices + 1;
n_rows = ceil(sqrt(handles.n_devices));
% Old plots must be moved:
if handles.n_rows ~= n_rows
AxesPos = SubPlotPos(handles.FullPos, n_rows, n_rows);
for iDev = 1:handles.n_devices - 1
set(handles.subplots_handles(iDev), 'Position', AxesPos(iDev, :));
end
end
handles.n_rows = n_rows;
handles.n_cols = n_rows;
% Plot new device:
AxesPos = SubPlotPos(handles.FullPos, n_rows, n_rows, handles.n_devices);
a_subplot_handle = axes('Units', 'Pixels', 'Position', AxesPos, ...
'Parent', handles.uipanel1);
handles.subplots_handles(handles.n_devices) = a_subplot_handle;
plot(1:10, rand(1, 10));
guidata(hObject, handles);
end
This replaces subplot:
function AxesPos = SubPlotPos(FullPos, nCol, nRow, k)
% Position of diagrams - a very light SUBPLOT
% AxesPos = SubPlotPos(Area, nRow, nCol, kAxes)
% A very light version of SUBPLOT without shifting, force requests, an so on.
% Only the positions are replied and the caller has to create its axes itself.
% Input:
% Area: Available area [X, Y, W, H] in pixels. This area is filled by
% the diagrams.
% nCol, nRow: Number of columns and rows.
% kAxes: Reply position of the k'th axes only.
% Optional. Without this, all nCol*nRow positions are replied.
%
% Author: Jan Simon, Heidelberg, (C) 2017, License: CC BY-SA 3.0
% Formula: Space = a + b * n
% Increase [b] to increase the space between the diagrams.
if nRow < 3
BplusT = 0.18;
else
BplusT = 0.09 + 0.045 * nRow;
end
if nCol < 3
LplusR = 0.18;
else
LplusR = 0.09 + 0.05 * nCol;
end
spread = 0.7;
nPlot = nRow * nCol;
plots = 0:(nPlot - 1);
row = (nRow - 1) - fix(plots(:) / nCol);
col = rem(plots(:), nCol);
col_offset = FullPos(3) * LplusR / (nCol - LplusR);
row_offset = FullPos(4) * BplusT / (nRow - BplusT);
totalwidth = FullPos(3) + col_offset;
totalheight = FullPos(4) + row_offset;
width = totalwidth / nCol - col_offset;
height = totalheight / nRow - row_offset;
if width * 2 > totalwidth / nCol
if height * 2 > totalheight / nRow
AxesPos = [(FullPos(1) + col * totalwidth / nCol), ...
(FullPos(2) + row * totalheight / nRow), ...
width(ones(nPlot, 1), 1), ...
height(ones(nPlot, 1), 1)];
else
AxesPos = [(FullPos(1) + col * totalwidth / nCol), ...
(FullPos(2) + row * FullPos(4) / nRow), ...
width(ones(nPlot, 1), 1), ...
(spread * FullPos(ones(nPlot, 1), 4) / nRow)];
end
else
if height * 2 <= totalheight / nRow
AxesPos = [(FullPos(1) + col * FullPos(3) / nCol), ...
(FullPos(2) + row * FullPos(4) / nRow), ...
(spread * FullPos(ones(nPlot, 1), 3) / nCol), ...
(spread * FullPos(ones(nPlot, 1), 4) / nRow)];
else
AxesPos = [(FullPos(1) + col * FullPos(3) / nCol), ...
(FullPos(2) + row * totalheight / nRow), ...
(spread * FullPos(ones(nPlot, 1), 3) / nCol), ...
height(ones(nPlot, 1), 1)];
end
end
if nargin > 3 && ~isempty(k)
AxesPos = AxesPos(k, :);
end
end
These are not exactly the same positions as with subplot. But you can adjust the parameters to your needs.
See also the mayn submissions in the FEX to improve the subplot command: FEX: Search for "subplot"
  1 Comment
Lask
Lask on 28 Jun 2017
Edited: Lask on 28 Jun 2017
Thanks Jan. Yes, I can see what you're trying to do. I'm also trying to do something similar. I'm using an aux_figure to display the first subplot and get its position so I can move the original subplot without overlap. Hopefully we'll get to some interesting point. I'll wait, thanks.

Sign in to comment.

More Answers (0)

Categories

Find more on Graphics Performance in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!