Problem with callback function "hanging" after certain number of times pressed

I am having an issue with a function that should allow the user to select a undefined number of points on a graph, and click done whenever the user decides all the relevant points are selected. I am having an issue where, after 19 "btnAdd" clicks, MATLAB "hangs" and will not let me click anything else. I have added print statements at the beginning of my callback which do not execute, making it seem that the callback is never called on the 20th click. I have also tried using drawnow and pause(0.2), etc. to get rid of the hang problem as recommended in different answers. Here is an example of the code without any extraneous details.
function testAddButton
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
handles = guidata(gcf);
handles.impointArray = impoint.empty;
guidata(gcf, handles);
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addCallbackFunction,...
'BusyAction', 'cancel',...
'Interruptible', 'off');
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', 'uiresume(gcbf); drawnow; pause(0.05);');
uiwait(gcf); % wait until 'Done' is pressed
drawnow; pause(0.05);
delete(btnAdd); % remove the button
delete(btnDone); % remove the button
handles.guidata(gcf)
end
function addCallbackFunction(gcf, eventdata, handles)
handles = guidata(gcf);
impointArray = handles.impointArray;
impointArray(end+1) = impoint(gca);
handles.impointArray = impointArray;
guidata(gcf, handles);
uiwait(gcbf);
end
When MATLAB is hanging, I can Ctrl-C and I get this message:
??? Operation terminated by user during ==> uiwait at 73
In ==> testAddButton>addCallbackFunction at 36
uiwait(gcbf);
??? Error using ==> waitfor
Interrupt while evaluating uicontrol Callback.
??? Operation terminated by user during ==> uiwait at 73
In ==> manageInteractivePlacement at 93
uiwait(h_fig);
In ==> impoint>impointAPI at 247
placement_aborted =
manageInteractivePlacement(h_axes,h_group,@placePoint);
In ==> impoint>impoint.impoint at 90
[h_group,draw_api] = impointAPI(varargin{:});
In ==> testAddButton>addCallbackFunction at 33
impointArray(end+1) = impoint(gca);
??? Error using ==> waitfor
Interrupt while evaluating uicontrol Callback.
??? Operation terminated by user during ==> uiwait at 73
In ==> testAddButton>addCallbackFunction at 36
uiwait(gcbf);
??? Error using ==> waitfor
Interrupt while evaluating uicontrol Callback.
I am using MATLAB R2011A. Thanks so much!

 Accepted Answer

