Search code examples
matlabmatlab-guidematlab-gui

How to use "ButtonDownFcn" in a populated GUI axes?


I have a very simple GUI made in guide where i have a plot function initiated by a pushbutton which plots a scatter plot in axes (called Method1axes1):

handles.plot = scatter(X,Y, 'parent', handles.Method1axes1);

Now I want the user to be able to click the axes (plot) to get en new larger figure. I tried the below code which is working if i DON'T plot in the axes first. As soon as I run the plot function the scatterplot appears in Method1axes1 but I can no longer click the figure.

% --- Executes on mouse press over axes background.
function Method1axes1_ButtonDownFcn(hObject, eventdata, handles)
figure
scatter(X,Y);

What am I doing wrong?


Solution

  • This is a kind of special case for MATLAB, and it is not extremely well documented.

    There are 2 things you need to consider:

    • 1) The most obvious part. When you plot something in your axes, the plot is on the foreground. So when you click on your axes, the top plot intercept that click and tries to process it. You need to disable the mouse click capture from the plot/scatter/image objects you have in your axes. For that, you have to set the HitTest property of your scatter object to 'off'. (recent MATLAB version have changed the name of this property, it is now called PickableParts).

    • 2) Much less obvious and documented. It used to be in the doc for the axes ButtonDownFcn callback but it is not explained anymore (although the behaviour persist). This is what I could find on old forums:

    When you call PLOT, if the axes NextPlot property is set to 'replace' (which it is by default) most of the properties of the axes (including ButtonDownFcn) are reset to their default values.

    Change the axes NextPlot property to 'replacechildren' to avoid this, or set the ButtonDownFcn after calling PLOT, or use the low-level LINE function instead of the higher-level PLOT function.

    This is also discussed and explained here: Why does the ButtonDownFcn callback of my axes object stop working after plotting something?


    For your case, I tried set(axe_handle,'NextPlot','replacechildren') and it works ok to let the mouse click reach the ButtonDownFcn, but unfortunately it creates havoc with the axes limits and LimitModes ... so I opted for the second solution, which is to redefine the callback for ButtonDownFcn after every plot in the axes.


    So in summary, your code for the pushbutton1_Callback should be:

    function pushbutton1_Callback(hObject, eventdata, handles)
        % Whatever stuff you do before plotting
        % ...
        % Plot your data
        handles.plot = scatter(X,Y, 'parent', handles.Method1axes1);
        % Disable mouse click events for the "scatterplot" object
        set(handles.plot,'HitTest','off') ;
        % re-set the "ButtonDownFcn" callback
        set(handles.Method1axes1,'ButtonDownFcn',@(s,e) Method1axes1_ButtonDownFcn(s,e,handles) )
    

    And for your axes mouse click event, you might as well keep the handle of the new generated objects:

    function Method1axes1_ButtonDownFcn(hObject, eventdata, handles)
        handles.newfig    = figure ;
        handles.axes1copy = copyobj( handles.Method1axes1 , handles.newfig ) ;
    

    Note that instead of plotting a new set, I simply use the copyobj function, very handy when you need to reproduce a plot.


    Illustration:

    enter image description here