How can I implement the deprecated full crosshair pointer functionality in MATLAB?
Asked Answered
G

1

5

I'm trying to replace the pointer on a chart plot with a full crosshair (that is, a set of 2 perpendicular lines that stretch to the edges of the plot vertically and horizontally and follow the mouse cursor). Years ago, I was able to accomplish this with this line of code:

set(gcf,'Pointer','fullcross')

However, when I try to run this line now, I get the following message:

Warning: Full crosshair pointer is no longer supported. A crosshair pointer will be used instead.

I would really like to find an alternative way to implement this functionality, but have been unable to thus far. I have come across the following function: MYGINPUT, but it does not seem to accomplish what I'm looking for. Does anyone have any suggestions?

Grefe answered 15/11, 2017 at 22:4 Comment(0)
P
8

You can actually do this by adding a 'WindowButtonMotionFcn' to your figure (assuming nothing else is using it) that will display crosshair lines in your axes when your mouse is over it. Here's a function that creates such functionality for all axes in a figure:

function full_crosshair(hFigure)

  % Find axes children:
  hAxes = findall(hFigure, 'Type', 'axes');

  % Get all axes limits:
  xLimits = get(hAxes, 'XLim');
  xLimits = vertcat(xLimits{:});
  yLimits = get(hAxes, 'YLim');
  yLimits = vertcat(yLimits{:});

  % Create lines (not displayed yet due to NaNs) and listeners:
  for iAxes = 1:numel(hAxes)
    hHoriz(iAxes) = line(xLimits(iAxes, :), nan(1, 2), 'Parent', hAxes(iAxes));
    hVert(iAxes) = line(nan(1, 2), yLimits(iAxes, :), 'Parent', hAxes(iAxes));
    listenObj(iAxes) = addlistener(hAxes(iAxes), {'XLim', 'YLim'}, ...
                                   'PostSet', @(~, ~) update_limits(iAxes));
  end

  % Set callback on the axes parent to the nested function below:
  set(hFigure, 'WindowButtonMotionFcn', @show_lines);

  function update_limits(axesIndex)
    xLimits(axesIndex, :) = get(hAxes(axesIndex), 'XLim');
    yLimits(axesIndex, :) = get(hAxes(axesIndex), 'YLim');
    set(hHoriz(axesIndex), 'XData', xLimits(axesIndex, :));
    set(hVert(axesIndex), 'YData', yLimits(axesIndex, :));
  end

  function show_lines(~, ~)

    % Get current cursor positions in axes:
    cursorPos = get(hAxes, 'CurrentPoint');
    cursorPos = vertcat(cursorPos{:});
    cursorPos = cursorPos(1:2:end, 1:2);

    % Determine if the cursor is within an axes:
    inAxes = (cursorPos(:, 1) >= xLimits(:, 1)) & ...
             (cursorPos(:, 1) <= xLimits(:, 2)) & ...
             (cursorPos(:, 2) >= yLimits(:, 1)) & ...
             (cursorPos(:, 2) <= yLimits(:, 2));

    % Update lines and cursor:
    if any(inAxes)  % Cursor within an axes
      set(hFigure, 'Pointer', 'custom', 'PointerShapeCData', nan(16));
      set(hHoriz(inAxes), {'YData'}, num2cell(cursorPos(inAxes, 2)*[1 1], 2));
      set(hVert(inAxes), {'XData'}, num2cell(cursorPos(inAxes, 1)*[1 1], 2));
      set(hHoriz(~inAxes), 'YData', nan(1, 2));
      set(hVert(~inAxes), 'XData', nan(1, 2));
    else  % Cursor outside axes
      set(hFigure, 'Pointer', 'arrow');
      set(hHoriz, 'YData', nan(1, 2));
      set(hVert, 'XData', nan(1, 2));
    end

  end

end

If you do the following:

full_crosshair(gcf);

Then as you move the cursor over each axes in your figure the cursor will disappear and you will see two lines appear and track the mouse position. If any of the axes limits change, then the event listeners in the above code will detect and account for it. If axes are added to or removed from the figure, you will need to call full_crosshair again to update 'WindowButtonMotionFcn' accordingly.

Finally, you can turn it off by just clearing 'WindowButtonMotionFcn':

set(gcf, 'WindowButtonMotionFcn', []);
Psychologism answered 15/11, 2017 at 22:31 Comment(6)
First of all, thank you for taking the time to answer! I'm pretty lost as to how to actually implement this. Please see the edit I made to my original question for the code I tried (the edit should be up in a few minutes). Thanks again!Grefe
thank you gnovice! I got your code to work by changing two lines (please see EDIT 2 in my original post). As a follow-up, if I have multiple sub plots, is it possible to make this work on more than one subplot at a time? The original set(gcf,'Pointer','fullcross') implementation would work on the entire figure (rather than individual plots). Is there a way to do something similar with this?Grefe
@Alarik: I'm not sure why you were getting that first error. The line function should allow you to pass the desired parent axes as the first argument. You could try ... = line(..., ..., 'Parent', hAxes); and see if that works. Making the above work for all axes in the figure will take a little tinkering, but I'll add an update when I can.Psychologism
@Alarik: The new version above will both add the full crosshair functionality to all axes in the figure, as well as account for changes in the axes limits.Psychologism
Thank you, this works great! I'm not sure if I should start a new question thread regarding this, but is it possible to have the crosshair appear at all times? In other words, as long as my cursor is anywhere within the bounds of the figure window, the perpendicular lines appear and stretch to the edges of the figure itself, rather than to any individual plot. If this warrants a new question, please let me know (I don't want to break the rules or something by asking too many similar questions).Grefe
@Alarik: I think this perhaps requires a new question. It will require a slightly different approach, and will depend on a few things... Do you want the crosshairs to stay visible when the cursor is outside the figure? If so, do you want the crosshairs to keep updating position when the cursor is moved outside the figure?Psychologism

© 2022 - 2024 — McMap. All rights reserved.