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"