How do I exactly pass function handles for MATLAB callbacks?

I'm getting problems trying to use the ButtonDownFcn in a MATLAB plot. The documentation seems to be a bit contradictive in this sense, so it's hard to understand exactly what I should do. What I want to do is assign a function to lines in a plot. When the user clicks on any given line with that "ButtonDownFcn" callback function assigned, my program is meant to take both the line object and a variable called "selectionObj", copy the line object into "selectionObj" (which is a class specifically written for that), and return the new version of "selectionObj" so the user can keep selecting more lines and compose a selection set. My problem, specifically, is with passing inputs and outputs to this Handle correctly, as I'm getting insecure about how I should work with the syntax in this case.
I will attach an MWE (Minimal Working Example) below so you can reproduce the same problem I am having. My problem specifically is with this line:
set(lineInPlot{i}, 'ButtonDownFcn', @(linehandle)mouseLineSelectMWE(linehandle), 'PickableParts', 'all')
Specially at
@(linehandle)mouseLineSelectMWE(linehandle)
MATLAB documentation says that, in general, I can work with handles in the form of
hand = @(s) s.^2 + 1;
result = hand(2); % Which produces result = 5.
However, I have no means to specify a left-hand argument here in this case. If you would think of a language like C and its derivatives, usually that would mean that you have to pass pointers into the function. MATLAB's way of doing that is specifying "global" variables that can be called literally anywhere and will point to the same place in memory. Therefore, in the MWE, selectionObj is global and called in the mouseLineSelectMWE as a global.
Also, going back to the snippet @(linehandle)mouseLineSelectMWE(linehandle)", I've seen people working with these handles as @(~,~)function, @function(aRandomVariableHere), and so on and so forth. I'm not so sure, then, how exactly I will specify inputs and outputs here, especially when I need to pass that exact line into the code.
So, in the end of the day, all I really need a fix on is the (~,~)fun() part, which I'm not really understanding how it works.
MWE:
close all % Clear current plots
clear all
global selectionObj
% selectionObj = BCselectionMWE; % Creation
selectionObj = BCselection; % Creation
%% Starting the BC selection figure
fig1 = figure(1);
ax1 = axes;
hold on
% Plotting lines
for i = 1:4
lineInPlot{i} = plot3(...
rand(2,1), ...
rand(2,1), ...
rand(2,1),...
'Color',[.5 .5 .5],...
'LineWidth',1.2...
);
linehandle = lineInPlot{i}; % Perhaps this is unnecessary
set(lineInPlot{i}, 'ButtonDownFcn', @(linehandle)mouseLineSelectMWE(linehandle), 'PickableParts', 'all')
end
% Figure formatting
grid on
view([50 25]) % Initial POV ([45 30] is also good)
daspect([1 1 1]) % Constraints the data aspect ratio. The geometry won't
% be distorted as you pan around.
%% Class
%
% classdef BCselectionMWE
% %BCSELECTION Summary of this class goes here
% % Detailed explanation goes here
%
% properties (SetAccess = private)
% % Every clicked line will be, first, placed in 'kids'
% kids % ...'kids' because all the 'children' lines will be here
% end
%
% methods
% function obj = BCselection
% %BCSELECTION Construct an instance of this class
% % This class creates objects that make easier the process of
% % organising the selected child lines, their created patches,
% % and the different BC groups created
%
% % We want all properties being created as empty cell arrays.
% kids = cell(0,0);
% end
%
% function obj = addLine(obj,lineChildren)
% %ADDLINE This method adds a line to the current selection
%
% % Doing the stuff necessary if this is a line being added to
% % the selection
% % Highlighting in blue the selected line
% lineChildren.Color(3) = 1;
% % Adding the kid to the kindergarten
% obj.kids{n+1} = lineChildren;
% end
% end
% end
%% Function for callbacks
function selectionObj = mouseLineSelectMWE(lineObj,~)
%MOUSELINESELECT Passes the line object clicked to the addline command of
%the BCselection class. Important since the output can be redirected to the
%class, and not the line object itself.
% THE GLOBAL VARIABLE selectionObj MUST EXIST! selectionObj is an instance
% of the BCselection class
global selectionObj
% Adds (or removes) the line
selectionObj = selectionObj.addLine(lineObj);
% selectionObj.addLine(lineObj);
% This is necessary since the output of the class function must still point
% to the selectionObj variable
end
Current error messages are:
*Error using lineSelectionMWE>@(linehandle)mouseLineSelectMWE(linehandle)
Too many input arguments.
Error while evaluating Line ButtonDownFcn.*
EDIT:
Funny how things work exactly when you give up and ask the community. I did this and it worked exactly like I wanted, but why?
(~,~)mouseLineSelect(linehandle)

