Кодом делится Slava Kostin:
(*Данный юнит содержит описание класса для отображения в консольном окне прогресса выполнения какой-либо длительной операции. (c) Slava Kostin Период с которым будет производиться вывод на экран задается функцией SetPause(), в качестве параметра в нее передается период (в секундах) вывода сообщений на экран. Сообщение может содержать ключевые слова MIN - в эту позицию выводится минимальное значение прогресса MAX - ---''--- максимум CURRENT - ---''--- текущая позиция PROGRESS - ---''--- прогресс (процент завершения) TIME - ---''--- время, в течение которого работает программа LEFTTIME - ---''--- сколько приблизительно осталось работать TOTALTIME - ---''--- общее время работы процесса Все ключевые слова должны предваряться escape-символом, который по умолчанию равен '#', но его можно менять функцией SetEsc(); Формат вывода процентов задается переменной ProgressFmt (как его правильно составить - см. Help Delphi по функции FormatFloat). По умолчанию формат равен '00.00', что означает, что выводится процент завершения с точностью до второго знака. Текущее значение параметра, характеризующего прогресс (см. переменную i в примере) задается функцией SetCurr. Если значение этого параметра меняется при каждой итерации на единицу, можно использовать функцию Inc (не системную, а принадлежащую классу TConsoleProgress). Функция SetCurr возвращает код нажатой клавиши, если пользователем была нажата клавиша, код которой определяется параметром WaitKeys. Это массив из кодов клавиш, нажатие которых необходимо отлавливать. Добавление в список дополнительных кодов клавиш осуществляется функцией AddWaitKey, удаление из списка - DelWaitKey. Пример использования (В КОНСОЛЬНОМ ПРИЛОЖЕНИИ!!!): program ConsoleProgress; {$AppType Console} uses SysUtils, Progress in 'Progress.pas'; const g = 10000000; var p: TConsoleProgress; i: Integer; begin p := TConsoleProgress.Create(0, g, 1, 'Минимум = #MIN, Максимум = #max, Позиция = #CURRENT, Прогресс = #ProGreSs%'); p.AddWaitKey($1B); for i := 0 to g do if p.SetCurr(i) <> 0 then WriteLn('Escape key is pressed!'); p.Free; end. *) unit Progress; interface uses Sysutils, Windows; const DEF_MAX_STR_LEN = 79; //Максимальное число символов в строке const W_Num = 7; //Общее число обрабатываемых команд const W: array [0..W_Num - 1] of String = ( 'MIN', 'MAX', 'CURRENT', 'PROGRESS', 'TIME', 'LEFTTIME', 'TOTALTIME'); type TConsoleProgress = class private LastTime: TDateTime; Current: Integer; //текущее значение Pause: TDateTime; //Пауза между отображением прогресса StartTime: TDateTime; //Время начала тестирования ProgressFmt: String; //Формат команды FormatFloat для вывода процентов ParamCount: Integer; //Сколько параметров в строке Text TextParts: array of String; //Кусочки Text ParamNums: array of Integer; //Номера команд в порядке появления WaitKeys: array of Word; //Коды клавиш, нажатие на которые заставит //функции SetCurr и Inc вернуть значение false; isForce: Boolean; //Форсировать вывод на экран не дожидаясь завершения интервала ожидания Con_Hnd: THandle; //Хэндл консольного окна CCI: TConsoleCursorInfo; //Информация о курсоре function StrToOEM(str: String): String; //Конвертор строк в OEM function KeyHook: Word; procedure CursorON; //Включение курсора procedure CursorOFF; //Выключение курсора public Esc: Char; //Escape-символ, с которого начинаются служебные слова Min, Max: Integer; //Минимальное и максимальное значения TimeFmt, LeftTimeFmt, TotalTimeFmt: String; //Форматы вывода времени работы и оставшегося времени MaxStrLength: Integer; //Максимальная длина строки вывода на экран ClearOnComplete: Boolean; //Очищать ли строку вывода при достижении 100% NewLineOnComplete: Boolean; //Ставить перевод строки при достижении 100% isKeyPressed: Boolean; //Была ли нажата в процессе работы хотя бы одна клавиша PercentDone: Real; //Процент выполнения процесса constructor Create; overload; //Конструктор без параметров (по умолчанию) constructor Create(Max: Integer; Text: String); overload; constructor Create(Min, Max: Integer; Pause: Real; Text: String); overload; procedure Init; //Инициализация, обнуление всех счетчиков procedure SetPause(Pause: Real); procedure SetText(Text: String); procedure AddWaitKey(KeyCode: Word); procedure DelWaitKey(KeyCode: Word); function Inc(Step: Integer): Word; function SetCurr(CurrentPos: Integer): Word; procedure ShowP; procedure ForceShow; //Форсировать вывод на экран end; implementation { TConsoleProgress } constructor TConsoleProgress.Create; begin Min := 0; Max := 0; Pause := EncodeTime(0, 0, 1, 0); Esc := '#'; SetText('Done #PROGRESS%'); ProgressFmt := '00.00'; TimeFmt := 'h:nn:ss'; LeftTimeFmt := 'h:nn:ss'; TotalTimeFmt := 'h:nn:ss'; LastTime := Now; MaxStrLength := DEF_MAX_STR_LEN; GetConsoleCursorInfo(Con_Hnd, CCI); Init; end; constructor TConsoleProgress.Create(Max: Integer; Text: String); begin Min := 0; Self.Max := Max; Pause := EncodeTime(0, 0, 1, 0); Esc := '#'; SetText(Text); ProgressFmt := '00.00'; TimeFmt := 'h:nn:ss'; LeftTimeFmt := 'h:nn:ss'; TotalTimeFmt := 'h:nn:ss'; LastTime := Now; MaxStrLength := DEF_MAX_STR_LEN; Init; end; constructor TConsoleProgress.Create(Min, Max: Integer; Pause: Real; Text: String); begin Self.Min := Min; Self.Max := Max; SetPause(Pause); Esc := '#'; SetText(Text); ProgressFmt := '00.00'; TimeFmt := 'h:nn:ss'; LeftTimeFmt := 'h:nn:ss'; TotalTimeFmt := 'h:nn:ss'; LastTime := Now; MaxStrLength := DEF_MAX_STR_LEN; Init; end; procedure TConsoleProgress.DelWaitKey(KeyCode: Word); var i, j: Integer; begin i := 0; while (i < Length(WaitKeys)) and (WaitKeys[i] <> KeyCode) do System.Inc(i); if i < Length(WaitKeys) then begin for j := i + 1 to Length(WaitKeys) - 1 do WaitKeys[j - 1] := WaitKeys[j]; SetLength(WaitKeys, Length(WaitKeys) - 1); end; end; function TConsoleProgress.Inc(Step: Integer): Word; begin System.Inc(Current, Step); ShowP; Result := KeyHook; end; procedure TConsoleProgress.Init; begin Current := Min; StartTime := Now; isKeyPressed := false; Con_Hnd := GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(Con_Hnd,CCI); CursorOFF; end; function TConsoleProgress.SetCurr(CurrentPos: Integer): Word; begin Current := CurrentPos; ShowP; Result := KeyHook; end; procedure TConsoleProgress.SetPause(Pause: Real); begin Self.Pause := Pause / 24 / 60 / 60; end; procedure TConsoleProgress.SetText(Text: String); var i, p: Integer; begin Text := StrToOEM(Text + Char($0D)); ParamCount := 0; SetLength(TextParts, 1); p := Pos(Esc, Text); if p = 0 then p := Length(Text) - 1; TextParts[0] := Copy(Text, 1, p - 1); while p <= Length(Text) do begin if Text[p] = Esc then begin i := 0; while (i < W_Num) and (UpperCase(Copy(Text, p + 1, Length(W[i]))) <> W[i]) do System.Inc(i); if i < W_Num then begin System.Inc(ParamCount); SetLength(TextParts, ParamCount + 1); SetLength(ParamNums, ParamCount); ParamNums[ParamCount - 1] := i; System.Inc(p, Length(W[i]) + 1); end; end; TextParts[ParamCount] := TextParts[ParamCount] + Text[p]; System.Inc(p); end; end; procedure TConsoleProgress.ShowP; var i: Integer; c: String; begin if ((Now - LastTime) < Pause) and (Current < Max) and not isForce then Exit; c := TextParts[0]; for i := 0 to ParamCount - 1 do begin case ParamNums[i] of 0: //Команда MIN c := c + IntToStr(Min); 1: //Команда MAX c := c + IntToStr(Max); 2: //Команда CURRENT c := c + IntToStr(Current); 3: //Команда PROGRESS begin PercentDone := (Current - Min) / (Max - Min) * 100; if (Max - Min) <> 0 then c := c + FormatFloat(ProgressFmt, PercentDone); end; 4: //Команда TIME c := c + FormatDateTime(TimeFmt, Now - StartTime); 5: //Команда LEFTTIME if (Current - Min) > 0 then c := c + FormatDateTime(LeftTimeFmt, (Now - StartTime) * (Max - Current) / (Current - Min)); 6: //Команда TOTALTIME if (Current - Min) > 0 then c := c + FormatDateTime(TotalTimeFmt, (Now - StartTime) * (Max - Min) / (Current - Min)); end; c := c + TextParts[i + 1]; end; if Length(c) > MaxStrLength then SetLength(c, MaxStrLength); Write(c); if (Current = Max) then begin if ClearOnComplete then begin Write(StringOfChar(' ', DEF_MAX_STR_LEN)); if not NewLineOnComplete then Write(Char($0D)); end; if NewLineOnComplete then WriteLn; CursorON; end; LastTime := Now; end; function TConsoleProgress.StrToOEM(str: String): String; begin Result := str; CharToOEM(PChar(Result), PChar(Result)); end; procedure TConsoleProgress.AddWaitKey(KeyCode: Word); begin SetLength(WaitKeys, Length(WaitKeys) + 1); WaitKeys[Length(WaitKeys) - 1] := KeyCode; end; function TConsoleProgress.KeyHook: Word; var i: Integer; ks: TKeyboardState; begin Result := 0; for i := 0 to Length(WaitKeys) - 1 do if (GetKeyState(WaitKeys[i]) and $80) = $80 then begin Result := WaitKeys[i]; ks[Result] := 0; SetKeyboardState(ks); isKeyPressed := true; Exit; end; end; procedure TConsoleProgress.ForceShow; begin isForce := true; ShowP; end; procedure TConsoleProgress.CursorOFF; begin CCI.bVisible := false; SetConsoleCursorInfo(Con_Hnd, CCI); end; procedure TConsoleProgress.CursorON; begin CCI.bVisible := true; SetConsoleCursorInfo(Con_Hnd, CCI); end; end. |
[001945]