Can I use arrows to move Data Tips on scatter plots?

I know that when using the plot function with data tips you can use the arrow keys to move the data tip through the data set. Is there a way to do this with scatter plots as well? I need to be able to define the marker color individually, which is why I'm using scatter instead of plot.

 Accepted Answer

Hi David,
Edit: I have updated the code and it works. It's better if the callback is on KeyPressFcn, especially for plots with many points.
I have taken part of code from Adam's answer, thanks Adam!
% clear functions % On the second run, datatip will continue from where it stopped the previous run, if you mind that, uncomment this line
fig = figure('KeyPressFcn', @ArrowKeyPressed);
ax = axes(fig);
x = linspace(0,2*pi,25);
y = cos(x) + rand(1,numel(x))/2;
sz = (abs(sin(x))*4+1)*25;
c = linspace(1,10,numel(x));
scatter(ax, x,y,sz,c,'filled','MarkerEdgeColor', 'k');
grid on
It is possible to write a callback for the figure that would detect arrow key presses
function ArrowKeyPressed(Source, Event)
persistent dtNum
if isempty(dtNum) % Initialise the variable
dtNum = 0;
end
% Get axes handle
ax = findall(Source, 'Type', 'axes');
% Get the scatter object
h = findall(ax, 'Type', 'scatter');
% Get the datatips associated with scatter object
datatipObjs = findobj(h,'Tag','datatip');
% Get the number of points
numPoints = length(h.XData); % assuming x is a vector
% Setting up the counter
switch Event.Key
case 'leftarrow'
if dtNum > 1
dtNum = dtNum - 1;
else
dtNum = 1;
end
case 'rightarrow'
if ~isequal(dtNum, numPoints)
dtNum = dtNum + 1;
end
otherwise
return
end
% Removing the previous (all if there are more) and creating the next datatip
if isvalid(datatipObjs); delete(datatipObjs); end
datatip(h, h.XData(dtNum), h.YData(dtNum));
end

6 Comments

Neat idea, Mario. I played around with it briefly and assigned some additional inputs to the KeyReleaseFcn. x is the x-data and h is the handle to the scatter object.
fig.KeyReleaseFcn = {@ArrowKeyPressed,x,h};
function ArrowKeyPressed(Source, Event,x,h)
That eliminates the need to use findall(). Another alternative: ax = gca(Source);
It also eliminates the assumption that the scatter object is the first listed child of the axes.
ax.Children(1).Children(:)
% turns into
h.Children(:)
so you no longer need an axis handle at all.
Actually, the best way to get the datatip handles associated with the scatter object is,
datatipObjs = findobj(h,'Tag','datatip'); % h is scatter handle
So the bottom part can be reduced to
delete(findobj(h,'Tag','datatip'))
Lastly, the rightward key moved the datatip leftward.
What if a different key is pressed or if there aren't any datatips or more than 1 data tip? What if the next x-value is NaN or -inf? Haven't tested it.
This is was actually my first idea to try, but I found that when datacursormode was "on", the key presses were not being captured by my KeyPressFcn. Have you encountered this, or do you know a way around it?
That's true, callbacks won't be ran if any of the toolbar buttons are on. I can only find this question related to mentioned issue. Actually, you don't need to have active datacursormode to create datatips.
Regarding the datatips with NaN values for XData - datatip won't show any error, but the datatip will be generated for the first point. I can submit this as a bug.
Thanks, that's great! Here's the Undocumented Matlab article that provides a workaround: https://undocumentedmatlab.com/articles/enabling-user-callbacks-during-zoom-pan. Any idea how to call the default mode's callback so as to not accidentally break the normal functionality? I haven't actually tried it yet, so it may just do it automatically.
Wrt data points that are not finite, the default functionality for plot is to just jump over these values. It wouldn't be too difficult to put in a check to account for that and increment to the next point if need be.
That callback will be active only on the figure that has ArrowKeyPressed callback specified, other figures will have the normal functionality.
Also, on the normal plots, arrow key datatips work only when datacursor mode is on. To have the same functionality for scatter plots, I guess one has to find information on Undocumented MATLAB.
What I ment on my comment with NaN values, if you use
datatip(h, h.XData(dtNum), h.YData(dtNum))
on point with NaN on XData, it will generate datatip on the first point. I guess this is not working as intended, datatip shouldn't be generated at all.

Sign in to comment.

More Answers (1)

It appears that keyboard control of DataTips is not supported in scatter().
However, a less-than-perfect workaround is to plot very small (nearly invisible) points on top of the scatter markers using plot() which will support keyboard control of DataTips.
Demo:
% rng('default') % for reproducibility
x = linspace(0,2*pi,25);
y = cos(x) + rand(1,numel(x))/2;
sz = (abs(sin(x))*4+1)*25;
c = linspace(1,10,numel(x));
scatter(x,y,sz,c,'filled','MarkerEdgeColor', 'k')
grid on
% plot() markers must go on top; My tests indicate that marker size
% smaller than 0.5 won't work. Setting Marker style to "none" also
% does not work.
hold on
plot(x,y,'ko','MarkerSize',0.5)

Categories

Products

Community Treasure Hunt

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

Start Hunting!