3

I need to wait for a process to finish with basically unknown duration. I call this process via:

output = system('foo.cmd')

Process foo is blocking any further execution of code. The process takes often about 10 seconds to finish and does not return any status but its output value afterwards. Sometimes it takes a second, sometimes a minute or more. To simulate the progress to the user I would like to implement a waitbar in advance of the excution, somehow like:

function f = my_waitbar()
    f = waitbar(0,'Please wait...');

    for i=1:10
    pause(1)
        waitbar(i/10,f,'Executing foo');
    end
    waitbar(1,f,'Finishing, please wait...'); % should wait now and do nothing
end

Then after foo.cmd finished I would close the bar.

f = my_waitbar() % blocks my code from execution, therefore is pointless
output = system('foo.cmd')
close(f)

But by just calling function f = my_waitbar() in advance, the process would not be triggered parallel. How do I execute my_waitbar() without waiting for it to be finished?

Note that everything after system('foo.cmd') is depending on its execution, therefore further code execution has to wait for it as it does now, so system('foo.cmd &') would not work well for me.

EDIT

Please assume that I have no influence on the content of foo.cmd

6
  • 1
    Do you have control over the process "foo.cmd" ? If you can make it create a file (even empty) when it is finished, then there are ways for MATLAB to monitor that without blocking the thread. Or turning the question around, does the process already create or delete any file while running ? Commented Nov 22, 2019 at 13:17
  • "foo.cmd" triggers an process not running on my device, and I have no control over it. It basically sends a previously created json to a custom webservice, and receives a json as answer when finished. No status in between (That's my issue). No files are created, I get a simple json with some values as output or an error mesage if something failed. Commented Nov 22, 2019 at 14:12
  • But foo.cmd is a script running on your computer. It could potentially be edited to write a file, as @Hoki suggests, so that your MATLAB process has something to look for. Commented Nov 22, 2019 at 14:23
  • This would just move my problem from MATLAB to the script. Please assume that I have no influence on the content of foo.cmd. Commented Nov 22, 2019 at 16:01
  • 1
    Do you really need an external script? Could you not send the request and receive the json directly in MATLAB? Commented Nov 22, 2019 at 16:17

2 Answers 2

3

It's not a great UX choice to have a percentage-based progress bar which may "complete" long before your process does, or never complete because your process finishes sooner. See this question on UX.stackexchange which discusses the alternatives. In short, a spinner is more conventional.

You can create a custom load spinner fairly easily. This uses an undocumented but simple method to display an animated gif within a figure without having to use code to advance the frames (which would not work for an asynchronous task!).

There are several options for animated gifs already shipped with MATLAB, I've chosen to use this one:

loader

Here is the code and the result (note that in reality the result is animated!)

function hFig = loadspinner()    
    % Main path for installed MATLAB images (and other files)
    fp = fullfile(matlabroot,'toolbox','matlab');
    % Path to the image we want, there are other options in this folder...
    fp = fullfile(fp, 'sourcecontrol\release\images\spinner.gif');

    % Create the figure    
    hFig = figure('Color', 'w', 'NumberTitle', 'Off', ...
                  'Resize', 'Off', 'Menubar', 'None');
    % Get image size to reduce hard-coding in case we change image
    sz = size( imread( fp ) );
    % Insert the animated gif into a HTML pane to enable the animation
    je = javax.swing.JEditorPane('text/html', ['<html><img src="file:/', fp, '"/></html>']);
    [~, hc] =  javacomponent(je,[],hFig);
    % resize figure and image
    hFig.Position(3:4) = [220,sz(2)+35];
    set(hc, 'pos', [(220-sz(1))/2-2,6,sz(1)+4,sz(2)+4])
    % Add text
   annotation( hFig, 'textbox', [0,0.9,1,0], ...
                'String', 'Loading, please wait...', ...  
                'LineStyle','none', ...
                'margin', 0, ...
                'verticalalignment', 'top', ...
                'horizontalalignment', 'center' );
end

result

You could use this the same as you showed for the waitbar

f = loadspinner();
output = system('foo.cmd')
close(f);
Sign up to request clarification or add additional context in comments.

2 Comments

Nice trick. You could try it with that gif too: loader.gif
@Hoki my original answer made the gif optional, that one is nice, as is a blue circle spinner (can't remember the name). I simplified the code before posting though!
3

As Wolfie already mentionned, a standard progressbar is not suited to wait for a process with unknown duration (or unknown iterations). In these cases you better use a spinner (gif file), a circular waitbar (good choice on the File Exchange: cProgress), or an indefinite progressbar. This is what I used for this example:

enter image description here


Now how to make it possible. Since I cannot replicate your exact process foo.cmd, I replaced it by the dos command dir. So for the base line example:

tic
command = 'dir' ;
[~,cmdout] = system(command) ;
toc

>> Elapsed time is 1.547987 seconds.

1.5 second is enough to notice, and cmdout does indeed contain the list of files and directories. So I'll assume this is as close as your case than can be.

To be able to monitor the end of the process, I will package the call to dir (or in your case the call to foo.cmd) into a batch file which will:

  • Check if a file ("MyProcessIsFinished") exist. If yes delete it.
  • Call the process of interest, redirect the output to a file.
  • Create an empty file "MyProcessIsFinished"

This allow MATLAB to call the batch file without waiting for the result (using &). You then make MATLAB wait until it detects the file. When detected, you know the process is finished, you can close your waitbar and continue execution of your code. You can then read the file containing the results you used to get in cmdout.

% initialise flag
processFinished = false ; 

% start the continuous waitbar
hw = mywaitbar(0.5,'Please wait','Waiting for response ...',true);

% call the process in the batch file, without waiting for result
command = 'mycommand.bat &' ;
system(command) ;

% wait for the process to be finished
while ~processFinished
    if exist('MyProcessIsFinished','file')
        processFinished = true ;
    end
end
close(hw) ; % close the wait bar

% now read your results
cmdout = fileread('outputfile.txt') ;

The file mycommand.bat is now:

@echo off
if exist MyProcessIsFinished del MyProcessIsFinished
dir > outputfile.txt
copy nul MyProcessIsFinished > nul
exit

Don't forget to replace the line dir > outputfile.txt by a call to your process and a redirection to a suitable file name. It could look like:

foo.cmd > ReceivedRequest.json

The continuous waitbar: I picked up mywaitbar.m from the file exchange: mywaitbar. The code is nice but I had to change a couple of things to improve the timer management so if you want a working version there will be a couple of changes:

  • Line 109: Add 'CloseRequestFcn',@closeRequestFcn to the properties of the new figure.
  • Line 120: Add 'Name','CircularWaitbarTimer' to the properties of the new timer

Then at the bottom of the file, add the following function:

function closeRequestFcn(hobj,~)
    % Delete the timer
    t = timerfindall('Name','CircularWaitbarTimer') ;
    if strcmpi('on',t.Running)
        stop(t) ;
    end
    delete(t)
    delete(hobj)

That will make a more stable waitbar utility and will get rid of the annoying warning/error messages about timers not managed properly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.