コマンドラインをアニメーション化する方法は?


80

私はいつも、コマンドラインの前の行をどのように更新するのか疑問に思っていました。この良い例は、Linuxでwgetコマンドを使用する場合です。これは、次のような種類のASCIIロードバーを作成します。

[======>] 37%

もちろん、ローディングバーが移動し、パーセントが変化しますが、新しい行は作成されません。これを行う方法がわかりません。誰かが私を正しい方向に向けることができますか?

回答:


46

これを行うために私が知っている2つの方法があります:

  • バックスペースエスケープ文字( '\ b')を使用して行を消去します
  • curses選択したプログラミング言語にバインディングがある場合は、パッケージを使用してください。

そしてグーグルはANSIエスケープコードを明らかにしました、それは良い方法であるように見えます。参考までに、これを行うためのC ++の関数を次に示します。

void DrawProgressBar(int len, double percent) {
  cout << "\x1B[2K"; // Erase the entire current line.
  cout << "\x1B[0E"; // Move to the beginning of the current line.
  string progress;
  for (int i = 0; i < len; ++i) {
    if (i < static_cast<int>(len * percent)) {
      progress += "=";
    } else {
      progress += " ";
    }
  }
  cout << "[" << progress << "] " << (static_cast<int>(100 * percent)) << "%";
  flush(cout); // Required.
}

7
彼が最近のバージョンのWindows(つまり、2000以降)でWin32コンソールアプリ(DOSではない)を実行していると仮定すると、ANSIエスケープコードはまったく機能しません。あなたがリンクしたウィキペディアの記事で述べられているように。
ヒューアレン

WindowsでANSIエスケープシーケンスを使用する場合は、Ansiconを使用できます。github.com/adoxa/ansicon
Jens A. Koch

58

これを行う1つの方法は、現在の進行状況でテキスト行を繰り返し更新することです。例えば:

def status(percent):
    sys.stdout.write("%3d%%\r" % percent)
    sys.stdout.flush()

各行の終わりに「\ r \ n」(キャリッジリターン改行)が自動的に出力されるためsys.stdout.writeprint(これはPythonです)の代わりに使用したことに注意してくださいprint。カーソルを行の先頭に戻すキャリッジリターンが必要です。また、flush()デフォルトでsys.stdoutは、改行の後(またはバッファがいっぱいになった後)にのみ出力をフラッシュするため、が必要です。


そして、printfと '\ r'を使用した 'c'でも同じです。
David L Morris

@Nearoo通常、stdoutは、改行(\ n)が書き込まれるまで出力をバッファリングします。フラッシュすると、部分的な線がすぐに表示されます。
Greg Hewgill 2017

21

秘訣は、行のとに\ nまたは\ r \ nではなく\ rのみを出力することです。

\ rはキャリッジリターンと呼ばれ、行の先頭にカーソルを移動します

\ nは改行と呼ばれ、コンソールの次の行にカーソルを移動します。\ rのみを使用する場合は、前に書き込んだ行を上書きします。したがって、最初に次のような行を記述します。

[          ]

次に、ティックごとに記号を追加します

\r[=         ]

\r[==        ]

...

\r[==========]

等々。それぞれが10%を表す10文字を使用できます。また、終了時にメッセージを表示する場合は、次のように以前に記述した等号を上書きするために、十分な数の白い文字も追加することを忘れないでください。

\r[done      ]

1
これは完全に機能しました。私の意見でも、それははるかに簡単です。
Erutan409 2015

4

以下は私の答えです、Windows APIコンソール(Windows)、Cのコーディングを使用してください。

/*
* file: ProgressBarConsole.cpp
* description: a console progress bar Demo
* author: lijian <hustlijian@gmail.com>
* version: 1.0
* date: 2012-12-06
*/
#include <stdio.h>
#include <windows.h>

HANDLE hOut;
CONSOLE_SCREEN_BUFFER_INFO bInfo;
char charProgress[80] = 
    {"================================================================"};
char spaceProgress = ' ';

/*
* show a progress in the [row] line
* row start from 0 to the end
*/
int ProgressBar(char *task, int row, int progress)
{
    char str[100];
    int len, barLen,progressLen;
    COORD crStart, crCurr;
    GetConsoleScreenBufferInfo(hOut, &bInfo);
    crCurr = bInfo.dwCursorPosition; //the old position
    len = bInfo.dwMaximumWindowSize.X;
    barLen = len - 17;//minus the extra char
    progressLen = (int)((progress/100.0)*barLen);
    crStart.X = 0;
    crStart.Y = row;

    sprintf(str,"%-10s[%-.*s>%*c]%3d%%", task,progressLen,charProgress, barLen-progressLen,spaceProgress,50);
#if 0 //use stdand libary
    SetConsoleCursorPosition(hOut, crStart);
    printf("%s\n", str);
#else
    WriteConsoleOutputCharacter(hOut, str, len,crStart,NULL);
#endif
    SetConsoleCursorPosition(hOut, crCurr);
    return 0;
}
int main(int argc, char* argv[])
{
    int i;
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hOut, &bInfo);

    for (i=0;i<100;i++)
    {
        ProgressBar("test", 0, i);
        Sleep(50);
    }

    return 0;
}

どこでbInfo定義されていますか?
トマシュザト-モニカを復活させる2014年


3

これがあなたの質問に対する答えです...(python)

def disp_status(timelapse, timeout):
  if timelapse and timeout:
     percent = 100 * (float(timelapse)/float(timeout))
     sys.stdout.write("progress : ["+"*"*int(percent)+" "*(100-int(percent-1))+"]"+str(percent)+" %")
     sys.stdout.flush()
     stdout.write("\r  \r")

2

グレッグの答えのフォローアップとして、複数行のメッセージを表示できるようにする彼の関数の拡張バージョンを次に示します。表示/更新する文字列のリストまたはタプルを渡すだけです。

def status(msgs):
    assert isinstance(msgs, (list, tuple))

    sys.stdout.write(''.join(msg + '\n' for msg in msgs[:-1]) + msgs[-1] + ('\x1b[A' * (len(msgs) - 1)) + '\r')
    sys.stdout.flush()

注:これはLinuxターミナルを使用してのみテストしたため、Windowsベースのシステムではマイレージが異なる場合があります。


@naxaグレッグの答え(上記)はあなたのために働きますか?改行文字に問題がある可能性があります。'\ n'を '\ r \ n'に置き換えてみてください。
ブレーカー2013

グレッグは機能するので、1行で機能しますが、複数行のメッセージ更新を試みました。:)スクリプトでに置き換え\nまし\r\nたが、それでもWindowsで動作させることができませんでした(そうですか?)。私はなってきた←[A←[A私は疑う、いくつかのメッセージの後に'\x1b[A'シーケンスは、それが何にすべき行いませんcmd.exe
n611x007 2013

1
@naxa '\ x1b [A'は、カーソルを上に向けたANSIエスケープシーケンスであり、コード内の行ブロックの先頭にカーソルをリセットするために使用されます。これをもう少し調べてみると、Win32コンソールはANSIエスケープシーケンスをまったくサポートしていないことわかりました。WindowsのstdoutにANSIサポートを追加するために、ここで説明たソリューションをラップするために、関数にifステートメントを追加することをお勧めします。
ブレーカー2013

0

スクリプト言語を使用している場合は、「tputcup」コマンドを使用してこれを実行できます... PSこれは私が知る限りLinux / Unixのものです...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.