0

I have created a GUI which computes a trajectory based on an external .m file.

main-gui

When the user press the 'Calculate' button, the external .m function file gets called via the callback function of the button:

% calculateBtn button pushed function
function calculate(app)
    numSteps = app.stepSlider.Value;

    app.omega = app.omegaSpin.Value;
    app.phid = app.phi.Value;

    app.x0 = app.x0Spin.Value;
    app.y0 = app.y0Spin.Value;

    app.u0 = app.u0Spin.Value;
    app.v0 = app.v0Spin.Value;

    set(app.calculateBtn, 'Enable', 'off')
    set(app.showBtn, 'Enable', 'off')

    [app.Xc, app.Xi, app.C, T, f]=coriolis_traj(app.x0, app.y0, app.u0, app.v0, app.phid, numSteps);

    app.fEdit.Value = num2str(f);
    app.tEdit.Value = num2str(T);

    set(app.calculateBtn, 'Enable', 'on')

    if length(app.Xc)>1
        set(app.showBtn, 'Enable', 'on')
    else
        set(app.showBtn, 'Enable', 'off')
    end
end

The external file consists of the main loop of the computations.

    while 1
    % Counters
    i = i + 1;
    t = t + Dt;
    theta = theta + Omega * Dt;

    % Parcel's position
    % on the inertial frame
    x1 = x0 + Dt*u0;
    y1 = y0 + Dt*v0;

    % Parcel's position translated to the
    % rotating frame
    xc1 = x1*cos(theta)+y1*sin(theta);
    yc1 = x1*sin(theta)+y1*cos(theta);

    x(i) = x1 ; y(i) = y1;
    xc(i) = xc1 ; yc(i) = yc1;

    x0 = x1 ; y0 = y1;

    [in] = inpolygon(xc,yc,xv,yv);
    if ~in(i) > 0
        break;
    end
end

I want to stop the computation and clear the computed arrays when the user changes any of the values in 'Controls' panel, or when the button 'Break' is pushed.

How could I code this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nick
  • 37
  • 11
  • 1
    Look at the [interruptible attribute of callbacks](https://uk.mathworks.com/help/matlab/ref/matlab.ui.figureappd-properties.html#property_d119e1254871) (in partnership with the BusyAction property) – Wolfie Feb 27 '18 at 18:42
  • @Wolfie I cannot find a way I could use the interruptible property on my callback. I don't know which way I could implement this. – nick Feb 27 '18 at 18:54

2 Answers2

0

The best solution I can figure out is to bring your while loop inside your GUI callback. The inner code of your while loop can be kept on a separate, external file, but bringing in the loop will give you full control over it and will make it easier to interrupt. The only constraint is that it must be make less "tight"... it must contain a small pause (better if followed by a drawnow() call) so that the main GUI thread can have the time to process the application messages.

And here is the code of your callbacks:

% Computation Callback
function Button1_Callback(obj,evd,handles)
    obj.Enable = 'off';            % disable this button
    handles.Button2.Enable = 'on'; % enable the interruption button
    handles.stop = false;          % the control variable for interruption

    while (true)
        % call your external function for running a computational cycle
        res = myExternalFunction(...);

        % refresh the application handles
        handles = guidata(obj);

        % interrupt the loop if the variable has changed
        if (handles.stop)
            break;
        end

        % this allows the loop to be interrupted
        pause(0.01);
        drawnow();
    end

    obj.Enable = 'on';
end

% Interruption Callback
function Button2_Callback(obj,evd,handles)
    obj.Enable = 'off';            % disable this button

    handles = guidata(obj);
    handles.stop = true;
end
Tommaso Belluzzo
  • 23,232
  • 8
  • 74
  • 98
0

There is no way in MATLAB to interrupt a function by another function, e.g. say programmatically injecting a CTRL-C into another running function, without modifying the to-be-interrupted function.

The closest you can get is by modifying the simulator code to regularly perform a callback. This is how I integrated by simulation code into a GUI. IMO it is a clean solution and also works with MEX files.

Think of it as a progress callback for your simulator code.

It could be regularly (e.g. every second, or at completion of the i-th step) called by the simulator with some percentage parameter to indicate the degree of completion.

If you modify the simulator code to stop simulating in case the callback returns false, you achieve what you want. At the same time this is minimum invasive in your simulator code. Supply a dummy callback and it will run stand alone.

GUI pseudocode:

function button_cancel_Callback(hObject, eventdata, handles)
global cancel_pressed;
cancel_pressed = true;

function dostop = callback( progress, handles )
<show progress>( handles.<XXX>, progress );
global cancel_pressed;
if cancel_pressed
   dostop = true;
else
   dostop = false;
end

function button_run_simulation_Callback(hObject, eventdata, handles)
global cancel_pressed;
cancel_pressed = false;

<simulator function>( ..., @callback, handles )

SIMULATOR pseudocode:

function <simulator function>( ..., callback, callback_param )
while ...  % main loop
  if ~isempty(callback) && ~callback( progress, callback_param )
      error("canceled-by-user") % could also be something more elaborate
end
return "simulation completed"
Andreas H.
  • 5,557
  • 23
  • 32