Stop GUI in the middle of a process in MATLAB

I am designing a GUI using GUIDE in MATLAB R2014b. My program has a long cycle (it takes 2 ~ 5 hours to process). I want to have a button in my GUI so that the user stops the process every time he wants (the GUI constantly updates graphics and texts based on the results of cycles). Something like pressing Control+C not after the loop ends. How to implement this?

PS. I do not want MATLAB to delete my workspace. The user can continue the process with the previously loaded data and the workspace by changing some parameters in the graphical interface.

+5
source share
2 answers

Here's the trick that should work: somewhere in the graphical interface, for example, in its OpeningFcn , initialize a flag with a name, for example, StopNow - false , and save it in the structure of the graphical interface handles. Then, in a loop that takes a long time to execute, put an if with a call to return whenever the flag is set to true . This will stop the loop and you will have access to your data. You can click the button to change the value of the flag.

Code example: I made a simple graphical interface that starts listing the numbers in a for loop and prints them in a text box. When you press the STOP button, the flag is true and the loop stops. If something is unclear, tell me.

 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 

Screenshot after pressing the STOP button:

enter image description here

Hope this helps!

+6
source

A better solution would be to use separate threads (one for the user interface and one for processing), possibly using a parallel toolbar. In any case, it would be rather difficult to synchronize both.

So, here is a simple solution that relies only on anonymous functions (to delegate an interface that is refreshing outside the processing unit) and on a drawnow function (to make the GUI process its messages).

Application example

The sample application for working with it is very simple. He contains:

  • Settings panel (with only one setting, the number of iterations for the processing unit)
  • Progress bar
  • Go / Cancel Button

NB: The source code is quite long (about 250 lines, mainly due to the creation of gui), so I omitted it here .

ResponsiveGuiInterface

Creating a GUI is not important. The most important parts are the processing unit, anonymous functions that allow code and callbacks to respond to GUI events. Thus, I will describe theses in detail.

Processing unit

The processing block is a simple procedure:

 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 

The only feature in this is that it requires an additional instrumentation structure. This structure has two fields that point to two anonymous functions defined by the caller (i.e. a graphical interface). We will soon see how.

  • CheckCancel is the function responsible for raising the error if the user wants to stop processing.
  • Progress is a feature that can be used to delegate a progress report.

This is how anonymous functions are connected to the graphical interface (see the doProcessing subfunction in the code):

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

Handlers of progress and verification.

Here is the graphical interface handler for Progress :

 function [] = onProgress(dlg, ratio) %[ % Update the progress bar value data = guidata(dlg); uiprogress(data.handles.pbProgress, ratio); % Force interface to refresh drawnow(); %] end 

It's simple, it just updates the progressbar control and forces the GUI to be updated (recall that Matlab is single-threaded and is currently executing a processing unit, so we need to force the GUI to be updated).

Here is the handler for 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 

Again, this is pretty simple. We force the GUI to handle events (button clicks, etc.), and then we read if UserData been changed to some expected value. If so, we throw an exception to stop processing. Remind again that the current executable code is a processing unit.

GUI Event Handlers

There are only two interesting events in the graphical interface. Either the user clicks the close button, or he clicks the "Go / Cancel" button.

Note. We remind you that even if Matlab is blocked during the execution of the processing unit, GUI events are still processed, since the processing unit occasionally causes drawnow (based on the average value of the toolkit delegates).

Here is the close button code:

 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 

It is simple, if we are in the operating mode, we simply signal that we want to stop, otherwise we will immediately close the dialog box.

Here is the go / cancel button code:

 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 

This is a little longer, at least it is one and the same. If we are in operating mode, we simply indicate that we want to stop; otherwise, the interface is in normal mode, and we begin processing.

The functions tryReadSettings , enterComputationMode and leaveComputationMode are just adhesives for updating controls in the interface and for a pleasant report of errors or cancellations.

Conclusion

We have developed a flexible graphical interface that relies only on drawnow and anonymous . This, of course, is just a trick, and the best solution would be to use multitasking.

The graphical interface was created here programmatically. The principle is the same if you build with GUIDE or using the GUI Toolbar .

Tool callbacks can be further improved, for example by adding a Log field to report processing data to a text field or to some end similar to Log4Net (only with message level and message value). Or by adding a callback for intermediate results.

The interface can also be improved or changed, for example, why not start processing whenever the parameter has been changed (that is, just stop any current start and without having to manually press the "Go / Cancel" button each time).

There are many possibilities. Here, just providing some ground-based applications to get you started (or not ...).

+5
source

Source: https://habr.com/ru/post/1212792/


All Articles