2013-06-05 44 views
3

想知道如何实现标题中的问题。我有一些从按钮按下运行的回调。这些回调,如果被数字关闭中断,将导致错误,因为函数似乎运行,然后被关闭函数关闭数字而中断,然后在数字关闭后回调似乎恢复。如何防止回拨被图形的关闭功能中断?

如果我将按钮的'Interruptible'属性设置为'on',它可以防止其他回调中断它,但似乎不适用于图形的关闭功能。我的另一个想法是在图的'CloseRequestFcn'回调中指定'closefunction',然后在删除图之前调用drawnow来刷新事件队列,但这不起作用。

对我来说,最后一招是在运行回调时将图形的'CloseRequestFcn'设置为'',但这看起来很乏味。有没有一个标准的解决方案来完成这个?

编辑:

从MATLAB的文件:

注意如果中断回调是DeleteFcn或CreateFcn 回调或人物的CloseRequest或ResizeFcn回调,它 不管中断值的执行回调该对象的可中断属性为 。中断回调在下一个drawnow,figure,getframe,pause或waitfor 语句中启动 执行。图形的WindowButtonDownFcn回调例程或对象的ButtonDownFcn或回调例程按照上述规则进行处理。

因此,看来是interruptible属性不影响关闭功能的情况。

编辑2:

好的,所以我觉得我发现了一个问题。这真是奇怪。其实我从MATLAB文档是回调只是中断发现,如果他们有interruptible属性设置为on AND:

If there is a drawnow, figure, getframe, waitfor, or pause command in the running callback, then MATLAB executes the interrupting callbacks which are already in the queue and returns to finish execution of the current callback.

我不使用任何的明确这些功能,所以事实证明我的大部分回调不能被closereqfcn中断。但是,事实证明有些是,而为什么看起来很奇怪。如果有回调:

