How do i select a point on matlab UIaxes and then get data for it?

I have a plot on UIAxes and i want to use the brush tool to select the already plotted data and get what points the user selects

 Réponse acceptée

Adam Danz
Adam Danz le 17 Fév 2020
Modifié(e) : Adam Danz le 28 Sep 2020
The brush tool is not (yet?) available on UIAxes as of r2019b; neither is rbbox. Update: the brush tool is now available as of r2020a.
Here's a workaround for releases <= r2019b.
The first block of code simulates an App by creating a plot on a UIAxes within a UIFigure.
The second block of code is a function that can be added to your App. When the function is called, the title of the axes will change to instructions for the user to draw a rectangle with their mouse. After drawing the rectangle, it will disappear and the (x,y) coordinates within or on the border of the rectangle will be returned.
See inline comments for details.
% Create a demo-app
app.myApp = uifigure();
app.UIAxes = uiaxes(app.myApp);
plot(app.UIAxes, rand(10,4),'o')
title(app.UIAxes, 'My Data')
% Call the custom function so the user can
% draw a temporary rectangle within the axes.
[x,y] = selectDatapoints(app.UIAxes);
function [x,y] = selectDatapoints(ax)
% Remove any pre-existing rectanlges (if any)
delete(findall(ax, 'Type', 'images.roi.Rectangle'))
% Get coordinates of all data points in the axes
xyobj = findall(ax.Children, '-Property','xData');
xydata = get(xyobj, {'XData','YData'});
xydataMat = cell2mat(xydata');
% change title of axes to instructions, in red
originalTitle = get(ax.Title, {'String', 'Color'});
set(ax.Title, 'String', 'Draw rectangle around desired datapoints', 'Color', 'r')
% allow user to draw rectangle; see more options:
% https://www.mathworks.com/help/images/ref/drawrectangle.html
pan(ax, 'off') %turn off panning so the interaction doesn't drag the data.
roi = drawrectangle(ax);
% quit if figure is closed
if ~isvalid(roi)
x = [];
y = [];
return
end
% Return original title
set(ax.Title, 'String', originalTitle{1}, 'Color', originalTitle{2})
% determine which coordinates are within the ROI
isIn = xydataMat(1,:) >= roi.Position(1) & xydataMat(1,:) <= sum(roi.Position([1,3])) ...
& xydataMat(2,:) >= roi.Position(2) & xydataMat(2,:) <= sum(roi.Position([2,4]));
% Delete ROI
delete(roi)
% Return outputs
x = xydataMat(1,isIn);
y = xydataMat(2,isIn);
end

22 commentaires

this is a great solution but i am getting some errors
Error using images.roi.internal.ROI/parseInputs
ROI parent must be a valid Axes object.
Error in images.roi.Rectangle
Error in drawrectangle (line 188)
h = images.roi.Rectangle(varargin{:});
"ROI parent must be a valid Axes object."
In my example, the axes handle is app.UIAxes.
[x,y] = selectDatapoints(app.UIAxes);
That handle is passed into the selectDatapoint() function where the variable name for the axis handle becomes ax.
roi = drawrectangle(ax);
Therefore, ax is a valid axis handle in my example.
My guess is that you're not passing the correct axis handle into the function (assuming you haven't made any changes to the function).
My axes handle is app.UIAxes also
I created the axes by drag and drop the axes on the figure
and I dont want to programmatically code the axes
I have already created the app with the axis
How do I use this axis handle?
Adam Danz
Adam Danz le 18 Fév 2020
Modifié(e) : Adam Danz le 18 Fév 2020
That sounds correct. You shouldn't be adding the axes within the code; I did that just for the example.
Please share you code so I can see what's going on. If you didn't change anything in the selectDatapoints function, I just need to see the code that calls this function. If you made changes in selectDatapoints then I'd need to see that, too.
Also, what matlab release are you using?
I am using 2019 b
and instead of a using a function i just copy pasted the body of the function into my callback method so whenever i click on that button i should be able to draw a box .
Here is my code:
% Value changed function: OPENGRAPHINNEWWINDOWButton
function OPENGRAPHINNEWWINDOWButtonValueChanged(app, event)
value = app.OPENGRAPHINNEWWINDOWButton.Value;
ax=app.UIAxes
app.MANPOWERPLANNINGTOOLUIFigure
delete(findall(ax, 'Type', 'images.roi.Rectangle'))
% Get coordinates of all data points in the axes
xyobj = findall(ax.Children, '-Property','xData')
xydata = get(xyobj, {'XData','YData'})
xydataMat = cell2mat(xydata')
% change title of axes to instructions, in red
originalTitle = get(ax.Title, {'String', 'Color'})
set(ax.Title, 'String', 'Draw rectangle around desired datapoints', 'Color', 'r')
% allow user to draw rectangle; see more options:
% https://www.mathworks.com/help/images/ref/drawrectangle.html
roi = drawrectangle(ax);
% quit if figure is closed
if ~isvalid(roi)
x = [];
y = [];
return
end
% Return original title
set(ax.Title, 'String', originalTitle{1}, 'Color', originalTitle{2})
% determine which coordinates are within the ROI
isIn = xydataMat(1,:) >= roi.Position(1) & xydataMat(1,:) <= sum(roi.Position([1,3])) ...
& xydataMat(2,:) >= roi.Position(2) & xydataMat(2,:) <= sum(roi.Position([2,4]));
% Delete ROI
delete(roi)
% Return outputs
x = xydataMat(1,isIn)
y = xydataMat(2,isIn)
Adam Danz
Adam Danz le 18 Fév 2020
Modifié(e) : Adam Danz le 18 Fév 2020
"I am using 2019 b"
Good, because ROI objects were not supported on UIAxes in r2019a.
I just tested this code in r2019a and I got the same error message as you received. "ROI parent must be a valid Axes object." But the same exact code from my answer works in r2019b. So just be sure you're not using a release prior to r2019b. Note that if the ax did not point to a valid axis handle, you would get errors much earlier in the function such as when you call ax.Children or ax.Title.
I also just embedded the function from my answer into a test-app and adapted it to the app. It runs without problems.
Please double-check that you're using r2019b and that app.UIAxes is the exact axis handle. If the problem persists, please attach the app with instructions on how to recreate the problem.
I still could not figure it out ,
I cannot send the code
but is there anyother approach like if we select the datatip we get its information
I double checked everything
There's no reason this shouldn't work in r2019b.
Could you supply the entire copy-pasted error message?
Could you execute this line just prior to receiving the error and show me what it returns?
class(ax) % <----
roi = drawrectangle(ax);
the code runs now but now when i try to draw the rectangle the whole graph moves
Adam Danz
Adam Danz le 18 Fév 2020
Modifié(e) : Adam Danz le 18 Fév 2020
Sounds like progress!
Here's two things to try.
  1. Make sure the panning tool is not selected (see image below)
  2. Turn off panning from within the callback function using the line below.
pan(app.UIAxes, 'off')
I'll add the 2nd suggestion to the answer since it's pretty important.
Adam Danz
Adam Danz le 18 Fév 2020
Modifié(e) : Adam Danz le 18 Fév 2020
Regarding the use of datatips (which you mentioned), that's a good option if you only want to access the coordinates from one data point at a time and only need to see the coordinates.
thanks a lot for helping me
so i tried to use
pan(app.UIAxes, 'off')
and I still was able to drag the graph
How to avoid this?
I don't have this problem on my end so I'm just taking shots in the dark here.
Here are some things to try.
  1. If you're using my function, just before drawrectangle() is called, the title should turn red which indicates that you can draw the rectangle without the axis limits moving around. If you click-and-drag before or after this window of time, the axis limits will be affected.
  2. Try disabling and enabling all interactivity prior to / after calling drawrectangle() (see below
disableDefaultInteractivity(app.UIAxes)
drawrectangle(...)
enableDefaultInteractivity(app.UIAxes)
I tried using disable command and when i am about to draw the rectangle the whole graph moves
I am trying to go for a different approach where i can get the current point of the mouse when i click near a datatip
Any suggestions?
Thank you so much for your help
i checked the link you sent for datatips
Do i need to wait for button press and then copy the code in that link?
I just need to check one point at a time
Data tips should be enabled by default in AppDesigner UIAxes in r2019b.
If they are not enabled, try running enableDefaultInteractivity(app.UIAxes) (although I'm not sure if that is supported by UIAxes yet).
If you want the store the coordinates of the datatips you can use the function In the "update" section of this answer.
The idea is to add a button or some event that triggers this function. The function does the following.
  1. Removes any pre-existing datatips in the UIAxes
  2. waits for a datatip to appear after the user hovers over a data point
  3. displays the coordinates of the datatip
The changes you might need to make are
  1. If you want to get data from >1 datatip at a time, you could alter the while-loop to either count the number of expected datatips or add a stop-button that triggers the end of the while-loop.
  2. Depending on how dense your data are, you may want to remove the datatip after it's coordinates are stored so you don't end up with a bunch of datatips cluttering the axes.
  3. you'll probably want to store the coordinates rather than display them.
Error in
appdesigner.internal.service.AppManagementService/tryCallback (line
333)
callback(app, event);
Error in
matlab.apps.AppBase>@(source,event)tryCallback(appdesigner.internal.service.AppManagementService.instance(),app,callback,requiresEventData,event)
(line 35)
newCallback = @(source,
event)tryCallback(appdesigner.internal.service.AppManagementService.instance(),
...
>>
it says my tth is an empty array when i run the first line of code
As you can see in the comments following that answer, the solution worked for at least two people using r2019b (including me). If you're having trouble implementing the solution I wonder what troubleshooting you've already done to try to get it working.
"it says my tth is an empty array when i run the first line of code"
That's fine and is not a problem. That just means there were no pre-existing datatips.
It is a nice solution. The pan function call should have ax and not app.UIAxes. IT IS IMPORTANT TO NOT THAT THIS SOLUTION REQUIRES THE IMAGE PROCESSING TOOBOX.
Thanks, Peter. I corrected the pan(app.UIAxes...) error.

Connectez-vous pour commenter.

Plus de réponses (1)

Looks like there's something easier to do in 2020a. May want to try it out in 2019a or 2019b as I did not see it in the current documentation, but it seems to work fine for me.
%app.UIAxes will be our handle
x = 1:10; %some values for x
y = x*2; %some values for y
plot(app.UIAxes,x,y) %plot in the UIAxes
indx = app.UIAxes.Children(end).BrushData; %grab the indices

2 commentaires

This can be activated by selecting the brush icon that appears in the tools menu when hovering over the axes. The BrushData referenced in your answer only returns the index of selected items of a single object after using the brush tool.
How do we get thos values back in command window?
bcz indx returns empty when called in command window.
how do i get those accurate values if i neeed to display them in some other window?

Connectez-vous pour commenter.

Catégories

En savoir plus sur Interactive Control and Callbacks dans Centre d'aide et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by