Axis labels are invisible? - matlab

I have the following code:
figure(1);
suptitle('Percentage of games won with board size');
count = 0;
% relation of board size and the game outcome
for i = 1:4 % number combination of player and opponent
for j = 1:4 % starting indexes for board sizes
percentageStepResult = [sum(sizeResultVec{i}(j:4:120) == 1), sum(sizeResultVec{i}(j:4:120) == -1), sum(sizeResultVec{i}(j:4:120) == 0)];
count = count + 1;
handle = subplot(4, 4, count);
xlabel('x axis');
ylabel('y axis');
pie(percentageStepResult)
end
end
Which generates the following plot:
Why doesn't this display the labels at all? I'm trying to move towards having one xlabel and one ylabel for the whole plot, but I'm confused as to why they won't even display for the individual subplots.

The concept of X and Y axis make little sense to me for a pie chart, and probably for the Mathworks too, so they decided to "hide" these meaningless labels.
The labels are not displayed because each axe underlying the pie chart has its visible property set to 'off'. This hides everything about the axe (i.e. ticks, grid line, background color, etc...).
If they are not meaningless to you and you really want the labels to be displayed, you have to set the axes visible property to 'on'. The code below is inspired from your example and shows you how to do it.
The problem with this method, is that you will have to manually 'hide' everything else you do not want to see. This is why I hid the ticks, the background and the grid lines, but the axe border will remain.
count = 0 ;
hdl = zeros(4,4) ;
for i = 1:4 %// number combination of player and opponent
for j = 1:4 %// starting indexes for board sizes
percentageStepResult = rand(4,1) ;
count = count + 1 ;
hdl(i,j) = subplot(4, 4, count) ;
pie(percentageStepResult)
set( hdl(i,j) , 'Visible','on' ) %// set the underlying axes to visible
set( hdl(i,j) , 'Color','none' ) %// set the axes background color to nothing (transparent)
set( hdl(i,j) , 'XTick',[] , 'YTick',[] ) %// remove the 'X' and 'Y' ticks
grid off %// make sure there is no grid lines
xlabel('x axis');
ylabel('y axis');
end
end
Note that I also changed the variable which holds the handles to the axes. It is not a good idea to call something handle as it is the name of a Matlab built in function. I also put these handles in an array so you can set the axes properties later on if you'd like.
Also note that you can combine all the calls to set( hdl(i,j) , ... ) into one line, I only developed it here for clarity.
edit: Look at the answers from this questions if you want to also hide the axe border (X0 and Y0 lines).
This showed you how to force each axe label to be displayed, but in practice it is very messy. I would recommend instead to just create text objects and work out how to position them near each pie. At least you won't have to manage the visibility of everything else manually.

Related

Labeling plots such that label is aligned with the ylabel outside the axes

Please see the following code which creates a 2 by 2 subplot with some plots:
x = linspace(0,2*pi);
y = sin(x);
hfig = figure('Position',[1317 474 760 729]);
subplot(2,2,1)
plot(x,y)
ylabel('plot1');
subplot(2,2,2)
plot(x,y.^2)
ylabel('plot2');
subplot(2,2,3)
plot(x,y.^3)
ylabel('plot3');
subplot(2,2,4)
plot(x,abs(y))
ylabel('plot4');
in each one, I have added labels by hand in Tools: Edit plot (a) (b) (c) (d) producing this figure:
The problem is, if I resize the plot they are no longer aligned with the ylabel text:
Is there a way to add these labels programmatically and have them automatically align to the ylabel text? I am surprised MATLAB does not have something like this built in already.
Thanks
This is not something that is easy to do without attaching a listener to the figure resize event (see example), and doing some computations related to aspect ratios.
It's not entirely clear what sort of objects your labels are (text or annotation), so I'll just show how to do this programmatically using the text command, which creates labels in axes coordinates (as opposed to figure coordinates). This doesn't solve the problem entirely, but it looks better, possibly to an acceptable degree:
function q56624258
x = linspace(0,2*pi);
y = sin(x);
hF = figure('Position',[-1500 174 760 729]);
%% Create plots
[hAx,hYL] = deal(gobjects(4,1));
for ind1 = 1:3
hAx(ind1) = subplot(2,2,ind1, 'Parent' , hF);
plot(hAx(ind1), x,y.^ind1);
hYL(ind1) = ylabel("plot" + ind1);
end
hAx(4) = subplot(2,2,4);
plot(hAx(4), x,abs(y));
hYL(4) = ylabel('plot4');
%% Add texts (in data coordinates; x-position is copied from the y-label)
for ind1 = 1:4
text(hAx(ind1), hYL(ind1).Position(1), 1.1, ['(' char('a'+ind1-1) ')'], ...
'HorizontalAlignment', 'center');
end
Note several modifications to your code:
The handles returned by some functions that create graphical elements are now stored (mainly: hAx, hYL).
All functions that create graphical elements (subplot, plot, ylabel) now have the target (i.e. parent or container) specified.
I changed the 'Position' of the figure so that it works in my setup (you might want to change it back).

