I want a graphical way to select/deselect points from a curve.

16 views (last 30 days)
I am processing data comprising 21 x,y pairs. Lots and lots of sets of 21 x,y pairs. (and not always 21 points) Occasionally one or two of these points are bad and I need to eliminate them before subsequent processing. I want to create a function I can drop into my processing routine and call it like this:
ii = downselect(x,y);
where x and y are vectors of my input data, and ii is a vector of indices of the points I wind up keeping. (After the call I can use ii to downselect the data and continue processing.) Inside the function I want to to plot the data and present a series of toggle buttons, one for each point, and allow the user to toggle eachpoint on or off. And then when the user is satisfied, hit another button that passes the preserved indices to the output and exits.
Callbacks are sort of a mystery to me. I've got something sort of half working, but it seems to use the root workspace rather than the workspace inside the function, so the callbacks can't find the data. I'd like everything to be self-contained so I only insert the above line in my processing routine. Any help would be much appreciated. When I have so far follows, but it doesn't work:
And is there maybe a toggle button that is an array, rather than creating 21 separate toggle buttons?
Thanks.
Call it like this:
x = [1:5];y = rand(1,5);ii = downselect(x,y);
And downslect.m contains the following:
function ii = downselect(x,y);
plotdata(x,y);
for i = 1:length(x);
l = 20*i+60;
t = 20;
w = 20;
h = 20;
hh(i) = uicontrol('Style', 'togglebutton', ...
'String', sprintf('%d',i), ...
'Position', [l,t,w,h], ...
'Value',1, ...
'Callback','ii = plotdata(x,y,hh);');
end
hx = uicontrol('Style', 'togglebutton', ...
'String', 'X', ...
'Position', [20,20,20,20], ...
'Value',0, ...
'Callback',' ');
while get(hx,'Value') == 0;
pause(0.1);
end;
%**************************************************************************
function ii = plotdata(x,y,hh)
if nargin < 3;hh = [];end;
if isempty(hh);
ii = [1:length(x)];
else;
ii = [];
for i = 1:length(hh);
if get(hh(i),'Value') == 1;
ii(end+1) = i;
end;
end;
end;
plot(x(ii),y(ii),'ko','MarkerFaceColor','k');
grid on;

Answers (2)

John D'Errico
John D'Errico on 21 Jan 2023
Edited: John D'Errico on 21 Jan 2023
I posted this code on the file exchange many years ago
It is dsigned to do exactly what you want, and it gives you multiple ways to perform the selection.
  5 Comments
Walter Roberson
Walter Roberson on 23 Jan 2023
There are almost always better alternatives than using global
Or store into properties of your app if you are using App Designer (which I can tell you are not doing.)
John Feiereisen
John Feiereisen on 23 Jan 2023
I figured it out. There's a UserData field in the figure. I can use this field to make the data available wherever I need it as long as the figure remains open. I create the figure, create the buttons, write the data and button handles to the UserData field. The callback gets everything from the UserData field in the figure. It works exactly how I wanted, but it's not completely self-contained. Since the callback function executes in the base workspace I can't have it as an embedded function. But it works.
Thank you to you and John!

Sign in to comment.


Walter Roberson
Walter Roberson on 22 Jan 2023
'Callback','ii = plotdata(x,y,hh);'
When you specify a callback as a character vector, the callback is always executed in the context of the base workspace.
Remember that when you configure a callback, you are telling the UI which function or code to execute at some future time when the UI detects events. The callback gets attached to the graphics object. There is no way to configure a "workspace" to be attached to the callback function or script, for variables to be resolved within the context of that workspace.
A more common flow would be to initialize the controls in one function (for example, the function that builds the GUI), and then to want to get the results of using the controls in a different function. It would be clear that in such a case that the initialization function is long gone and no longer exists for its variables to be accessed or stored into.
You do not use that flow, so it certainly might feel as if the callbacks "ought" to be able to access the variables in the function that constructed the controls and which is not yet executed... but notice there is no way to specify "operate this callback within the current workspace" as opposed to "operate this callback after the current function is gone". If it were possible to operate in the current workspace you would expect that there would have to be an explicit option saying which workspace was to be used, 'base' or 'caller' (or something like that). And since there is no option, we deduce that callbacks do not have that flexibility and are designed only to handle more common situation where the creation workspace is no longer available.

Categories

Find more on Interactive Control and Callbacks in Help Center and File Exchange

Products


Release

R2017b

Community Treasure Hunt

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

Start Hunting!