Why does the ButtonDownFcn callback of my axes obj

2020-04-11 07:57发布

问题:

I'm creating a set of axes in a figure and assigning a callback for its 'ButtonDownFcn' property like so:

HRaxes = axes('Parent', Figure, 'Position', [.05 .60 .9 .35],...
              'XLimMode', 'manual', 'ButtonDownFcn', @HR_ButtonDown);

Where the callback function is defined as such:

function HR_ButtonDown(hObject, eventData)
  %# Do some stuff here when the axes is clicked on...
end

The callback works fine until I plot something on the axes like so:

plot(HRaxes, data.HR_X, data.HR_Y, 'b');

After which the callback doesn't trigger anymore when I click on the axes. What's going wrong and how can I fix it?

回答1:

The main problem here is that the function PLOT is a high-level plotting function, meaning that it adds objects to the plot and will modify existing plot settings. If you look at the 'NextPlot' property for axes objects, you will see that it has three settings that determine how high-level plotting functions can affect the axes object:

  • add — Use the existing axes to draw graphics objects.

  • replace — Reset all axes properties except Position to their defaults and delete all axes children before displaying graphics (equivalent to cla reset).

  • replacechildren — Remove all child objects, but do not reset axes properties (equivalent to cla).

Since 'replace' is the default setting, the handle you set for the 'ButtonDownFcn' callback gets reset to nothing when you call PLOT, thus turning off the button-click behavior. There are two ways you can avoid this:

  • Setting the 'NextPlot' property of the axes to either 'add' (to add to the existing plot objects) or 'replacechildren' (to replace the existing plot objects but keep the current axes property settings) before you make your call to PLOT.

    HRaxes = axes('Parent', Figure, 'Position', [.05 .60 .9 .35],...
                  'XLimMode', 'manual', 'ButtonDownFcn', @HR_ButtonDown,...
                  'NextPlot', 'add');
    plot(HRaxes, data.HR_X, data.HR_Y, 'b');
    
  • Using a lower-level plotting routine (such as LINE) that doesn't modify existing plot properties:

    HRaxes = axes('Parent', Figure, 'Position', [.05 .60 .9 .35],...
                  'XLimMode', 'manual', 'ButtonDownFcn', @HR_ButtonDown);
    line(data.HR_X, data.HR_Y, 'Parent', HRaxes, 'Color', 'b');
    


回答2:

@David Snyder, observe that an image object can have a ButtonDownFcn callback property as well. Then in your callback you can have access to the corresponding axes property throught Parent property or ancestor function. For instance, say you want to use in your ButtonDownFcn callback the position of the pixel and the button you clicked with. When you plot the image, use

imh = image(some_image);
set(imh,'ButtonDownFcn',@position_and_button);

where you defined your callback somewhere else

function position_and_button(hObject,eventdata)
   Position = get( ancestor(hObject,'axes'), 'CurrentPoint' );
   Button = get( ancestor(hObject,'figure'), 'SelectionType' );
   %# do stuff with Position and Button