Listener ignores some part of the code and creates unwanted figure

2 views (last 30 days)
Hello,
I have a GUI that takes values from a slider and makes plots using them. I'm using a listener to get all the plots for all the values along the way when the slider is dragged. It is working, however, I have a little problem.
function slider1_Callback(hObject, eventdata, handles)
%unnecessary code here
D_son = evalin('base','D_son');
axes(handles.axes1);
plot(handles.axes1, D_son);
xlim([1,360]) %LINE ONE
set(gca,'XTick',[1 45 90 135 180 225 270 315 360])%LINE TWO
addlistener(handles.slider1,'ContinuousValueChange',@(hObject, event) slider1_Callback(hObject, eventdata, handles));
My problem is, when I drag the slider;
1. It places the graph on the correct axes, however, it brings up a blank Figure page.
2. It completely ignores LINE ONE and LINE TWO I marked up there.
Other than these two, it works just fine. How can I fix these problems? Thank you in advance.
Regards

Accepted Answer

Geoff Hayes
Geoff Hayes on 4 Aug 2014
deniz - I mocked up a GUI that was similar to yours and did notice some odd behaviour when it came to plotting the data and setting the axis limits. More often than not, a new figure was created.
I added the following lines of code to the end of your slider1_Callback because I was curious as to the effect of re-adding the listener each time the callback was called
% get the most recent handles structure (not necessarily the copy from
% above)
handles = guidata(hObject);
% if no count field, then create one!
if ~isfield(handles,'count')
handles.count = 0;
end
% increment the count
handles.count = handles.count + 1;
% save the data to the GUI
guidata(hObject,handles);
% print out the count number
fprintf('slider1_Callback %d\n',handles.count);
The handles structure that is being passed in as the third argument to this callback is just a copy of when the callback function was created/assigned to the event (ContinuousValueChange) so I use guidata to get the most up-to-date copy of the handles structure. Now when I ran the code, the simple GUI appeared and I pressed the slider up-arrow once. This was the result in the Command Window
slider1_Callback 1
As expected, the callback was just called the once. I then pressed the up-arrow of the slider again
slider1_Callback 2
slider1_Callback 3
Uh-oh! And a figure appeared outside of the GUI (with the correct axis limits). A third time
slider1_Callback 4
slider1_Callback 5
slider1_Callback 6
slider1_Callback 7
A fourth time,
slider1_Callback 8
slider1_Callback 9
slider1_Callback 10
slider1_Callback 11
slider1_Callback 12
slider1_Callback 13
slider1_Callback 14
slider1_Callback 15
And I think that you can see the pattern. Each time the callback is called, a new listener is created. Yikes.
The listener needs only to be created the once, and I suggest that you do that in the OpeningFcn of the GUI. Please try the following
1. Rename your slider1_Callback (in the m file) to slider1ContValCallback.
2. In the property editor for your slider, remove the callback text associated with the Callback property. We are doing this because we don't want both a Callback and a ContinousValueChange callback for the same slider object.
3. In the OpeningFcn of your GUI, add the following lines of code (or modify so that it looks similar)
function yourGuiName_OpeningFcn(hObject, eventdata, handles, varargin)
% Choose default command line output for untitled
handles.output = hObject;
handles.sliderListener = addlistener(handles.slider1,'ContinuousValueChange', ...
@(hObject, event) slider1ContValCallback(...
hObject, eventdata, handles));
% Update handles structure
guidata(hObject, handles);
So all we are doing is creating the listener and saving its handle to the handles structure (in case we ever need access to it).
4. In your (now renamed) slider1ContValCallback function, set the first line as
handles = guidata(hObject);
just to make sure that you have the most recent copy of handles. Now do as before with
%unnecessary code here
D_son = evalin('base','D_son');
and replace everything that follows with
plot(handles.axes1, D_son);
xlim(handles.axes1,[1,360]) %LINE ONE
set(handles.axes1,'XTick',[1 45 90 135 180 225 270 315 360])%LINE TWO
% if no count field, then create one!
if ~isfield(handles,'count')
handles.count = 0;
end
% increment the count
handles.count = handles.count + 1;
% save the data to the GUI
guidata(hObject,handles);
% print out the count number
fprintf('slider1_Callback %d\n',handles.count);
So this is almost as before but without the axes(handles.axes1) call, no listener add, and the code explicitly indicates which axes to update for xlim and set.
Trying out the above seems to produce the desired result - no new figure is created, the axes limits are defined, and the callback is only called the once for each up or down slide of the slider.
Try the above and see what happens!
  2 Comments
deniz
deniz on 4 Aug 2014
Edited: deniz on 4 Aug 2014
That is amazing! Not just because it worked, it worked yeah but the amazing part is that you took time typing all the explanations clearly, which made your post very informative for a matlab rookie like me. Now first, I quickly modified my code as you said to see if it would work, without paying too much attention to explanations. And then it worked, and then I came back and read the entire post carefully and there is a lot for me to learn about MATLAB in your post. I'll need to read it a couple more times and do some google-ing to make sure I understand everything completely.
Also, turns out dpb was right, I was putting the listener under the wrong callback for the whole time.
Thank you both a lot for your answers and help!

Sign in to comment.

More Answers (1)

dpb
dpb on 3 Aug 2014
I don't do GUIs so anything specific to that is out of scope, but...
axes(handles.axes1);
plot(handles.axes1, D_son);
xlim([1,360]) %LINE ONE
set(gca,'XTick',[1 45 90 135 180 225 270 315 360])%LINE TWO
Why do you call axes, specifically, when you use the same handle in plot? Altho it shouldn't really matter seems like overkill to do both.
axes(handles.axes1);
plot(D_son);
xlim([1,360])
set(gca,'XTick',[1 45:45:360])
Mayhaps you need a drawnow to refresh the plot?
I'm not sure what is meant by "a blank Figure page"?
  4 Comments
deniz
deniz on 3 Aug 2014
actually yes, I might have put the code on a wrong line. and thank you again for your responses dpb.
dpb
dpb on 3 Aug 2014
Edited: dpb on 4 Aug 2014
I don't think it's a "code on a wrong line" thing; I think it's having code that creates a figure/plot/axes (the latter two which create the first automatically) in the wrong callback or at least in a place in a callback that should test for whether the desired figure already exists or not.
It's an overall application architecture question of "who does what and when?" that needs solving is what I'm getting at.
ADDENDUM
And, to specifically point it out, it's NOT the code in this callback that's the problem -- the figure/axes have already been created when you get here or the handle to the axes you reference here would not be valid and you'd error.

Sign in to comment.

Categories

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

Products

Community Treasure Hunt

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

Start Hunting!