Scope of 'ButtonDownFcn' property of lines in structure [R2017b]

2 ビュー (過去 30 日間)
TJ
TJ 2018 年 1 月 14 日
コメント済み: Rik 2018 年 1 月 15 日
Hello,
My question is regarding the scope of the ButtonDownFcn in two scenarios when used for selecting lines plotted in a figure.
Scenario 1: I have a push button callback from a GUIDE created GUI that plots two lines:
function pushbutton_Plot_Callback(hObject, eventdata, handles)
handles.P(1) = line(handles.Plot1, 'XData', handles.x', 'YData', handles.y1, 'Color', 'Blue', 'Tag', 'Ch1', 'ButtonDownFcn', {@LineSelected, handles} );
handles.P(2) = line(handles.Plot1, 'XData', handles.x', 'YData', handles.y2, 'Color', 'Green', 'Tag', 'Ch2', 'ButtonDownFcn', {@LineSelected, handles} );
For both scenarios the function LineSelected is called clicking on a line in the figure. The LineSelected function for scenario 1 is:
function LineSelected(hObject, eventdata, handles)
handles.strTag = get(hObject, 'Tag');
switch handles.strTag
case 'Ch1'
delete(handles.P(1));
handles.P(1) = line(handles.Plot1, 'XData', handles.x', 'YData', -handles.y1, 'Color', 'Blue', 'Tag', 'Ch1', 'ButtonDownFcn', {@LineSelected, handles} );
case 'Ch2'
delete(handles.P(2));
handles.P(2) = line(handles.Plot1, 'XData', handles.x', 'YData', -handles.y2, 'Color', 'Green', 'Tag', 'Ch2', 'ButtonDownFcn', {@LineSelected, handles} );
In scenario 1 if I click the 'Ch2' line I keep getting an error saying 'index exceeds matrix dimensions' and if I step through the program in the debugger, I see handles contained handles.P which consists of 2 lines but once the program steps into the LineSelected function is only consists of one elements and therefore one line. It seems the other line is outside the scope of the function even though I have said the entire handles structure should eb passed as an argument to the function.
Scenario 2: I have a push button callback from a GUIDE created GUI that plots two lines:
function pushbutton_Plot_Callback(hObject, eventdata, handles)
handles.P(1) = line(handles.Plot1, 'XData', handles.x', 'YData', handles.y1, 'Color', 'Blue', 'Tag', 'Ch1');
handles.P(2) = line(handles.Plot1, 'XData', handles.x', 'YData', handles.y2, 'Color', 'Green', 'Tag', 'Ch2');
set(handles.P, 'ButtonDownFcn', {@LineSelected, handles} );
function LineSelected(hObject, eventdata, handles)
handles.strTag = get(hObject, 'Tag');
switch handles.strTag
case 'Ch1'
delete(handles.P(1));
handles.P(1) = line(handles.Plot1, 'XData', handles.x', 'YData', -handles.y1, 'Color', 'Blue', 'Tag', 'Ch1');
case 'Ch2'
delete(handles.P(2));
handles.P(2) = line(handles.Plot1, 'XData', handles.x', 'YData', -handles.y2, 'Color', 'Green', 'Tag', 'Ch2' );
set(handles.h, 'ButtonDownFcn', {@LineSelected, handles}
In scenario 2 when I call the LineSelect function the function seems to have access to both handles.P(1) and handles.P(2). I am not sure why this is the case because from what I understand both methods are just setting the properties of the 'ButtonDownFcn' to {@LineSelected, handles}.

採用された回答

Rik
Rik 2018 年 1 月 14 日
To avoid these confusing bugs, it may be simpler to recreate the handles variable when you call the function:
'ButtonDownFcn', @(hObject,eventdata)LineSelected(hObject,eventdata,guidata(hObject))
(of course you should have preceded this by having saved the data to guidata)
  4 件のコメント
TJ
TJ 2018 年 1 月 14 日
Sorry Rik, I wasn't clear. Let me illustrate with an example:
function varargout = tester2(varargin)
% TESTER2 MATLAB code for tester2.fig
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @tester2_OpeningFcn, ...
'gui_OutputFcn', @tester2_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before tester2 is made visible.
function tester2_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to tester2 (see VARARGIN)
handles.x = 0:10;
handles.y1 = handles.x;
handles.y2 = handles.x*2;
% Choose default command line output for tester2
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% --- Outputs from this function are returned to the command line.
function varargout = tester2_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.h(1) = line(handles.axes1, 'XData', handles.x, 'YData', handles.y1, 'color','blue','Tag','1','ButtonDownFcn',{@select, handles});
handles.h(2) = line(handles.axes1, 'XData', handles.x, 'YData', handles.y2, 'color','green', 'Tag','2','ButtonDownFcn',{@select, handles});
function select(hObject, ~, handles)
switch hObject.Tag
case '1'
handles.y1=handles.y1+100;
set(hObject, 'YData', handles.y1);
case '2'
end
guidata(hObject, handles)
Now when I look at what is in handles.y2, the original values which I set in the initialisation function are still there so it seems that the handles.y2 variable was not updated.
Rik
Rik 2018 年 1 月 15 日
I hope you understand the phrasing by Steven Lord, because that is also what I meant with my initial answer. You need to be careful when it is you load the guidata. That is one of the reasons why I don't use the cell syntax: it makes this more difficult (also I didn't know it until a few weeks back).

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

その他の回答 (1 件)

Steven Lord
Steven Lord 2018 年 1 月 15 日
"Now when I look at what is in handles.y2, the original values which I set in the initialisation function are still there so it seems that the handles.y2 variable was not updated."
That is correct. Look at your code:
handles.h(1) = line(handles.axes1, 'XData', handles.x, ...
'YData', handles.y1, 'color','blue','Tag','1', ...
'ButtonDownFcn',{@select, handles});
The last part of that command creates a cell array with two elements. The first element of the cell array is a function handle. The second element of the cell array is a copy of the handles struct array as it exists when that command gets executed. Changing the handles structure later on has no effect on the copy created by this line.
Instead of passing the handles structure in as an input, retrieve the handles structure inside the select function. The first input to the ButtonDownFcn will be the handle to the line. Use the guidata function with that line handle as input to retrieve the handles structure.
  2 件のコメント
TJ
TJ 2018 年 1 月 15 日
Thanks Steve. One question: when one calls the set function with the handle to the function and the additional argument, in this case handles, you mentioned that the state of handles at that time of the set call would be used when making calls through the ButtonDownFcn. I have tested using the guidata(hObject) (as Rik also mentioned above) and it works fine now. However, why is it that the associated values of the hObject which we use in guidata(hObject) are not loaded as they were when the set function is called? Why is the updated structure used in this case but not in the case where we use handles as the argument?
Hope my question makes sense.
Rik
Rik 2018 年 1 月 15 日
Because the hObject is the same each time. You could compare it to an address: the building stays at the same place, the people might move. If you want to use the number of people in the house, you don't hard-code the number of people, you load the number of people in the house with guidata.
Could you also accept the answer that solves your problem best?

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

カテゴリ

Help Center および File ExchangeInteractive Control and Callbacks についてさらに検索

Community Treasure Hunt

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

Start Hunting!

Translated by