【问题标题】:Stop A GUI in a middle of process in MATLAB在 MATLAB 的进程中间停止 GUI
【发布时间】:2015-04-06 12:07:27
【问题描述】:

我正在使用 MATLAB R2014b 中的GUIDE 设计一个 GUI。我的程序有一个很长的循环(需要 2~5 小时来处理)。我想在我的 GUI 中有一个按钮,用户每次需要时都停止进程(GUI 会根据循环结果不断更新图形和文本)。类似于在结束循环后按 Control+C 之类的东西。我该如何实现?

附言。 我不希望 MATLAB 删除我的工作区。用户可以通过更改 GUI 中的一些选项来使用先前加载的数据和工作区继续该过程。

【问题讨论】:

  • 您好,您是否尝试过以下任何答案?他们对你有用吗?
  • 两个答案都很好:-)。为什么我们不能选择两个答案作为接受:-)
  • 哈哈我不知道 :) 但是你可以选择一个这样线程就会被关闭。谢谢!
  • 基于正面投票:-)
  • 哈哈谢谢你:)

标签: matlab user-interface matlab-guide


【解决方案1】:

这里有一个技巧应该奏效:在 GUI 的某个地方,例如在其 OpeningFcn 中,将一个名为 StopNow 的标志初始化为 false,并将其存储在 GUI 的句柄结构中。然后在需要很长时间执行的循环中,只要将标志设置为true,就放置一个if 语句并调用return。这将停止循环的执行,您将可以访问您的数据。您可以制作一个按钮来更改标志值。

示例代码:我制作了一个简单的 GUI,它开始在 for 循环中枚举数字并将它们打印到文本框中。当您按下 STOP 按钮时,标志设置为 true 并且循环停止。如果有什么不清楚的地方请告诉我。

function StopGUI

clear
clc
close all

%// Create figure and uielements
handles.fig = figure('Position',[440 500 400 150]);

handles.CalcButton = uicontrol('Style','Pushbutton','Position',[60 70 80 40],'String','Calculate','Callback',@CalculateCallback);

handles.StopButton = uicontrol('Style','Pushbutton','Position',[250 70 80 40],'String','STOP','Callback',@StopCallback);

%// Initialize flag
handles.StopNow = false;

handles.Val1Text = uicontrol('Style','Text','Position',[150 100 60 20],'String','Value 1');
handles.Val1Edit = uicontrol('Style','Edit','Position',[150 70 60 20],'String','');



guidata(handles.fig,handles); %// Save handles structure of GUI. IMPORTANT

    function CalculateCallback(~,~)

        %// Retrieve elements from handles structure.
        handles = guidata(handles.fig);


        for k = 1:1000

            if handles.StopNow == false
                set(handles.Val1Edit,'String',num2str(k));
                pause(.5) %// The pause is just so we see the numbers changing in the text box.

            else
         msgbox('Process stopped');
                return
            end

        end

        guidata(handles.fig,handles); %// Save handles structure of GUI.
    end

    function StopCallback(~,~)

        %// Retrieve elements from handles structure.
        handles = guidata(handles.fig);

        handles.StopNow = true;

        guidata(handles.fig,handles); %// Save handles structure of GUI.
    end

end

按下停止按钮后的屏幕截图:

希望有帮助!

【讨论】:

  • 这是有效的,因为在pause matlab 继续处理 HG 回调。如果处理循环中没有pausedrawnow,GUI 将停止(单线程执行模型)。
  • 所以一个循环应该是完整的,之后我们可以停止这个过程吗?我们不能在循环工作的中间停止进程吗?谢谢。
  • 是的,如果你愿意,你可以把它停在中间;试试代码自己看看:)
  • 在上面的例子中,我开始循环并在 2 次迭代后停止它。
  • 我们如何使用 GUI 内部的这个函数来停止一个外部函数(不是在 GUI 内部编写的)?我们如何传递 GUI 句柄并从外部函数接收它们?
【解决方案2】:

最好的解决方案是使用单独的线程(一个用于用户界面,一个用于处理)也许使用并行工具箱。无论如何,这将是相当复杂的同步两者。

所以,这里有一个简单的解决方案,它只依赖于anonymous 函数(在处理块之外委托界面刷新)drawnow 函数(强制图形界面处理其消息)

示例应用程序

要使用的示例应用程序非常基础。它包含:

  • 一个设置面板(只有一个设置,处理块的迭代次数)
  • 进度条
  • 执行/取消按钮

注意:源代码很长(大约 250 行,主要是由于创建 gui),所以我放弃了它here

GUI 创建并不重要。最重要的部分是处理块、检测代码的匿名函数和对 GUI 事件作出反应的回调。因此,我将仅详细介绍这些论文。

处理块

处理块是一个简单的例程:

