Matlab - AppDesigner: прерывание цикла с графическим интерфейсом

Я создал графический интерфейс, который вычисляет траекторию на основе внешнего файла.m.

Основной-графический интерфейс

Когда пользователь нажимает кнопку "Рассчитать", внешний файл функции.m вызывается через функцию обратного вызова кнопки:

% 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

Внешний файл состоит из основного цикла вычислений.

    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

Я хочу остановить вычисление и очистить вычисленные массивы, когда пользователь изменяет любое из значений на панели "Элементы управления" или когда нажата кнопка "Разрыв".

Любые идеи о том, как я мог бы написать это?

1 ответ

Лучшее решение, которое я могу найти, это принести while Цикл внутри вашего обратного вызова GUI. Внутренний код вашего while Цикл может храниться в отдельном внешнем файле, но включение цикла даст вам полный контроль над ним и упростит прерывание. Единственным ограничением является то, что он должен быть менее "плотным"... он должен содержать небольшой pause (лучше, если после drawnow() вызов), чтобы у основного потока GUI было время для обработки сообщений приложения.

А вот код ваших обратных вызовов:

% 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

В MATLAB нет способа прервать функцию другой функцией, например, программно внедрить CTRL-C в другую работающую функцию, не изменяя функцию, которая должна быть прервана.

Самое близкое, что вы можете получить, это изменить код симулятора для регулярного выполнения обратного вызова. Вот как я интегрировал код моделирования в графический интерфейс. ИМО это чистое решение, а также работает с файлами MEX.

Думайте об этом как обратный вызов прогресса для вашего кода симулятора.

Он может регулярно (например, каждую секунду или по завершении i-го шага) вызываться симулятором с некоторым процентным параметром для указания степени завершения.

Если вы измените код симулятора, чтобы прекратить симуляцию в случае, если обратный вызов вернет false, вы добьетесь того, чего хотите. В то же время это минимально инвазивный код вашего симулятора. Поставьте фиктивный обратный звонок, и он будет работать самостоятельно.

Псевдокод GUI:

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:

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"
Другие вопросы по тегам