【问题标题】:How to prevent a callback from being interrupted by a figure's close function?如何防止回调被图形的关闭函数中断?
【发布时间】:2013-06-05 02:52:27
【问题描述】:

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

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

我最后的办法是在运行回调时简单地将图形的'CloseRequestFcn' 设置为'',但这似乎很乏味。有没有标准的解决方案来实现这一点?

编辑:

来自 matlab 的文档:

注意如果中断回调是 DeleteFcn 或 CreateFcn 回调或图形的 CloseRequest 或 ResizeFcn 回调,它 中断正在执行的回调,无论它的值如何 对象的可中断属性。中断回调开始 在下一个drawow、figure、getframe、pause或waitfor执行 陈述。图窗的 WindowButtonDownFcn 回调例程,或 对象的 ButtonDownFcn 或 Callback 例程按照 上述规则。

因此,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

如果我在第一个 set 命令的回调期间退出,matlab 会发出错误。

large computation -> imshow -> imshow -> imshow

如果我在回调期间取消,也会在第三个 imshow 上发出错误。

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

【问题讨论】:

  • 一种解决方法可能是在CloseRequestFcn 中启动一个计时器,并在计时器回调中尝试通过检查 flag (@ 987654347@) 当图形不应该关闭时(例如在重要进程中)设置为 false。
  • 在我看来,导致错误的实际问题是您的回调中使用的数据被数字删除了。在图形之外创建所需数据的副本可能是一个想法(无论如何都是一个好习惯)。也许将copyfig 用于隐藏窗口,或者只是在回调开始时手动存储您需要的所有内容。请务必在完成后将其清理干净。
  • @bdecaf 不仅如此。有时我必须更新可见的图形轴。如果此回调被图形关闭中断,则它会尝试更新不存在的轴句柄。看起来很奇怪matlab会这样做。这似乎使任何回调都容易被图形关闭中断。如何使更新轴免受这种行为的影响?
  • 正在考虑在每个回调周围放置一个try-catch 声明...遗憾的是,这似乎是最紧凑的解决方案...
  • 如果你有冗长的计算,实现一些逻辑以便你可以取消它们可能是有意义的。这是一个很好的做法,因此不再需要输出的函数不会浪费您的资源。但我同意你的观点,这个功能应该由 matlab 提供。

标签: matlab


【解决方案1】:

我从来没有真正相信过Interruptible 标志(或类似机制)...我立即承认我从未经常使用它,但那是因为当我第一次尝试它时,我注意到@ 987654322@、'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

理论上,当你将所有个回调放在这种模式中时,它基本上等同于管理你自己的事件队列。

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

无论如何,我怀疑这是一个很好的起点。

【讨论】:

  • 关闭请求函数在返回中断回调之前不是运行完成了吗?在这种情况下,'userData' 似乎永远不会是idle
  • @jucestain:你的意思是关闭请求功能也应该锁定GUI?
  • 我将closeReqFcn 中的while 循环解释为暂停执行直到中断的回调完成运行的一种方式。我的理解是 closeReqFcn 在返回被中断的回调之前完成,因此将处于死锁状态,因为它正在等待中断的回调完成。这是正确的吗?
【解决方案2】:

@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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2017-08-10
    • 2020-07-13
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多