kaycraw - since you are programmatically creating your GUI, there are a couple of things that you can do to simplify the above. Rather than having your main function and your callback separate, you can nest the latter into the former so that it can access all local variables defined in the parent function. That way you can avoid the use of the handles structure (which is more of a GUIDE GUI "feature") and just access the variables as you need them. For example, the above code can be rewritten as
function testAddButton
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
pointArray = [];
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
function addPointBtnCallback(hObject, eventdata)
newPoint = ginput(1);
pointArray = [pointArray ; newPoint];
end
function doneBtnCallback(hObject, eventdata)
fprintf('%f %f\n',pointArray');
delete(btnAdd);
delete(btnDone);
end
end
Note how the pointArray variable is declared within the testAddButton function but can still be accessed in the nested (callback) functions. Also, the use of the uiwait and uiresume have been removed since we can use the doneCallback to delete the controls from our figure. (I used ginput in place of impoint since I don't have the Image Processing Toolbox, but the idea will be the same.)
Try the above and see what happens!
As an aside, your signature for the * addCallbackFunction* is defined as
function addCallbackFunction(gcf, eventdata, handles)
The first input parameter is named gcf. This is the name of a built-in MATLAB function (get current figure) and so you should avoid naming your own variables like this. Also, the third input parameter, handles, is likely never initialized. Outside of GUIDE, callbacks typically have only the object handle and event data input parameters. You can add more, but you need to do this explicitly.

10 Comments

Thanks Geoff! This worked perfectly. Thank you also for input into problematic areas of the code. For reference, do you think my original problem had to do with the uiwait and uiresume, or the passing of variables between the callback and main code?
Best, Kate
Hi Geoff,
I actually have an issue where I provided too simple of an example code, and now the answer does not work in my actual code. I am so sorry for not formulating the question correctly.
Essentially, in my final piece of code, I feel that I need to use the uiwait and the uiresume, as I perform some logic on the impoint array after all the points have been defined by the user. In addition, the figure is created inside a for-loop, and in the ideal case, the user is able to loop through many figures, defining points on each, with the logic done on each set of point.
When the uiwait and uiresume are taken out, however, the function "ends" before the done button is pressed - I believe this means that the user cannot loop through the many images defining points.
An example piece of code for this problem is:
function testAddButton_MWResponse
for i=1:10
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
pointArray = [];
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
% Finding the x index of all the points (necessary in real code)
posArray = zeros(numel(pointArray),2);
indexArray = zeros(numel(pointArray), 1);
for point = 1:numel(pointArray)
posArray(point,:) = getPosition(pointArray(point));
indexArray(point) = find(x(:)>posArray(point,1), 1, 'first');
end
indexArray = sort(indexArray);
% do some logic to separate the curves into sections defined by
% points and save the data into the curve number
for curveNum = 1:(length(indexArray)-1)
thisCurve = y(indexArray(curveNum):indexArray(curveNum+1));
saveMatDir = strcat(num2str(curveNum), '.mat');
thisCell.curveData = thisCurve;
save(saveMatDir, 'thisCell');
end
end
function addPointBtnCallback(hObject, eventdata)
newPoint = impoint(gca);
pointArray = [pointArray; newPoint];
end
function doneBtnCallback(hObject, eventdata)
delete(btnAdd);
delete(btnDone);
end
end
When I execute this piece of code, the logic and saving section does not complete when done is pressed, and the code does not loop through 10 figures. Because of this, I feel that I need uiwait and uiresume, but I still get the MATLAB hang after 19 clicks issue that I was previously getting. I have tried adding all the logic done of the user-defined points into the doneBtnCallback function, but I still have the issue where the for loop doesn't loop - instead the function just ends.
Thanks again!
Best, Kate
Just wanted to input my code that is having issues with uiwait and uiresume:
function testAddButton_MWResponse
for i=1:10
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
pointArray = [];
clicks = 0;
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
if clicks == 19
fprintf('here');
end
uiwait(gcf);
% Finding the x index of all the points (necessary in real code)
posArray = zeros(numel(pointArray),2);
indexArray = zeros(numel(pointArray), 1);
for point = 1:numel(pointArray)
posArray(point,:) = getPosition(pointArray(point));
indexArray(point) = find(x(:)>posArray(point,1), 1, 'first');
end
indexArray = sort(indexArray);
% do some logic to separate the curves into sections defined by
% points and save the data into the curve number
for curveNum = 1:(length(indexArray)-1)
thisCurve = y(indexArray(curveNum):indexArray(curveNum+1));
saveMatDir = strcat(num2str(curveNum), '.mat');
thisCell.curveData = thisCurve;
save(saveMatDir, 'thisCell');
end
end
function addPointBtnCallback(hObject, eventdata)
clicks = clicks + 1;
newPoint = impoint(gca);
pointArray = [pointArray; newPoint];
wait(pointArray(end));
end
function doneBtnCallback(hObject, eventdata)
delete(btnAdd);
delete(btnDone);
uiresume(gcf);
end
end
Hi Kate - if you have some code that you want to evaluate once the user has pressed the Done button, then try placing all that code within the doneBtnCallback. Perhaps something like
function doneBtnCallback(hObject, eventdata)
% Finding the x index of all the points (necessary in real code)
posArray = zeros(numel(pointArray),2);
indexArray = zeros(numel(pointArray), 1);
for point = 1:numel(pointArray)
posArray(point,:) = getPosition(pointArray(point));
indexArray(point) = find(x(:)>posArray(point,1), 1, 'first');
end
indexArray = sort(indexArray);
% do some logic to separate the curves into sections defined by
% points and save the data into the curve number
for curveNum = 1:(length(indexArray)-1)
thisCurve = y(indexArray(curveNum):indexArray(curveNum+1));
saveMatDir = strcat(num2str(curveNum), '.mat');
thisCell.curveData = thisCurve;
save(saveMatDir, 'thisCell');
end
delete(btnAdd);
delete(btnDone);
end
As for wanting to wait before proceeding to the next figure, could you not just put the uiresume in the doneBtnCallback? Perhaps something like
function testAddButton_MWResponse
for i=1:10
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
pointArray = [];
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
uiwait;
end
function addPointBtnCallback(hObject, eventdata)
newPoint = impoint(gca);
pointArray = [pointArray; newPoint];
end
function doneBtnCallback(hObject, eventdata)
% Finding the x index of all the points (necessary in real code)
% etc.
delete(btnAdd);
delete(btnDone);
uiresume;
end
end
Hi Geoff!
That is definitely the solution I implemented in my second response titled "issues with uiwait and uiresume." I agree that this should in theory work, but I have the issue where I am able to input 19 points, but the 20th point always has the MATLAB "hang" issue, where it is stuck in the:
wait(pointArray(end));
line in the addPtBtnCallback function. I'm not sure why this is happening, and always at the same 20th point. I also have this same issue if I just use uiwait at that line. If I don't use the wait in the addPtBtnCallback function, the function skips to the next graph without the user pressing the "Done" button, so it appears that wait is needed there. For example, when using the piece of code you sent, the next graph is automatically put up after the point is added.
Best, Kate
Hi Kate - I missed the line of code
wait(pointArray(end));
What are you attempting to do here? Are you waiting on the impoint to "finish"?
Hi Geoff.
In that line of code, I am trying to keep the function from going straight to the next figure in the loop. If I take that out (or similarly a uiwait at that position), the code immediately opens the next figure after the point is placed, instead of waiting for the done button push. I'm not sure why the uiwait after the buttons are created in the main body of code does not keep the for loop from moving on, as I believe it should.
Here is the code without the wait in the addPtBtnCallback function, which immediately skips straight into the next iteration of the for loop after a point is placed on the figure.
function testAddButton_MWResponse
for i=1:10
close all;
x = [1:1:100];
y = rand([100,1]);
plot(x,y, 'm');
pointArray = [];
clicks = 0;
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
uiwait;
end
function addPointBtnCallback(hObject, eventdata)
newPoint = impoint(gca);
pointArray = [pointArray; newPoint];
end
function doneBtnCallback(hObject, eventdata)
% Finding the x index of all the points (necessary in real code)
posArray = zeros(numel(pointArray),2);
indexArray = zeros(numel(pointArray), 1);
for point = 1:numel(pointArray)
posArray(point,:) = getPosition(pointArray(point));
indexArray(point) = find(x(:)>posArray(point,1), 1, 'first');
end
indexArray = sort(indexArray);
% do some logic to separate the curves into sections defined by
% points and save the data into the curve number
for curveNum = 1:(length(indexArray)-1)
thisCurve = y(indexArray(curveNum):indexArray(curveNum+1));
saveMatDir = strcat(num2str(curveNum), '.mat');
thisCell.curveData = thisCurve;
save(saveMatDir, 'thisCell');
end
delete(btnAdd);
delete(btnDone);
uiresume;
end
end Thanks so much again for all your help!
Best, Kate
Hi Kate - I wonder if you need to be more explicit about which figure you want to wait on. From uiwait, uiwait(h) blocks execution until uiresume is called or the figure h is deleted. Whereas, uiwait blocks execution until uiresume is called or the current figure is deleted. Perhaps in the latter, the figure has something to do with the impoint function. Please try waiting on the figure upon which you have plotted the data
function testAddButton_MWResponse
close all;
for i=1:10
x = [1:1:100];
y = rand([100,1]);
hFig = figure;
plot(x,y, 'm');
pointArray = [];
btnAdd = uicontrol('String','Add point',...
'Position',[90 60 70 30],...
'Callback', @addPointBtnCallback);
btnDone = uicontrol('String','Done',...
'Position',[165 60 40 30],...
'Callback', @doneBtnCallback);
uiwait(hFig);
end
function addPointBtnCallback(hObject, eventdata)
newPoint = ginput(1);
pointArray = [pointArray; newPoint];
end
function doneBtnCallback(hObject, eventdata)
% Finding the x index of all the points (necessary in real code)
fprintf('%f %f\n',pointArray');
close(hFig);
end
end
The above is a very simplified version of yours, replacing the calls to impoint with ginput. Note the differences - I've put the close all outside of the for loop and left the responsibility of closing the current figure to be handled by the Done button (see its callback). We now wait on the figure that we create on each iteration of the for loop so this should hopefully remove any issues that you were experiencing before. So we shouldn't move to the next iteration of the for loop until either the Done button is pressed, which instead of calling uiresume closes or deletes the current figure. Please give it a try! :)
kaycraw's answer moved here
Hi Geoff - that works great! For some reason, ginput seems to work much better than impoint. Impoint needs to have the uiwait(hFig) inserted into the addBtnCallback, and also always seems to stall out at the 20th point. ginput doesn't need the uiwait in the addBtnCallback and doesn't stall out after a certain number. Do you have any idea why? Thank you so much again! I think my problem is solved!
Glad that it worked out, Kate. I'm not sure what ginput does differently. Perhaps someone from The Mathworks can respond (?).

Sign in to comment.

More Answers (0)

Asked:

on 30 Jul 2016

Commented:

on 2 Aug 2016

Community Treasure Hunt

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

Start Hunting!