`大的计算 - > imshow - > imshow

large computation -> set -> set -> set -> set

其中set命令设置轴visible属性为off,然后没有中断似乎发生,如果我退出回调

现在时,如果我有:

large computation -> imshow -> set -> imshow -> set

如果我在第二个set命令的回调期间退出,matlab会发出错误。另外,如果我有:

large computation -> imshow -> imshow -> set

MATLAB发出一个错误,如果第一set命令的回调过程中我退出。如果我在回调过程中取消

large computation -> imshow -> imshow -> imshow

还发行了第三imshow错误。

由于某种原因,似乎连续拨打两个imshow致使我的回调中断。是否有可能matlab隐式调用drawnow或做一些奇怪的事情,如果你使用多个imshow?顺便说一句,我的matlab版本是R2009a。

+1

一个解决方法可以在'CloseRequestFcn'可以启动计时器,并在计时器回调试图关闭这个数字如果没有通过检查**标志中断任何重要的回调**('close_allowed'),你当数字不应该被关闭时(例如在重要进程中)设置为false。 – pm89

+1

在我看来,导致错误的实际问题是回调中使用的数据会被删除。在图形之外创建所需的数据副本可能是一个想法(无论如何,这是一个很好的做法)。也许用'copyfig'到一个隐藏的窗口,或者只是在回调开始时手动存储你所需要的。完成后请务必清理干净。 – bdecaf

+0

@bdecaf不仅仅如此。有时我必须更新可见的数字轴。如果该回调被图形关闭中断,则它会尝试更新不存在的轴手柄。看起来很奇怪的matlab会这样做。这似乎使任何回调都可以被数字关闭中断。如何让这种行为更安全? – Justin

回答

2

我从来没有真正相信Interruptible标志(或类似的机制)......我立即承认我从来没有使用过很多,但那是因为当我第一次尝试它时,我注意到'Interruptible',   'off'(和朋友)似乎有更多的例外规则比它的维修 - 头痛材料警报!

所以,我养成了通过简单的使用标志来解决这类问题的习惯,并且在锁定/释放函数中包装了所有必须不可中断的回调。

事情是这样的:

% Define a button 
uicontrol(... 
    'style', 'pushbutton',... 
    'interruptible', 'off',... % Nice, but doesn't catch DeleteFcn, CreateFcn, ... 
           % CloseRequestFcn or ResizeFcn 
    % ... 
    % further definition of button 
    % ... 

    % Put callback in a wrapper: 
    'callback', @(src,evt) uninterruptibleCallback(@buttonCallback, src,evt)... 
); 

其中uninterruptibleCallback()看起来是这样的:

function varargout = uninterruptibleCallback(callback, varargin) 

    % only execute callback when 'idle' 
    % (you can omit this if you don't want such strict ordering of callbacks) 
    while ~strcmp(get(mainFigure, 'userData'), 'idle') 
     pause(0.01); 
     % ...or some other action you desire 
    end 

    % LOCK 
    set(mainFigure, 'userData', 'busy'); 

    try 
     % call the "real" callback 
     [varargout{:}] = callback(varargin{:}); 

     % UNLOCK 
     set(mainFigure, 'userData', 'idle'); 

    catch ME 
     % UNLOCK 
     set(mainFigure, 'userData', 'idle'); 

     throw(ME); 
    end 

end 

,它允许您使用此closeReqFcn()你的身材:

function closeReqFcn(~,~) 

    % only when the currently running locked callback (if any) has finished 
    while ~strcmp(get(mainFigure, 'userData'), 'idle') 
     pause(0.01); 
     % ...or some other action you desire 
    end 

    % ... 
    % further clean-up tasks 
    % ... 

    % deletion 
    delete(mainFigure); 

end 

理论上,当你把全部这种模式的回调,它基本上等同于管理你自己的事件队列。

这当然有一些优点,但有很多很多缺点 - 您可能需要考虑一下。整个机制对于您的用例来说可能会慢得令人无法接受,或者您可能需要定义更多具有更具体行为的锁定函数。

无论如何,我怀疑这是一个开始的好地方。

+0

关闭请求函数在返回中断的回调之前是否运行完成?在这种情况下,似乎'userData'将始终保持不“空闲”状态。 – Justin

+0

@jucestain:你的意思是关闭请求函数也应该锁定GUI? –

+0

我正在解释'closeReqFcn'中的'while'循环,作为暂停执行的方式,直到中断的回调完成运行。我的理解是,closeReqFcn在返回到被中断的回调之前完成,并因此处于死锁状态,因为它正在等待中断的回调完成。它是否正确? – Justin

0

@Rody Oldenhuis的解决方案的替代方法是在CloseRequestFcn内部启动一个计时器,以便在没有不间断代码正在进行时关闭该数字(可以用一个标记来表示该数字(Closing_Allowed)。

function mainFig_CloseRequestFcn(hObject, eventdata, handles) 

    Time = 3; % Wait time before force killing (in sec) 
    Kill.tmr = timer('executionMode', 'fixedRate',... 
     'Period', 1/10,... 
     'TimerFcn', {@KillingTimer_Callback, handles}); 
    Kill.counts = ceil(Time/Kill.tmr.Period); 

    setappdata(handles.mainFig,'Kill',Kill); 

    start(Kill.tmr); 

function KillingTimer_Callback(hObject, eventdata, handles) 

    Kill = getappdata(handles.mainFig,'Kill'); 
    Kill.counts = Kill.counts - 1; % Count down 
    setappdata(handles.mainFig,'Kill',Kill); 

    if Kill.counts == 0 || getappdata(handles.mainFig, 'Closing_Allowed') 
     stop(Kill.tmr); 
     delete(handles.mainFig); 
    end 

if Kill.counts == 0意味着超时,并关闭即使不间断工作正在进行中,然后将导致你现在有时同样的错误的数字,但如果你知道的最长时间你需要完成不可中断的工作,那么你可以正确设置上面的Time

最后通过设置Closing_Allowed标志来包装不间断代码。

function pushbutton_Callback(hObject, eventdata, handles) 

    setappdata(handles.mainFig, 'Closing_Allowed', 0); % Closing is not allowed 
    pause(2); 
    setappdata(handles.mainFig, 'Closing_Allowed', 1); % Closing is allowed