Using WindowButtonDownFcn in App Designer

160 views (last 30 days)
I'm trying to access data from a line in a UIAxes in my app I'm building in App Designer, but I'm unable to set a ButtonDownFcn for a UIAxes.
How can I access the x-coordinate of a point in a line so that I can generate a new plot whose x-limits are within a specified range of that x-value?

Accepted Answer

MathWorks Support Team
MathWorks Support Team on 24 Apr 2019

While ButtonDownFcns are not supported for UIAxes, UIFigures do support WindowButtonDownFcns. Additionally, selecting UIAxes that are children of said UIFigure, as well as selecting children of that UIAxes will trigger the WindowButtonDownFcn for the UIFigure.

If you would like the WindowButtonDownFcn to only be triggered when a point in your line is selected, you can use a combination of an if statement and tags to only trigger the callback when the line is selected.

First when plotting your original line, you should add a tag that can be referenced later:

plot(app.UIAxes, app.Var1, 'Tag', 'allData');

Next, create a WindowButtonDownFcn for your UIFigure by right clicking on your UIFigure in the Component Browser, selecting "Callbacks" from the context menu, then selecting "WindowButtonDownFcn". Once this callback is created, you can use something similar to below to check if there is a CurrentObject selected (i.e. a line was selected and not a blank space in the either the UIFigure or UIAxes), and if the tag of that CurrentObject matches the tag of your line. Beginning in MATLAB R2019a, you can then use the CurrentPoint property of UIAxes to determine where within the UIAxes was selected. All together this would look like as follows:

function UIFigureWindowButtonDown(app, event)
    %Create if statement that determines if the user clicked on the
    %line of the top UIAxes. If they didn't, do nothing
    if ~isempty(event.Source.CurrentObject) && isequal(event.Source.CurrentObject.Tag,'allData')
          %Store the CurrentPiont of the UIAxes as app.a
          app.a = app.UIAxes.CurrentPoint(1);
          alpha = 200;
          %Set the XLim of the bottom UIAxes to be +/- alpha of the
          %current point
          app.UIAxes2.XLim = [(app.a-alpha) (app.a+alpha)];
          %Plot the data on the bottom UIAxes

NOTE: The CurrentPoint property of UIAxes was introduced in R2019a