3 件のコメント

dpb
dpb 2019 年 7 月 13 日
Show in context where and how you used it...
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 13 日
@dbp
The minimum working example is already enough to reproduce the problem exactly as I have it. Don't forget to copy paste the classdef section to another file.
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 13 日
Also,
this line
set(lineInPlot{i}, 'ButtonDownFcn', @(linehandle)mouseLineSelectMWE(linehandle), 'PickableParts', 'all')
became
set(lineInPlot{i}, 'ButtonDownFcn', @(~,~)mouseLineSelect(linehandle), 'PickableParts', 'all')
in order to properly work.

サインインしてコメントする。

 採用された回答

Walter Roberson
Walter Roberson 2019 年 7 月 14 日

1 投票

MATLAB automatically passes callbacks handle of the current object, and event data (typically empty or a struct, sometimes an object). You were allowing for the object handle but not for the event data.

11 件のコメント

Alexandre Piccini
Alexandre Piccini 2019 年 7 月 14 日
Ok but... Take a look at this, which looks close to what I posted, and works:
@(~,~)func(inputA)
So... The handle and eventdata are introduced by the ButtonDownFcn callback at the left of func? In this case, is this the same as if I did this:
func(objecthandle, eventData, inputA)?
That is, is matlab passing to the function, silently, two arguments that go before the one I specified?
Having arguments both at the left and right of func is what has been confusing me a lot.
Stephen23
Stephen23 2019 年 7 月 14 日
編集済み: Stephen23 2019 年 7 月 14 日
"...is matlab passing to the function, silently, two arguments that go before the one I specified?"
In general graphics callback functions are called with minimum two arguments, exactly as the relevant documentation states. For example, the axes ButtonDownFcn is described as: "If you specify this property using a function handle, then MATLAB passes two arguments to the callback function when executing the callback:"
  • "Clicked object — Access properties of the clicked object from within the callback function."
  • "Event data — Empty argument. Replace it with the tilde character (~) in the function definition to indicate that this argument is not used."
