Если нужно организовать инициированное пользователем прерывание выполнения по завершению некоторой итерации.Например, каждая итерация цикла может выполняться достаточно долго. Хотелось бы прервать выполнение по завершению очередной итерации. Также (чтобы не выводить на рабочий лист, а затем не удалять номер очередной выполненной итерации) хотелось бы видеть в некотором окне % выполненных вычислений. Для этого можно создать dll с формой, которая отображает процент выполненных вычислений и содержит кнопку, предназначенную для того, чтобы сообщить программе желание пользователя прервать вычисления. Не зная программирования оконных приложений в Windows, это просто сделать в Delphi.
Пусть dll должна содержать:
0) форму с заголовком (caption) Progress,
1) обработчик нажатия на кнопку Interrupt,
2) процедуру создания формы (ProgressCreate),
3) процедуру закрытия формы (ProgressClose),
4) функцию, передающую форме данные для отображения % выполненных вычислений и возвращающую была ли нажата кнопка Interrupt (ProgressIsInterrupted).
(Использование такой dll предполагается таким)
Перед началом цикла вызывается ProgressCreate. После каждой итерации цикла вызывается функция ProgressIsInterrupted и передаётся значение выполненной доли вычислений. Возвращаемое значение проверяется, и если была нажата кнопка Interrupt, то выполняются действия для случая прерывания вычислений.
По завершению цикла вызывается ProgressClose.
Просто разработать такую dll можно следующими шагами.
0. Создадим проект (ProgressDev) приложения с двумя формами. При создании нового приложения будет автоматически создана главная форма проекта Form1. На форму добавим кнопку с обработчиком, запускающим выполнение цикла (имя формы оставим заданное системой, а модуль назовём ProgressRunDev). Добавим в проект ещё одну форму. (Назовём её PrForm, а модуль ProgressForm). Эта вторая форма с модулем и будут основой dll. На форму PrForm добавим Label (для отображения % выполненных вычислений) и кнопку с надписью Interrupt. В модуле ProgressRunDev добавим
uses ProgressForm.
unit ProgressRunDev;
interface
uses
Windows,Forms, StdCtrls, Controls, Classes;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses ProgressForm;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var i : integer;
const N = 10; // число итераций в цикле
begin
ProgressCreate();
for i:= 1 to N
do begin
sleep(1000); // имитирует длительные вычисления
if ProgressIsInterrupted(i/N)
then begin
// действия в случае прерывания вычислений
break;
end;
end;
ProgressClose();
end;
end.
unit ProgressForm;
interface
uses Windows, SysUtils, StdCtrls, Forms, Classes, Controls;
type TPrForm = class(TForm)
Label1: TLabel;
Button: TButton;
procedure ButtonClick(Sender: TObject);
end;
var PrForm: TPrForm;
procedure ProgressCreate(const f:Double=0); stdcall;
procedure ProgressClose; stdcall;
function ProgressIsInterrupted(const f:Double): Boolean; stdcall;
implementation
var
InterruptF: Boolean;
{$R *.DFM}
procedure ProgressCreate;
begin
if not Assigned(PrForm) then PrForm:= TPrForm.Create(nil);
PrForm.Label1.Caption:= FloatToStrF(f*100, ffFixed, 3, 0)+ '% is completed';
PrForm.Show;
InterruptF:= False;
Application.ProcessMessages;
end;
procedure ProgressClose;
begin
PrForm.close;
end;
function ProgressIsInterrupted;
begin
PrForm.Label1.Caption:= FloatToStrF(f*100, ffFixed, 3, 0)+ '% is completed';
ProgressIsInterrupted:= InterruptF;
Application.ProcessMessages;
end;
procedure TPrForm.ButtonClick(Sender: TObject);
begin
InterruptF:= True;
end;
end.
Вид на этапе выполнения показан на рис. (после нажатия на кнопку Form1.Button c надписью Start открылось окно Progress).
Вложение:
Form.PNG [ 4.78 Кб | Просмотров: 2076 ]
1. Создадим dll-проект на базе ProgressForm
library Progress;
uses
ProgressForm;
{$R *.RES}
exports
ProgressCreate,
ProgressClose,
ProgressIsInterrupted;
begin
end.
Затем скомпилируем и получим progress.dll.
(Тестирование dll из приложения разработанного в Delphi)
Можно проект для разработки dll изменить с целью тестирования progress.dll для этого в модуле ProgressRunDev заменим
uses ProgressForm на
Код:
procedure ProgressCreate(f:Double=0); stdcall; external 'Progress.dll';
procedure ProgressClose; stdcall; external 'Progress.dll';
function ProgressIsInterrupted(f:Double=0): Boolean; stdcall; external 'Progress.dll';
Запустив скомпилированное приложение, убедимся, что progress.dll работает.
2. Для вызова функций progress.dll в рабочий лист Maple (расположенный пока в той же директории, что и dll, эта директория должна быть текущей) добавим строки
Код:
> ProgressCreate:= define_external('ProgressCreate', x::float[8], LIB="progress.dll"):
> ProgressClose:= define_external('ProgressClose', LIB="progress.dll"):
> ProgressIsTerminated:= define_external('ProgressIsInterrupted', x::float[8], RETURN::boolean[1], LIB="Progress.dll"):
Несмотря на то что в языке Maple есть «параметры по умолчанию»,
define_external их не поддерживает: нужно передавать 0 функции
ProgressCreate.
Добавим код для тестирования dll
Код:
> with(Threads):
> N:= 10:
ProgressCreate(0);
for i from 0 to N
do
Sleep(1);
if ProgressIsTerminated(evalf(i/N)) then WARNING("computation terminated"); break; end if;
end do;
ProgressClose();
На рис. приведен вид главного окна Maple 15 Classic Worksheet во время выполнения цикла.
Вложение:
ProgressTst.PNG [ 38.08 Кб | Просмотров: 0 ]
3. Вставка в документ строк с define_external может показаться неудобной. Поэтому создадим библиотеку mla (Maple Library Arhive) либ lib (в очень старых версиях). Для этого надо выполнить определения [строчки с define_external] указанных трёх функций (если они ещё не выполнены), а затем создать файл с библиотекой (например в текущей директории) при помощи
> savelib('ProgressCreate', 'ProgressClose', 'ProgressIsTerminated', "Progress.mla");Эту библиотеку можно скопировать в поддиректорию Maple\lib (в которой находится и основная библиотека — maple.mla или maple.lib в очень старых версиях), а dll — в поддиректорию Maple\bin (или \Maple\bin.win, в которой находятся исполняемые файлы maple) и функции библиотеки станут доступны при выполнении любого рабочего листа Maple.
_____________________
Самым большим из очевидных недостатков этого решения является замедленная реакция окна. Например на перемещение. Как исправить без накладных расходов и красиво — не знаю. Если будут предложения, то буду благодарен.
Если есть возможность повесить обработчик на нажатие кнопки Stop (на панели инструментов Maple), то возможно будет лучше. Но как это сделать — не знаю.