David Meissner
David Meissner on 20 Jan 2022 at 1:19
I used the above advice (thanks!) and dragpoints.m from MATLAB Central to get the code below working within App Designer. My first step was to right click the *UIFigure component and select "Callbacks", then "Window Callbacks", then "Add WindowButtonDown Callback". This jumped me over to the code view with the first two lines of what is below created automatically. Used the structure from above and altered dragpoints (as shown below). I agree... transitioning to App Designer is not for the faint of heart, but now I have a UI where users can graphically manipulate data points and my app can track, display, and save any changes. (The last two features I mentioned are not shown in the code below).
% Window button down function: ProfileCreatorUIFigure
function ProfileCreatorUIFigureWindowButtonDown(app, event)
% If user clicks on line on the UIAxes
if ~isempty(event.Source.CurrentObject) && isequal(event.Source.CurrentObject.Tag, 'data')
set(ancestor(event.Source.CurrentObject,'figure'), 'windowbuttonmotionfcn', ...
{@drag_marker, event.Source.CurrentObject})
set(ancestor(event.Source.CurrentObject,'figure'), 'windowbuttonupfcn',...
function drag_marker()
% Determine which point has the shortest distance to the dragged point
coords = app.UIAxes.CurrentPoint;
x_diff = abs(app.x - coords(1,1,1));
y_diff = abs(app.y - coords(1,2,1));
[~, index] = min(x_diff + y_diff);
%create new x and y data and exchange coords for the dragged point
x2 = app.x;
y2 = app.y;
x2(index) = coords(1,1,1);
y2(index) = coords(1,2,1);
app.x = x2;
app.y = y2;
app.update_edit_fields(x2, y2);
line(app.UIAxes, app.x, app.y, ...
'LineWidth', 2,...
'Tag', 'data');
function stop_dragging(uifig, ~)

Sign in to comment.

More Answers (3)

Paramonte on 15 Oct 2019
I cannot find the "WindowButtonDownFcn" upon on doing the callback, in my case, this is a callback for a listbox ui (inherited from guide generated Ui). i am working with version R2019a.
Am I doing anything wrong?
Thanks in advance

Stephanie on 8 Nov 2019
I have been making GUI's since 2008a. So I have done a few. I am now in Matlab 2019a. I am not trying to use the App Designer which is very nice but has some large wholes in it's functionality that I really need. One of them is using the ButtonDownFcn on a line to have the program add circles where I clicked on the line and I can then use the circles to removed or fill these data points (to remove spikes from data).
I found this page in my search to find a replacement for the buttondownfcn. So I have the WindowButtonDownFcn in the UIFigure to run a callback when I click on a line. Works great until I change the x and y limits of the graph. So when I click on the line the callback adds circles to the line where I clicked and I change the x and y limits (xlim and ylim) to "zoom" in on the line and dots just created. All the lines that I did not click on are still there and I can see them. Put then I try and click on them in this "zoomed" mode nothing happens. When I look at the event.Source.CurrentObject in the callback for these attempts it is empty. But if I click on my "Unselect: button, which removes the circles and changes the x and y limts (xlim and ylim) back so you can see all the lines ("unzoomed"), then I can click on any line again.
There are some other odd behaviors when using zoom + to zoom in or the home button.
I have searched online to find some place to start to figure out what is going on. This was the only page I could find that seemed relavent. Any help would be greatly appreciated.
The two lines I use to change the x and y limits when a line it clicked on
xlim(app.SignalUIAxes,[er(1)-10 er(end)+10])
ylim(app.SignalUIAxes,[nanmin(ydata(er))-drng nanmax(ydata(er))+drng])
>> ver
MATLAB Version: (R2019a)
Operating System: Microsoft Windows 8.1 Version 6.3 (Build 9600)
Java Version: Java 1.8.0_181-b13 with Oracle Corporation Java HotSpot(TM) 64-Bit Server VM mixed mode
MATLAB Version 9.6 (R2019a)
Curve Fitting Toolbox Version 3.5.9 (R2019a)
Deep Learning Toolbox Version 12.1 (R2019a)
GUI Layout Toolbox Version 2.3.4 (R2018b)
GUI Layout Toolbox Version 2.3 (R2016b)
Image Processing Toolbox Version 10.4 (R2019a)
Mapping Toolbox Version 4.8 (R2019a)
Partial Differential Equation Toolbox Version 3.2 (R2019a)
Signal Processing Toolbox Version 8.2 (R2019a)
Statistics and Machine Learning Toolbox Version 11.5 (R2019a)
Symbolic Math Toolbox Version 8.3 (R2019a)

Will Grant
Will Grant on 28 May 2020
Edited: Will Grant on 28 May 2020
The modern way of handling this behavior is to set an event handler on the line/surf/etc plot objects themselves.
This gets around messing with limits, testing which object fired, etc.
plot([1 2], [1 2], 'ButtonDownFcn', @(o, e) clickHandler(o, e));
p = plot([1 2], [1 2]);
p.ButtonDownFcn = @(o, e) clickHandler(o, e);
function clickHandler(o, e)
  1 Comment
David Young
David Young on 6 Jul 2020
The problem with this is that you have to click on the line itself. That can be quite tricky if you have, for example, a waveform that is very compressed on the time axis - which might well be the case if what you want to do is to generate an expanded view of a section. If you could click anywhere in the axes, it would be easier for the user, and just as simple for the programmer. If this is more modern, please give me the old-fashioned ButtonDownFcn callback in uiaxes!

Sign in to comment.




Community Treasure Hunt

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

Start Hunting!

Translated by