Dynamically reorder subplots in GUI
1 次查看(过去 30 天)
显示 更早的评论
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 个评论
Adam
2017-6-28
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.
采纳的回答
Jan
2017-6-28
编辑:Jan
2017-6-30
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.
更多回答(0 个)
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Graphics Performance 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!