Matlab gui WindowButtonMotionFcn crashes when called too often?

你说的曾经没有我的故事 提交于 2020-01-13 11:24:06

问题


I've set WindowButtonMotionFcn to my callback which plots three plots, with the data depending on mouse position. However this seems to be too much for MATLAB to handle, because after moving my mouse around a bit, the GUI stops responding.

I use this code (copied parts from someone):

set(handles.figure1, 'windowbuttonmotionfcn', @hover_Callback);

function hover_Callback(hObject, handles, eventdata)
inside = false;

pos = get(handles.axes1, 'currentpoint');
xlim = get(handles.axes1, 'XLim');
ylim = get(handles.axes1, 'YLim');

if (pos(1,1) > max(xlim(1), 1) && ...
        pos(1,1) < xlim(2) && ...
        pos(1,2) > ylim(1) && ...
        pos(1,2) < ylim(2))
    inside = true;
end
if ~inside
    return
end
ix = round(pos(1,1));
iy = round(pos(2,2));
axes(handles.axes2); cla; plot(squeeze(t2(ix,iy,:)), squeeze(d2(ix,iy,:)));
axes(handles.axes3); cla; plot(squeeze(t3(ix,iy,:)), squeeze(d3(ix,iy,:)));
axes(handles.axes4); cla; plot(squeeze(t4(ix,iy,:)), squeeze(d4(ix,iy,:)));

This causes my GUI to stop responding, without error codes. If I then quit it and start it again the whole of MATLAB stops responding. Anyone knows what could be happening and how I can fix this? Maybe I'm somehow clogging my memory?


回答1:


When a callback is called with high frequency, there is the danger that it will be called again before another call has finished executing (i.e. re-entrancy). With WindowButtonMotionFcn, there's a pretty darn good chance that this will happen.

You can prevent callback re-entrancy by inspecting the function call stack (the output of dbstack) for multiple calls to the responsible callback. A very straightforward, but clever implementation of such a check called isMultipleCall is presented in a post on undocumentedmatlab.com. The idea is to count the number of times the callback function name appears on the stack. Take the actual function directly from undocumentedmatlab.com, but it distills to the following:

function flag=isMultipleCall()
s = dbstack();
% s(1) corresponds to isMultipleCall
if numel(s)<=2, flag=false; return; end
% compare all functions on stack to name of caller
count = sum(strcmp(s(2).name,{s(:).name}));
% is caller re-entrant?
if count>1, flag=true; else flag=false; end

The usage of isMultipleCall is very simple. Put run it at the top of the callback (in this case, hover_Callback), and bail out if it indicates that multiple calls are in progress:

function hover_Callback(hObject, eventdata, handles)

if isMultipleCall();  return;  end

...

end

This prevents the callback from executing fully again until previous calls have terminated. Only the check will be run, skipping the intensive graphics object operations (i.e. axes, plot, etc.)


An alternative approach is to use a listener for the WindowButtonMotionEvent:

handles.motion = handle.listener(gcf,'WindowButtonMotionEvent',@hover_callback2);

Then in the callback, check the eventdata.CurrentPoint property instead of currentpoint. Check for re-entrancy as above.

If you are NOT using GUIDE and do not have a handles structure managed by guidata, then call the listener something like motionListener and use setappdata to store the listener. For example,

setappdata(hFigure,'mouseMotion',motionListener);

Just use the known handle of any object in the GUI so the listener persists. You could also use UserData instead of setappdata, or any other way of managing GUI data.


As an aside, note that the axes command is rather slow, and can be avoided by passing the axis handle to plot directly:

plot(handles.axes2, squeeze(t2(ix,iy,:)), squeeze(d2(ix,iy,:)));


来源:https://stackoverflow.com/questions/20427388/matlab-gui-windowbuttonmotionfcn-crashes-when-called-too-often

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!