在 MATLAB 的进程中间停止 GUI
Stop A GUI in a middle of process in MATLAB
我正在使用 MATLAB R2014b 中的 GUIDE
设计 GUI。我的程序有一个很长的循环(处理需要 2~5 小时)。我想在我的 GUI 中有一个按钮,用户每次 he/she 想要时停止进程(GUI 正在根据循环结果不断更新图形和文本)。类似于在结束循环后不按 Control+C
之类的东西。我该如何实施?
PS.
我不希望 MATLAB 删除我的工作区。用户可以通过更改 GUI 中的一些选项来继续使用之前加载的数据和工作区的过程。
这里有一个应该起作用的技巧:在 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
按下停止按钮后的屏幕截图:
希望对您有所帮助!
最好的解决方案是使用单独的线程(一个用于用户界面,一个用于处理) 也许使用并行工具箱。无论如何,这将非常复杂地同步两者。
所以,这是一个简单的解决方案,只依赖于 anonymous
functions (to delegate interface refreshing outside the processing block) and on drawnow
函数 (强制图形界面处理其消息).
示例应用程序
要使用的示例应用程序非常基础。它包含:
- 一个设置面板(只有一个设置,处理块的迭代次数)
- 一个进度条
- 一个Go/Cancel按钮
注意:源代码比较长(大约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);
进度和 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
这个很简单,就是更新progressbar控件,强制刷新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 只有两个有趣的事件。要么用户单击关闭按钮,要么 he/she 单击 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
这很简单,如果我们处于 运行ning 模式,我们只是发出我们想要停止的信号,否则我们会立即关闭对话框。
这是 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
有点长了,反正这个是一样的。如果我们处于 运行ning 模式,我们只是表示我们想要停止;否则,界面是普通模式,我们开始处理。
函数 tryReadSettings
、enterComputationMode
和 leaveComputationMode
只是用来更新界面中的控件并很好地报告错误或取消。
结论
我们设计了一个仅依赖drawnow
和anonymous
函数的响应式图形界面。这当然只是一个技巧,更好的解决方案是使用多任务处理。
图形界面是在此处以编程方式创建的。如果用 GUIDE or with the help of the GUI Layout toolbox.
构建,原理是一样的
检测回调可以进一步改进,例如,通过添加一个 Log
字段来报告文本框中的处理细节或一些类似于 Log4Net 的后端(只有消息级别和消息值)。或者通过为中间结果添加回调。
界面也可以改进或修改,例如为什么不在修改设置时 运行 处理(即停止任何当前 运行 而无需手动单击 'Go/Cancel' 按钮每次)。
有很多可能性。在这里,只是提供一些地面应用程序开始(或不...)。
我正在使用 MATLAB R2014b 中的 GUIDE
设计 GUI。我的程序有一个很长的循环(处理需要 2~5 小时)。我想在我的 GUI 中有一个按钮,用户每次 he/she 想要时停止进程(GUI 正在根据循环结果不断更新图形和文本)。类似于在结束循环后不按 Control+C
之类的东西。我该如何实施?
PS. 我不希望 MATLAB 删除我的工作区。用户可以通过更改 GUI 中的一些选项来继续使用之前加载的数据和工作区的过程。
这里有一个应该起作用的技巧:在 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
按下停止按钮后的屏幕截图:
希望对您有所帮助!
最好的解决方案是使用单独的线程(一个用于用户界面,一个用于处理) 也许使用并行工具箱。无论如何,这将非常复杂地同步两者。
所以,这是一个简单的解决方案,只依赖于 anonymous
functions (to delegate interface refreshing outside the processing block) and on drawnow
函数 (强制图形界面处理其消息).
示例应用程序
要使用的示例应用程序非常基础。它包含:
- 一个设置面板(只有一个设置,处理块的迭代次数)
- 一个进度条
- 一个Go/Cancel按钮
注意:源代码比较长(大约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);
进度和 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
这个很简单,就是更新progressbar控件,强制刷新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 只有两个有趣的事件。要么用户单击关闭按钮,要么 he/she 单击 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
这很简单,如果我们处于 运行ning 模式,我们只是发出我们想要停止的信号,否则我们会立即关闭对话框。
这是 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
有点长了,反正这个是一样的。如果我们处于 运行ning 模式,我们只是表示我们想要停止;否则,界面是普通模式,我们开始处理。
函数 tryReadSettings
、enterComputationMode
和 leaveComputationMode
只是用来更新界面中的控件并很好地报告错误或取消。
结论
我们设计了一个仅依赖drawnow
和anonymous
函数的响应式图形界面。这当然只是一个技巧,更好的解决方案是使用多任务处理。
图形界面是在此处以编程方式创建的。如果用 GUIDE or with the help of the GUI Layout toolbox.
构建,原理是一样的检测回调可以进一步改进,例如,通过添加一个 Log
字段来报告文本框中的处理细节或一些类似于 Log4Net 的后端(只有消息级别和消息值)。或者通过为中间结果添加回调。
界面也可以改进或修改,例如为什么不在修改设置时 运行 处理(即停止任何当前 运行 而无需手动单击 'Go/Cancel' 按钮每次)。
有很多可能性。在这里,只是提供一些地面应用程序开始(或不...)。