I am working on a GUI that is being initialized by creating several axes
along with an invisible colorbar
for each one (this is done so that axes maintain their predefined Position
)1. The handles to all axes and colorbars are stored.
Interactions with the UI may result in images being plotted on any one axes. I would like only the colorbar of the active axes to be shown at any given time, by appropriately setting the Visible
property of all colorbars.
I am having a problem with this approach, because I'm using imagesc
to update my axes, and this deletes any colorbar associated with the axes, making the stored handles invalid.
My question is: how can I use imagesc
or image
to update an axes that is associated with a colorbar, without the colorbar being deleted?
Here's how to reproduce this problem:
dbclear in newplot %// Needed for the code to be properly re-runnable
%// Create an example figure containing a colorbar:
figure(); imagesc(imread('cameraman.tif')); colorbar;
%// "Refreshing" the displayed image:
uiwait(msgbox('The image will now be refreshed. A breakpoint will be set in newplot.m'));
dbstop in newplot at 124 %// The line responsible for deleting the colorbar in R2015A/B
imagesc(imread('cameraman.tif'));
The line on which the breakpoint in newplot.m
is set reads:
cla(ax, 'reset', hsave);
Which is (un)surprisingly an undocumented way to invoke cla
(with 3 parameters) that preserves objects whose handles are found in hsave
.
Some ideas I had assuming colorbar deletion is inevitable (which I will pursue in case a "sane" solution is not found):
DeleteFcn
to the colorbar that saves its data to some struct
. Creating a new colorbar after imagesc
has finished, then iterating over the fields of the struct
and assigning all properties to the new colorbar object.findall(hFig,'type','colorbar')
or verify that each of the axes has a valid ColorbarPeerHandle
as per the Appendix below. Recreate the CB if invalid.Handle to the colorbar associated with a certain axes hAx
can be obtained (in hg2) using:
hCb = getappdata(hAx,'ColorbarPeerHandle');
Handle to the axes
associated with a colorbar object hCb
can be obtained using2:
hAx = hCb.Axes;
I've managed to come up with a couple of solutions:
safeUpdateImage1
- Based on the circumvention of cla(...)
via the creation of a new Image
object inside the axes after using hold
. This is good for cases when an Image
doesn't necessarily exist in the axes.safeUpdateImage2
- Following mikkola's suggestion, based on updating an existing Image
object. This is good for cases when there already is a ColorBar
and an Image
associated with the axes.Here's a demonstration of both solutions alongside the original problem:
function [] = Problem
dbclear in newplot %// Needed for the code to be properly re-runnable
%// Create an example figure containing a colorbar:
Img = imread('cameraman.tif');
figure(); imagesc(Img); hAx = gca; colorbar;
%// Refreshing the displayed image (comment/uncomment as needed):
switch questdlg('Please select an image update method:','Update method selection',...
'Broken','Safe1','Safe2','Safe1')
case 'Broken'
brokenUpdateImage(hAx,255-Img);
case 'Safe1'
safeUpdateImage1(hAx,255-Img);
case 'Safe2'
safeUpdateImage2(hAx,255-Img);
end
end
function brokenUpdateImage(hAx,newImg)
uiwait(msgbox('The image will now be refreshed. A breakpoint will be set in NEWPLOT'));
dbstop in newplot at 124 %// The line responsible for deleting the colorbar in R2015A/B
imagesc(newImg,'Parent',hAx);
end
% For cases when the only desired child is an Image and the axes contents are unknown
function safeUpdateImage1(hAx,newImg,imgType,imgUpdateFcn)
if nargin < 4 || isempty(imgUpdateFcn)
imgUpdateFcn = @imagesc;
end
if nargin < 3 || isempty(imgType)
imgType = 'Image';
end
if strcmp(hAx.NextPlot,'replace') %// Equivalent to checking "ishold == false"
hAx.NextPlot = 'add'; %// Equivalent to "hold on"
%// hCurrImgs = get(hAx,'Children'); %// Deletes all types of Children
hCurrImgs = findall(hAx,'type','Image'); %// Deletes only graphical objects of type
%// "matlab.graphics.primitive.Image"
for ind1=1:numel(hCurrImgs)
delete(hCurrImgs(ind1));
end
imgUpdateFcn(newImg,'Parent',hAx);
if strcmpi(imgType,'Image')
axis(hAx,'tight','ij');
%// 'tight' - XLimMode, YLimMode, and ZLimMode change to 'auto'. The limits automatic-
%// ally update to incorporate new data added to the axes. To keep the limits
%// from changing when using hold on, use axis tight manual.
%// 'ij' — Reverse direction. For axes with a 2-D view, the y-axis is vertical with
%// values increasing from top to bottom.
end
end
end
%// When it's known that the axes contains at least one Image:
function safeUpdateImage2(hAx,newImg,~,~)
%// <Input checking code>
hCurrImgs = findall(hAx,'type','Image');
%// <Optional code to decide which Image child to update>
hCurrImgs(1).CData = newImg; %// In this example, only update the "topmost" child
end