Those will aways be the first two arguments to that function, so your function must be written to accept two input arguments (even if their values are ignored). You can also define a callback function to have more input arguments, and call it using the cell array syntax shown in the documentation:
'Somecallback',{@myfun,A,B}
where
function myfun(obj,evt,Ain,Bin)
This is explained in the MATLAB documentation:
"Having arguments both at the left and right of func is what has been confusing me a lot."
Those two sets of arguments are two totally things: the LHS arguments defines the inputs to an anonymous function, the RHS arguments are the inputs to whatever function you call inside that anonymous function. They do not have to be the same at all.
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 14 日
編集済み: Alexandre Piccini 2019 年 7 月 14 日
Thanks Stephan, I think that clears up things a bit more. So, only to make sure I got it right about the LHS and RHS part...
If I write...
'ButtonDownFcn',@(someObject,~)func(someObject) ...
this will pass the object handle (here labeled as "someObject" in the LHS) to the function, as it has the same variable name "someObject" being used as input. As far as I could understand handles, i think this is right, isn't it?
Much in the same way, if I do
t = 2;
'ButtonDownFcn',@(someObject,~)func(someObject,t) ...
I will also pass 't' as a second argument to func, along with the handle to the object clicked, right?
Also, one last question, just to make sure... When I do not specify any RHS arguments, will then MATLAB automatically do the equivalent to this?
'ButtonDownFcn',@(objHandle, EventData)func(objHandle, EventData) ...
Thank you. It has been such a great help ;)
Stephen23
Stephen23 2019 年 7 月 15 日
"If I write... this will pass the object handle ... to the function"
Yes.
"if I do ... I will also pass 't' as a second argument to func, along with the handle to the object clicked, right?"
Yes.
"When I do not specify any RHS arguments, will then MATLAB automatically do the equivalent to this?"
It doesn't really have anything to do with "RHS arguments", simply that every callback function (whether a handle to a main/local/nested function or an anonymous function) must accept as many arguments as the callback documentation specifies. You example would be simpler written as:
'ButtonDownFcn',@func
in which case the function will be called with two input arguments, exactly as the documentation shows.
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 16 日
Right, thanks. That helps. Is there a way to specify a variable to which the function must return values to? Currently, I can do the equivalent of
func(objhandle,someRandomInputs)
What if I wanted to do the equivalent of
someRandomOutputVariableInTheWorkspace = func(objhandle,someRandomInputs);
... instead of using global variables?
Thank you once again. That's all I got for now.
Stephen23
Stephen23 2019 年 7 月 16 日
編集済み: Stephen23 2019 年 7 月 16 日
Graphics callback functions do not return output arguments (because callbacks are asynchronous code, so there is no location in synchronous code (i.e. some script) where it would make sense for the value to be returned to).
I strongly recommend avoiding global variables. Personally I prefer using nested functions to pass data between callbacks and the main GUI function, as they are simple and intuitive.
All you need to do is this:
function MyMainFun(...)
X = []; % must be defined in the parent workspace!
...
someGraphicsFun(...,'someCallback',@nestFun)
...
function nestFun(obj,evt)
X = 3;
end
...
end
To see an example GUI that uses a mixture of local and nested functions you can download my FEX submssion iregexp:
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 16 日
Thank you Stephan. The problem is that currently I'm not using GUIDE. Perhaps, in a very near future for this app, I will, but not for now. Currently, this is just a standard plot that's becoming a bit more complex than usual. Honestly, the only way I can think of doing what I want is by using global variables or... pointers? It would be perfect to have pointers in MATLAB in this case.
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 16 日
Well, I think what I'll have to do is create an intermediary class that works as a handle, so I can pass-by-reference. I found that the answer by Pursuit at https://stackoverflow.com/questions/14793453/matlab-link-to-variable-not-variable-value might be just what I need.
Thank you anyway.
Stephen23
Stephen23 2019 年 7 月 16 日
編集済み: Stephen23 2019 年 7 月 16 日
"The problem is that currently I'm not using GUIDE"
I don't see why that is a problem: nothing in any of my comments is related to GUIDE. I have no idea why you suddenly mention GUIDE, but it has nothing to do with any of my comments.
Note that nested functions are not recommended for GUIDE, and I just advised you to use nested functions...
"Perhaps, in a very near future for this app, I will..."
I strongly recommend writing your own GUIs, rather than using GUIDE. As you already seem to be doing this, I don't see any reason for you to change (and when you write your own GUI you can used nested functions, just like I wrote in my last comment. I do all the time).
" Honestly, the only way I can think of doing what I want is by using global variables or..."
by reading my comments, where I showed you how to pass data between callbacks (without global variables), and I also included links to the MATLAB documentation which also explains how data can be passed between callbacks: did you read it?
PS: note that Walter Roberson's suggestion below is explained in the documentation that linked to, under the title "Store Data in UserData or Other Object Properties": it really would help you to read the documentation.
Walter Roberson
Walter Roberson 2019 年 7 月 16 日
fig = gcf;
fig.UserData.SomeRandomOutputVariable = whatever
Alexandre Piccini
Alexandre Piccini 2019 年 7 月 16 日
I see Stephen. I thought what you mentioned was specific to GUIDE's implementation way-of-things (I just assumed that), so I didn't pay much attention. I'll try what you suggested.

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

ヘルプ センター および File ExchangeInteractive Control and Callbacks についてさらに検索

製品

リリース

R2019a

Community Treasure Hunt

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

Start Hunting!

Translated by