function [] = processing(count, instrumentation)
%[
    for ki = 1:count,

        instrumentation.CheckCancel();
        instrumentation.Progress((ki-1)/count);

        if (ki > 1), pause(1); end

        instrumentation.CheckCancel();
        instrumentation.Progress(ki/count);

    end
%]
end

唯一的特别之处是它需要一个额外的instrumentation 结构。这个结构有两个字段指向调用者定义的两个anonymous函数(即图形界面)。我们很快就会看到如何。

  • CheckCancel 是负责在用户想要停止处理时引发错误的函数。
  • Progress 是一个委托进度报告的函数。

这里是匿名函数如何连接到图形界面(见代码中的doProcessing子函数):

% Connect instrumentation callbacks with the gui
instrumentation.CheckCancel = @(ratio)onCheckCancel(dlg);
instrumentation.Progress = @(ratio)onProgress(dlg, ratio);

% Perform the processing
processing(settings.NumberOfIterations, instrumentation);

Progress 和 CheckCancel 处理程序

这是 GUI 为Progress 定义的处理程序:

function [] = onProgress(dlg, ratio)
%[
    % Update the progress bar value
    data = guidata(dlg);
    uiprogress(data.handles.pbProgress, ratio);

    % Force interface to refresh
    drawnow();
%]
end

这很简单,它只是更新进度条控件并强制刷新GUI(提醒一下matlab是单线程的,当前正在执行处理块,所以我们需要强制刷新GUI)。

这是CheckCancel 的处理程序:

function [] = onCheckCancel(dlg)
%[
    % Force interface to process its events
    drawnow();

    % Check 'UserData' has not been modified during events processing
    guiState = get(dlg, 'UserData');
    if (~isempty(guiState) && ....
        strcmp(guiState, 'CancelRequested') || strcmp(guiState, 'CloseRequested'))
        error('System:OperationCanceledException', 'Operation canceled');
    end
%]
end

同样,这很简单。我们强制 GUI 处理事件(按钮点击等),然后我们读取 UserData 是否被修改为某个预期值。如果是这样,我们会引发异常以停止处理。再次提醒,当前正在执行的代码是处理块。

GUI 事件处理程序

GUI 只有两个有趣的事件。用户要么单击关闭按钮,要么他/她单击执行/取消按钮。

注意:提醒即使 matlab 在执行处理块时被锁定,GUI 事件仍会被处理,因为处理块不时调用drawnow(通过检测委托)。

这里是关闭按钮的代码:

function [] = onCloseRequest(dlg)
%[
    % If already in computation mode    
    if (~isempty(get(dlg, 'UserData')))
        % Just indicate close is requested and leave immediatly
        set(dlg, 'UserData', 'CloseRequested');
        data = guidata(dlg);
        set(data.handles.btnGoCancel, 'Enable', 'off', 'String', 'Cancelling...');
        return;
    end

    % Immediate close
    delete(dlg);
%]
end

这很简单,如果我们处于运行模式,我们只需发出要停止的信号,否则我们立即关闭对话框。

这是执行/取消按钮的代码:

function [] = onGoCancelClick(dlg)
%[
    % If already in computation mode    
    if (~isempty(get(dlg, 'UserData')))
        % Just indicate cancel is requested and leave immediatly
        set(dlg, 'UserData', 'CancelRequested');
        data = guidata(dlg);
        set(data.handles.btnGoCancel, 'Enable', 'off', 'String', 'Cancelling...');
        return;
   end

    % Go into computation mode
    [settings, err] = tryReadSettings(dlg);
    if (~isempty(err))
        waitfor(msgbox(err.message, 'Invalid settings', 'Error', 'Modal'));
    else
        enterComputationMode(dlg);
        err = doProcessing(dlg, settings);
        leaveComputationMode(dlg, err);
    end
%]
end

有点长,反正都是一样的。如果我们处于运行模式,我们只是表明我们想要停止;否则,界面处于正常模式,我们开始处理。

函数tryReadSettingsenterComputationModeleaveComputationMode 只是用于更新界面中的控件并很好地报告错误或取消。

结论

我们设计了一个只依赖drawnowanonymous 函数的响应式图形界面。这当然只是一个技巧,更好的解决方案是使用多任务处理。

图形界面是在这里以编程方式创建的。如果使用GUIDE 或在GUI Layout toolbox 的帮助下构建,原理是相同的。

可以进一步改进仪器回调,例如,通过添加Log 字段来报告文本框中的处理详细信息或类似于Log4Net 的某些后端(只有消息级别和消息值)。或者通过为中间结果添加回调。

界面也可以改进或修改,例如为什么在修改设置时不运行处理(即停止任何当前运行,而无需每次手动单击“开始/取消”按钮)。

有很多可能性。在这里,只是提供一些地面应用程序开始(或不...)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    • 2016-12-10
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    相关资源
    最近更新 更多