MATLAB: issue with putting x and y labels on each side of a plot

Based code from here, I wrote a function which plots a figure and puts x-axis labels on both the top and bottom of the figure, as well as y-axis labels on the left and right side. My problem is that I need to run the code multiple times and each time the labels get written over, and for some reason the y-axis labels get overwritten in a weird way as shown,
first run:
second run:
The following is an mwe:
% sample data, plot
x=[1:168];
y=x;
plot(x, y, 'r', 'LineWidth', 1);
set(gca, 'XTick', [], 'YTick', []);
% set left yaxis label
ylabel(directions{1,1},'Rotation',-360);
% Adjust position - this seems to be what's causing the issue!
ylabelh = get(gca,'YLabel');
rpos = get(ylabelh,'Position');
set(ylabelh,'Position',rpos + [1.5*rpos(1) 0 0])
% do stuff... as per link above
axesPosition = get(gca,'Position');
hNewAxes = axes('Position',axesPosition,... %# Place a new axes on top...
'Color','none',... %# ... with no background color
'YAxisLocation','right',... %# ... located on the right
'XTick',[],'YTick',[],... %# ... with no x tick marks
'Box','off');
ylabel(hNewAxes,directions{2,1},'Rotation',-360); % yaxis label right
% Adjust position
ylabelh = get(gca,'YLabel');
Lpos = get(ylabelh,'Position');
set(gca,'YTick',[]);
set(ylabelh,'Position',Lpos+ [+Lpos(1)*0.02 0.05 0])
% -- And repeat for x axis -- %
% x axis labels
xlabel(directions{3,1},'Rotation',-360); % xaxis label bottom
% Adjust position
xlabelh = get(gca,'XLabel');
xlabpos = get(xlabelh,'Position');
set(gca,'XTick',[]);
rpos = get(xlabelh,'Position');
% do stuff ... as above
axesPosition = get(gca,'Position');
hNewAxes = axes('Position',axesPosition,... %# Place a new axes on top...
'Color','none',... %# ... with no background color
'XAxisLocation','top',... %# ... located on the right
'XTick',[],'YTick',[], ... %# ... with no x tick marks
'Box','off');
% xaxis label top
xlabel(hNewAxes,directions{4,1},'Rotation',-360);
Weirdly, if I run the code as a script it's fine (well, the labels get written over but I don't see the issue with the y axis labels) but if I run as a function multiple times in debug mode (which is currently the primary way I'm using it) then I see the above artefacts.
If you run your code then check at the end you will see that you have created 3 axes in your script. Run it once and they type get(gcf,'Children'). If you run it again you are adding another 2 axes, this continues everytime you run it.
Why does this happen?
Well your code creates 2 new axes handles (which you call the same name -> which you shouldn't do) - but only after you have already used gca -> i.e. the current axes (if none exists one is created).
I would advise you to completly reconfigure you code. Start by creating 2 axes before you do any plotting/labeling etc.. store the 2 axes variables independently which will allow you to refer to them as and when you need to.
Also it good practice to never rely on gca or gcf to get the currently axes or figure -> as it will eventually come back to bite you. Store the axes/figure handles and refer to them explicitly.

How to subplot + imagesc with a Position in Matlab?

Situation: change Position of a single subplot with imagesc
%% Matlab recommends this structure if axes(); in loop
a1 = subplot(1,2,1);
a2 = subplot(1,2,2);
while 1    
plot(a1, rand(3))    
plot(a2, rand(3))    
drawnow
end
%% Test code
unitsPerInches=[0 0 15 15];
figure('Units', 'inches');
a1 = subplot(1,2,1);
a2 = subplot(1,2,2);
while 1    
set(a1, 'Position', unitsPerInches); % TODO how to affect a1's Position here only?
imagesc(a1, rand(3))    
imagesc(a2, rand(3))    
drawnow
end
Open
What is imagesc corresponding structure to plot(a1,rand(3))?
How to change Position of figure inside the loop?
Forward in Q1 - almost done
%% Extension to imagesc
figure
a1=subplot(1,2,1);
a2=subplot(1,2,2);
for counter=1:2;
imagesc(a1,rand(3))
imagesc(a2,rand(3))
drawnow
end
Fig. 1 Output of Docs example, Fig. 2 Output of Imagesc, Fig. 3 about Q2 where Position affects both subplots
Q1 is almost done; I have just forgotten how to get corresponding plot in imagesc; x-values should be put there but pseudocode imagesc(a1,XDATA,rand(3)) is unsuccessfuly.
Backward in Q2
Code
%% Extension to imagesc
unitsPerInches=[0 0 15 15];
figure
a1=subplot(1,2,1);
a2=subplot(1,2,2);
for counter=1:2;
set(a1, 'Position', unitsPerInches); % TODO how to affect a1's Position here only?
imagesc(a1,rand(3))
imagesc(a1,rand(3))
drawnow
end
Output: position affects both images in Fig. 3.
I think I have misunderstood the meaning of Position here because so strange output.
Testing EBH's proposal for Q2
The implicit assignments cause problems when having two figures where subplots
unitsPerInches=[0 0 15 15];
aFig=figure();
a1=subplot(1,2,1);
a2=subplot(1,2,2);
bFig=figure();
b1=subplot(1,2,1);
b2=subplot(1,2,2);
for counter=1:2;
if counter==1
set(a1, 'Position', unitsPerInches); % affect only position of a1
end
subplot(1,2,counter);
imagesc(rand(3));
drawnow
subplot(1,2,counter);
imagesc(rand(3));
drawnow
end
Output: second figure of subplots fails.
System: Linux Ubuntu 16.04 64 bit
Linux kernel 4.6
Matlab: 2016a
Related threads: Matlab7 'bug' when using "subplot" + "imagesc"?
I'm not 100% sure what you're asking, but I think it's about combining multiple imagesc statements while in a loop. I'd do something more direct -- use gca and put the subplot inside the loop. Quite often, if you want to programmatically address multiple images, it makes sense to put them in some sort of structure other than creating lots of differently named variables. Note also that while 1 is probably not really what you want -- it will hammer your graphics device drivers -- and that pause can take an argument to act as a wait function, for some fraction of a second if required.
testImages{1}=double(imread('coins.png'));
testImages{2}=double(imread('cameraman.tif'));
h=figure;
set(h,'color','w'); %This handle refers to the background window
for ix=1:2
subplot(1,2,ix);
imagesc(testImages{ix});
axis equal;
colormap gray;
%Change, for example, axis position
curPoss=get(gca,'Position'); %gca stands for 'get current axis'
set(gca,'Position',curPoss+1e-2*ix^2); %Move one image up a bit
end
Does that help?
If you want to jump between figures, make an array of them, and use it within a loop:
unitsPerInches = [0.1 0.1 0.15 0.15];
figs = [figure(1) figure(2)];
for f = 1:numel(figs)
figure(figs(f));
for counter = 1:2;
subplot(1,2,counter);
imagesc(rand(3));
drawnow
end
figs(f).Children(1).Position = unitsPerInches;
figs(f).Children(2).Position = unitsPerInches+0.3;
end
Your original values for unitsPerInches was wrong, since the 'Position' property of an axes takes values between 0 to 1 by default. You can change this using the 'Units' property, like:
figs(f).Children(1).Units = 'inches';
The output from this example is two figures that looks like this:
Where there is a small axes on the down-left and a big one on the top-right.
So, back to your original questions:
What is imagesc corresponding structure to plot(a1,rand(3))?
Instead of passing the axes to imagesc, set the focus on the relevant figure, and subplot with:
figure(h)
subplot(x,y,c)
imagesc(data)
where h is a handle to the relevant figure, c is the place of the subplot within h where you want to plot the image (a number between 1 to x*y), and after these two line you call imagesc.
How to change 'Position' of figure inside the loop?
In this question it is not clear if you want to change the 'Position' of the figure or the axes, they have different units and meaning, but both are accessible in the same way:
h.Position = [left bottom width height]; % for the position of the figure
h.Children(c).Position = [left bottom width height]; % for the position of the axes
where h is as before, but c may be numbered differently, so subplot(x,y,c) may not refer to the same axes as h.Children(c). However, you can always use gca to get the current axes handle:
ax = gca;
ax.Position = [left bottom width height];
Hope it's all clear now, and if there are further questions, let me know ;)

Separating axes from plot area in MATLAB

I find that data points that lie on or near the axes are difficult to see. The obvious fix, of course, is to simply change the plot area using axis([xmin xmax ymin ymax]), but this is not preferable in all cases; for example, if the x axis is time, then moving the minimum x value to -1 to show activity at 0 does not make sense.
Instead, I was hoping to simply move the x and y axes away from the plot area, like I have done here:
left: MATLAB generated, right: desired (image editing software)
Is there a way to automatically do this in MATLAB? I thought there might be a way to do it by using the outerposition axes property (i.e., set it to [0 0 0.9 0.9] and drawing new axes where they originally were?), but I didn't get anywhere with that strategy.
The answers here already show you most of the way - here is the last step to separate the x and y axle as per the example you put together.
f = figure ( 'color', 'white' );
% create the axes and set some properties
ax = axes ( 'parent', f, 'box', 'off', 'nextplot', 'add', 'XMinorTick', 'on', 'YMinorTick', 'on' );
% plot some data
plot ( ax, 0:10, [0:10].^2, 'rx-' )
% modify the x and y limits to below the data (by a small amount)
ax.XLim(1) = ax.XLim(1)-(ax.XTick(2)-ax.XTick(1))/4;
ax.YLim(1) = ax.YLim(1)-(ax.YTick(2)-ax.YTick(1))/4;
% Set the tick direction
ax.TickDir = 'out';
% draw the plot to generate the undocumented vertex data var
drawnow()
%% R2015a
% X, Y and Z row of the start and end of the individual axle.
ax.XRuler.Axle.VertexData(1,1) = 0;
ax.YRuler.Axle.VertexData(2,1) = 0;
%% R2015b
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
vd = get(ax.XAxis.Axle,'VertexData');
% reset the zero value
vd(1,1) = 0;
% Update the vertex data
set(ax.XAxis.Axle,'VertexData',vd);
% repeat for Y (set 2nd row)
vd = get(ax.YAxis.Axle,'VertexData');
vd(2,1) = 0;
set(ax.YAxis.Axle,'VertexData',vd);
Edit: The vertex is something that Matlab recreates whenever the axes/figure changes size or if you zoom or pan for example.
You can try to counteract this (remember you are using undocumented features here) by adding a listener to attempt to capture this. We can use the MarkedClean event which is called quite a lot of times.
addlistener ( ax, 'MarkedClean', #(obj,event)resetVertex(ax) );
Where you resetVertex function is something like: (R2015b shown only)
Edit 2 added the code to turn off the minor ticks below 0.
function resetVertex ( ax )
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
ax.XAxis.Axle.VertexData(1,1) = 0;
% repeat for Y (set 2nd row)
ax.YAxis.Axle.VertexData(2,1) = 0;
% You can modify the minor Tick values by modifying the vertex data
% for them, e.g. remove any minor ticks below 0
ax.XAxis.MinorTickChild.VertexData(:,ax.XAxis.MinorTickChild.VertexData(1,:)<0) = [];
ax.YAxis.MinorTickChild.VertexData(:,ax.YAxis.MinorTickChild.VertexData(2,:)<0) = [];
end
Note: this uses undocumented features -> so may only work in certain versions of Matlab (I have added the code for r2015a & r2015b) and Matlab may recreate the vertex data depending on what you do with the plots..
Here is a simple way for achieving that:
% some data:
x = 1:100;
f=#(x) 5.*x;
y=f(x)+rand(1,length(x))*50;
close all
% plotting:
f1 = figure('Color','white');
ax = axes;
plot(ax,x,y,'o');
% 'clean' the data area a little bit:
box off
ax.TickDir = 'out';
% pushing the axis a bit forward:
lims = axis;
pos = ax.Position;
axis([lims(1)-ax.XTick(2)/5 lims(2)+0.1 lims(3)-ax.YTick(2)/5 lims(4)+0.1])
% Create lines
firstXtick = 0.013; %this value need to be adjusted only once per figure
firstYtick = 0.023; %this value need to be adjusted only once per figure
lx = annotation(f1,'line',[pos(1) pos(1)+firstXtick],...
[pos(2) pos(2)],'Color',[1 1 1],'LineWidth',1);
ly = annotation(f1,'line',[pos(1) pos(1)],...
[pos(2) pos(2)+firstYtick],'Color',[1 1 1],'LineWidth',1);
Which yields this figure:
The only thing to adjust here, once per type of figure, is firstXtick and firstYtick values, that have to be fine tuned to the specific axis. After setting them to the correct value the figure can be resized with no problem. Zoom and pan require a little fixes.
You can start your axes from less than zero and then remove the less than zero ticks from your plot. e.g.
plot(0:3:30,0:3:30); %Some random data for plotting
h = gca;
axis([-1 30 -1 30]); %Setting the axis from less than zero
box off; %Removing box
h.TickDir = 'out'; %Setting Direction of ticks to outwards
h.XTickLabel(1)= {' '}; %Removing the first tick of X-axis
h.YTickLabel(1)= {' '}; %Removing the first tick of Y-axis
With this code, you'll get this result:
This may have a drawback, sometimes, that zero ticks may also get removed (as you can see in above figure). This is because the plot had set the first ticks of axes equal to zero. This can be avoided using if condition. So, the code can be modified as below:
plot(0:3:30,0:3:30);
h = gca;
axis([-1 30 -1 30]);
box off;
h.TickDir = 'out';
if str2num(cell2mat(h.XTickLabel(1))) <0
h.XTickLabel(1)= {' '};
end
if str2num(cell2mat(h.YTickLabel(1))) <0
h.YTickLabel(1)= {' '};
end
The above code will yield the following result:-
Also note that, for your case, since your axes ticks are very less, -1 may not be much suitable for the starting value of axes and you may need to use -0.1 instead i.e. axis([-0.1 30 -0.1 30]);
With a slight modification of #matlabgui's answer you can track the (major) tick limits:
ax = gca();
% Set the tick direction
ax.TickDir = 'out';
% Make sure this stays when saving, zooming, etc
addlistener ( ax, 'MarkedClean', #(obj,event) change_ticks(ax) );
% Draw the plot to generate the undocumented vertex data variable
% and call callback for the first time
drawnow();
The callback
function change_ticks( ax )
% Modify ruler
ax.XRuler.Axle.VertexData(1,1) = ax.XTick(1);
ax.XRuler.Axle.VertexData(1,2) = ax.XTick(end);
ax.YRuler.Axle.VertexData(2,1) = ax.YTick(1);
ax.YRuler.Axle.VertexData(2,2) = ax.YTick(end);
end
I haven't test extensively but seems to work for custom ticks too. This nicely cuts the rulers not only on zero but beyond the fist and last tick. This was tested in Matlab 2019a on Windows and ax.XRuler.Axle.VertexData works just fine. Note this is only for major ticks!

Put datatip stack on top of axis label and update axes label after a change was done on axes position

This issue is only for unix matlabs, windows users won't be able to reproduce it.
I am having trouble while trying to create datatips in which are on top of the y axis label. The following picture illustrate the issue:
As you can see, the datatips created close to the ylabel will get bottom to the ylabel text, while the desire effect is the opposite: the datatip to be on top of the axis label.
I generated the plot with the following (not so minimal) code, which is available bellow. You may remove the lines commented with % may be removed, or even just put a datatip on −78 instead of a loop in order to achieve a faster testing script, but I leave this code if someone one day wants it to create custom datatips (in this case, consider seeing also http://undocumentedmatlab.com/blog/controlling-plot-data-tips/):
gradientStep = 1e-1;
x=-100:gradientStep:100; xSize=numel(x);
y=x.^3-x.^2;
figH=figure(42);
lineH=plot(x,y);
ylabel('YLabel (YUnits)','FontSize',16)
xlabel('XLabel (XUnits)','FontSize',16)
dcH=datacursormode(figH);
nTips = 20; % May change the loop for a datatip at x=-78.
for pos = round(linspace(2,xSize,nTips))
datatipH=dcH.createDatatip(lineH,...
struct('Position',[x(pos) y(pos)]));
orientation = 'top-left';
if pos>1
tipText{1} = 'The grandient here is: ';
tipText{2} = ['\Deltax:',sprintf('%d',x(pos)-x(pos-1)),' XUnits'];
tipText{3} = ['\Deltay:',sprintf('%d',y(pos)-y(pos-1)),' YUnits'];
else
tipText = 'Cannot calculate gradient here.';
end
bkgColor = [1 1 .5]; % May be removed.
fontSize = 12; % May be removed.
set(datatipH,'StringFcn',(#(~,~) tipText),'Orientation',...
orientation,'backGroundColor',bkgColor,'FontSize',...
fontSize,'Draggable','on'); % Only set text and orientation needed.
datatipTextBoxH=get(datatipH,'TextBoxHandle'); % May be removed.
uistack(datatipH,'top'); % Unfortunately makes no effect, since the ylabel handles is not at the axes children list
datatipTextBoxH=get(datatipH,'TextBoxHandle');
set(datatipTextBoxH,'HorizontalAlignment','left',...
'VerticalAlignment','top','Margin',0.02,'Interpreter',...
'tex','FontName','Courier','FontSize',fontSize); % May be removed.
end
uistack(get(gca,'YLabel'),'bottom') % Also makes no effect, for the same reason.
I have tried:
uistack all datatips to top,
uistack the label to bottom (both of them don't work because the ylabel handle is not in the axes children handles).
Update: After implementing the #horchler' solution, a new issue appeared: when zooming and panning the axes, the axes label would also move. I've found a small fix for that, I changed the following aspects:
Set datatip z-value to 1, so that it will always be higher than ylabel axis z.
Recreating the ylabel afterwards a pan or zoom movement occurs. For this, I implemented localAxisUpdate function that get the old ylabel properties, replace it by a new one, and them reset all settable properties but the ylabel position. For this I used this reference
The resulting code is as follows:
function test
gradientStep = 1e-1;
x=-100:gradientStep:100; xSize=numel(x);
y=x.^3-x.^2;
figH=figure(42);
lineH=plot(x,y);
ylabel('YLabel (YUnits)','FontSize',16)
xlabel('XLabel (XUnits)','FontSize',16)
dcH=datacursormode(figH);
%nTips = 20;
%for pos = round(linspace(2,xSize,nTips))
pos = find(x>-78,1);
datatipH=dcH.createDatatip(lineH,...
struct('Position',[x(pos) y(pos) 1]));
orientation = 'top-left';
if pos>1
tipText{1} = 'The grandient here is: ';
tipText{2} = ['\Deltax:',sprintf('%d',x(pos)-x(pos-1)),' XUnits'];
tipText{3} = ['\Deltay:',sprintf('%d',y(pos)-y(pos-1)),' YUnits'];
else
tipText = 'Cannot calculate gradient here.';
end
bkgColor = [1 1 .5]; % Light Yellow
fontSize = 12;
set(datatipH,'StringFcn',(#(~,~) tipText),'Orientation',...
orientation,'backGroundColor',bkgColor,'FontSize',...
fontSize,'Draggable','on');
datatipTextBoxH=get(datatipH,'TextBoxHandle');
datatipTextBoxH=get(datatipH,'TextBoxHandle');
set(datatipTextBoxH,'HorizontalAlignment','left',...
'VerticalAlignment','top','Margin',0.02,'Interpreter',...
%end
% Set changes due to zoom and pan to also use adaptativeDateTicks:
set(zoom(figH),'ActionPostCallback',...
#(~,~) localAxisUpdate(gca));
set(pan(figH),'ActionPostCallback',...
#(~,~) localAxisUpdate(gca));
end
function localAxisUpdate(aH)
% Fix axis label on top of datatip:
ylh = get(aH,'YLabel');
% Get original YLabel properties
ylstruct = get(ylh);
% Get settable fields:
yfieldnames=fieldnames(rmfield(set(ylh),'Position'))';
% Remove old label:
delete(ylh)
% Create new one:
ylh = ylabel(aH,'Dummy');
% Send it bottom:
ylpos = get(ylh,'Position');
set(ylh, 'Position', [ylpos(1:2) 0]);
% Reset new ylabel to old values:
for field=yfieldnames
field = field{1};
set(ylh,field,ylstruct.(field));
end
end
This approach creates an unwanted effect, which is the ylabel will move across the figure until the mouse button is released. How can I remove this unwanted effect ?
I think the solution may be more or less as it was done in undocummented matlab solution for updating axes ticks, but now I would need the listener to the ylabel postset property. Does anyone knows how to do that? If you are a windows user, you can also try to help, all I need is to reset the position of the ylabel after a change (pan, zoom or whatever) is made on the figure.
How about explicitly setting the z-position of the y-label via it's handle? If I put this after your loop it seems to work in R2012b:
ylh = get(gca,'Ylabel')
ylpos = get(ylh,'Position');
set(ylh,'Position',[ylpos(1:2) 0]);
If I adjust the z-position I can get the y-label to pop up and even interleave between the datatips. I'm not completely sure if this is a bug or a feature, but sometimes there are workarounds to rendering issues that involve tweaking the position of an element slightly to get Matlab to recalculate and redraw the figure.
A workaround that uses both linkaxes, so useful when zooming/panning multiple plots, and the visibilty of plots.
create an axes (hax_1) with the function to be plotted, without the datatips
create an axes (hax_2) with the function to be plotted AND the datatips but without the axes labels
set hax_2 visibility to 'off' (this will plot the datatips above the first axes labels)
link the 2 axes with linkaxes([hax_1 hax_2],'xy'); (zooming and panning on one of the axes will modify on the fly the second axes)
This gives with your first code (not the edited one):
gradientStep = 1e-1;
x=-100:gradientStep:100; xSize=numel(x);
y=x.^3-x.^2;
figH=figure(42);
plot(x,y);
ylabel('YLabel (YUnits)','FontSize',16)
xlabel('XLabel (XUnits)','FontSize',16)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% modification starts
hax_1 = gca;
hax_2 = axes('Position', get(hax_1,'Position'));
lineH = plot(x,y);
linkaxes([hax_1 hax_2],'xy');
set(hax_2,'Visible', 'off');
% modification ends
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dcH=datacursormode(figH);
nTips = 20; % May change the loop for a datatip at x=-78.
for pos = round(linspace(2,xSize,nTips))
datatipH=dcH.createDatatip(lineH,struct('Position',[x(pos) y(pos)]));
orientation = 'top-left';
if pos>1
tipText{1} = 'The grandient here is: ';
tipText{2} = ['\Deltax:',sprintf('%d',x(pos)-x(pos-1)),' XUnits'];
tipText{3} = ['\Deltay:',sprintf('%d',y(pos)-y(pos-1)),' YUnits'];
else
tipText = 'Cannot calculate gradient here.';
end
bkgColor = [1 1 .5]; % May be removed.
fontSize = 12; % May be removed.
set(datatipH,'StringFcn',(#(~,~) tipText),'Orientation',orientation,'backGroundColor',bkgColor,'FontSize',fontSize,'Draggable','on'); % Only set text and orientation needed.
datatipTextBoxH=get(datatipH,'TextBoxHandle');
set(datatipTextBoxH,'HorizontalAlignment','left','VerticalAlignment','top','Margin',0.02,'Interpreter','tex','FontName','Courier','FontSize',fontSize); % May be removed.
end
I am on OSX 10.8.4, R2012b, and had the same issue as yours. Here, the proposed solution plots datatips above the axis labels and allow zooming/panning without making use of undocumented features of matlab.