如何使用Delphi在控制台应用程序中显示进度条?

时间:2017-11-10 20:21:40

标签: delphi console

我想在漫长的过程中在我的控制台应用程序中显示某种动画,并且不知道如何做到这一点。

我已经做过研究,但我找到的解决方案并没有引起我的兴趣,或者我不乐意理解它们。

我的应用程序加载一个文本文件,并通过搜索要替换的单词逐个遍历所有行。

它可以是进度条或任何循环动画。

3 个答案:

答案 0 :(得分:11)

这是一个示例,它将在循环中生成消息处理X的Y(Z%)... ,其延迟表示在循环中执行某些操作所花费的时间。显然,这是一个人为的例子,但它显示了一种可能性。 (显然,你会用一个有意义的值替换循环上限值和消息中的Y,例如TStringList.Count。)

program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  i: Integer;
  StopValue, Pct: Integer;

(* 
  #13 is a carriage return, which moves the cursor back to the
  left side of the console without adding a line feed (#10). It
  allows writing on the same line over the same content without
  moving to the next line. See the demo output.
*)
const
  StatusMsg = #13'Processing %d of %d (%d%%) ...';

begin
  StopValue := 150;     // Replace with your upper limit, e.g. StringList.Count
  for i := 1 to StopValue do
  begin
    Pct := Trunc((i * 1.0 / StopValue) * 100);
    Write(Format(StatusMsg, [i, StopValue, Pct]));
    (****************************************************************
      Note: The call to Sleep here is only to introduce an artificial
      delay in the loop in order to allow the progress to be seen. 
      Otherwise, the empty loop runs so fast that it's not clear when
      the progress increments are shown.

      Clearly, you would replace the call to Sleep with your code to
      actually do some work, such as processing each line of the text
      file.

      Explained in detail for clarity, as some commenters have indicated
      they're not capable of understanding why a call to Sleep is used
      here, so adding this unnecessarily large comment is needed for them.
    ****************************************************************)
    Sleep(250);
  end;
  Write(#13'Processing complete. Press Enter to quit.');
  ReadLn;
end.

进度指示器快照

enter image description here

答案 1 :(得分:4)

很久很久以前;一个曾经与大型机通信,管子显示绿色字母,类型为VT100。自那时以来发生了很多变化,除了使用命令行界面的程序实际上仍然与(虚拟)VT100的后端进行通信。由于严重基于ASCII代码,这32个最低值对于控制在屏幕上显示的位置非常重要。这就是为什么一条新线仍然在某些环境中被编码为#13的原因,它会进行回车运输。在远程打字机上,以及#10,它可以将链式送纸纸送到一条线上。 VT100通过修改位置来模拟相同的内容以显示下一个接收到的角色。

因此,要在控制台窗口中覆盖屏幕上的内容,只需发送#13和新数据即可。例如:

procedure ShowProgress(Position,Max:integer);
var
  i,j:integer;
const
  BarWidth=28; //must be less than 79
begin
  Write(#13'[');
  j:=Position*BarWidth div Max;
  for i:=1 to BarWidth do
    if i<=j then Write('#') else Write('-');
  Write(']');
end;

WriteLn;(实际为Write(#13#10);)或以#13为前缀的其他内容结束,并且足以覆盖整个条形码。

答案 2 :(得分:2)

试试这个控制台应用程序:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Windows;


procedure ClearScreen;
var
  stdout: THandle;
  csbi: TConsoleScreenBufferInfo;
  ConsoleSize: DWORD;
  NumWritten: DWORD;
  Origin: TCoord;
begin
  stdout := GetStdHandle(STD_OUTPUT_HANDLE);
  Win32Check(stdout<>INVALID_HANDLE_VALUE);
  Win32Check(GetConsoleScreenBufferInfo(stdout, csbi));
  ConsoleSize := csbi.dwSize.X * csbi.dwSize.Y;
  Origin.X := 0;
  Origin.Y := 0;
  Win32Check(FillConsoleOutputCharacter(stdout, ' ', ConsoleSize, Origin,
    NumWritten));
  Win32Check(FillConsoleOutputAttribute(stdout, csbi.wAttributes, ConsoleSize, Origin,
    NumWritten));
  Win32Check(SetConsoleCursorPosition(stdout, Origin));
end;

var
  iFileLineCount : Integer;
  iCounter1,iCounter2 : Integer;
  iPercent : Integer;
begin


  try
    iFileLineCount := 12000;
    for iCounter1 := 1 to iFileLineCount do
      begin

        //do your application thing like reading file
        ClearScreen;
        iPercent :=  iCounter1 * 100 div iFileLineCount;
        for iCounter2 := 1 to iPercent do
          write('|');
        write(IntToStr(iPercent) + '%');
      end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;



end.

这是在dos等操作系统的控制台应用程序中显示进度条的老方法,当然有很多更好的方法,但它只是工作。 德里不支持CRT工具,所以我添加Procedure用于清晰屏幕。