在MATLAB的过程中停止GUI

use*_*243 5 matlab user-interface matlab-guide

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

PS. 我不希望MATLAB删除我的工作区.用户可以通过更改GUI中的某些选项来继续使用先前加载的数据和工作空间的过程.

Ben*_*_11 6

下面是应该工作一招:在某处的GUI,就像在OpeningFcn例如初始化一个名为例如标志StopNowfalse并将其存储在GUI的手柄结构.然后在需要很长时间才能执行的循环中,只要标志设置为,就if调用一个带语句的语句.这将停止循环的执行,您将可以访问您的数据.您可以按下按钮来更改标志值.returntrue

示例代码:我创建了一个简单的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
Run Code Online (Sandbox Code Playgroud)

按STOP按钮后的屏幕截图:

在此输入图像描述

希望有所帮助!


Cit*_*ane 5

最好的解决方案是使用单独的线程(一个用于用户界面,一个用于处理)可能使用并行工具箱.无论如何,这两者同步都会非常复杂.

因此,这是一个简单的解决方案,它只依赖于anonymous函数(委托处理块外部的接口刷新)drawnow函数(强制图形界面处理其消息).

样品申请

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

  • 设置面板(仅有一个设置,处理块的迭代次数)
  • 一个进度条
  • 一个Go/Cancel按钮

注意:源代码很长(大约250行,主要是因为创建了gui),所以我把它丢弃.

ResponsiveGuiInterface

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
Run Code Online (Sandbox Code Playgroud)

关于它的唯一特殊之处在于它需要一个额外的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);
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这很简单,它只是更新进度条控件并强制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
Run Code Online (Sandbox Code Playgroud)

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

GUI事件处理程序

GUI只有两个有趣的事件.用户单击关闭按钮,他/她单击Go/Cancel按钮.

注意:提醒即使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
Run Code Online (Sandbox Code Playgroud)

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

这是go/cancel按钮的代码:

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
Run Code Online (Sandbox Code Playgroud)

这是一个更长的时间,无论如何这是相同的.如果我们处于运行模式,我们只是表明我们想要停止; 否则,界面处于正常模式,我们开始处理.

功能tryReadSettings,enterComputationModeleaveComputationMode只是胶以更新控制在界面和漂亮报告错误或取消.

结论

我们设计了一个负责任的图形界面仅依靠drawnowanonymous功能.这当然只是一个技巧,更好的解决方案是使用多任务处理.

这里以编程方式创建了图形界面.如果使用GUIDE构建或在GUI布局工具箱的帮助下,原则是相同的.

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

界面也可以被改进或修改,例如,为什么不在每次修改设置时运行处理(即只是停止任何当前运行而不需要每次都手动点击'Go/Cancel'按钮).

有很多可能性.在这里,只提供一些基础应用程序(或不...).