2008-09-13 37 views
74

Zawsze zastanawiałem się, jak ludzie aktualizują poprzedni wiersz w linii poleceń. świetnym tego przykładem jest użycie polecenia wget w systemie Linux. Tworzy ASCII załadunku pasek z sortuje, który wygląda tak:Jak animować wiersz poleceń?

[======>                                        ] 37%

i oczywiście loa pasek przesuwania i zmiany procentowe, ale nie tworzy nowej linii. Nie mogę wymyślić, jak to zrobić. Czy ktoś może wskazać mi właściwy kierunek?

Odpowiedz

42

Istnieją dwa sposoby znam, aby to zrobić:

  • użyć znaku Backspace escape („\ b”), aby usunąć linia
  • użyć pakietu curses, jeśli język programowania wyboru ma dla niego powiązania.

Firma Google ujawniła, że ​​ANSI Escape Codes, które wydają się być dobrym sposobem. Dla porównania, tutaj jest to funkcja w C++, aby to zrobić:

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

zakładając, że jest uruchomiony app konsoli Win32 (nie DOS) w najnowszej wersji systemu Windows (tj. 2000+), kody ucieczki ANSI nie będą działać. Jak podano w artykule wikipedii, do którego linkowałeś. –

+0

Możesz używać Ansicon, jeśli pracujesz z ANSI Escape Sequences w systemie Windows. https://github.com/adoxa/ansicon –

56

Jednym ze sposobów, aby to zrobić jest wielokrotnie zaktualizować wiersz tekstu z bieżącego postępu. Np

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

Należy zauważyć, że zamiast stosowanego sys.stdout.writeprint (to Pythona), ponieważ print automatycznie wyświetla „\ r \ n” (wózek zwrotny nowej linii) na końcu każdej linii. Chcę tylko powrotu karetki, który przywraca kursor do początku linii. Ponadto, flush() jest konieczne, ponieważ domyślnie, sys.stdout tylko opróżnia swój wynik po znaku nowej linii (lub po zapełnieniu bufora).

+0

I to samo w "c" z printf i "\ r". –

+0

Co dokładnie jest "płukane" po 'flush()'? Po prostu ciekawy ... – Nearoo

+0

@Nearoo Normalnie stdout buforuje swoje wyjście, dopóki nie zostanie napisany nowy wiersz (\ n). Płukanie powoduje, że linia częściowa pojawia się natychmiast. –

3

PowerShell ma cmdlet Write-Progress, który tworzy pasek postępu w konsoli, który można aktualizować i modyfikować podczas działania skryptu.

0

Jeśli używasz języka skryptowego, możesz użyć polecenia "tput cup", aby to zrobić ... P.S. Jest to sprawa Linux/Unix tylko o ile mi wiadomo ...

2

Jako kontynuacja do Greg's answer, jest to rozszerzona wersja jego funkcji, która umożliwia wyświetlanie komunikatów wieloliniowych; wystarczy przekazać listę lub krotkę łańcuchów, które chcesz wyświetlić/odświeżyć.

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() 

Uwaga: Przetestowałem to tylko za pomocą terminala linuxowego, więc przebieg może się różnić w systemach opartych na systemie Windows.

+0

Windows (7sp1x64): nie działa. – n611x007

+0

@naxa Czy odpowiedź Grega (powyżej) działa dla Ciebie? Najprawdopodobniej jest to problem z charakterem nowej linii. Spróbuj zastąpić "\ n" przez "\ r \ n". – Blaker

+0

Greg robi, więc działa na jednej linii, ale starałem się aktualizować wiadomości w wielu wierszach. :) Zastąpiłem '\ n' na' \ r \ n' w twoim skrypcie, ale nadal nie mogłem go uruchomić w systemie Windows (robisz?). Dostaję '← [A ← [A' po niektórych wiadomościach, podejrzewam, że sekwencja' \ x1b [A'' nie robi tego, co powinna w "cmd.exe". – n611x007

3

Oto odpowiedź na twoje pytanie ... (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") 
4

poniżej jest moja odpowiedź, należy użyć Windows API Consoles(Windows), kodowanie C

/* 
* file: ProgressBarConsole.cpp 
* description: a console progress bar Demo 
* author: lijian <[email protected]> 
* 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; 
} 
+0

Gdzie zdefiniowano "bInfo"? –

13

Sekretem jest drukowanie tylko \ r zamiast \ n lub \ r \ n na linii i.

\ r nazywa powrotu karetki i przemieszcza kursor na początku linii

\ n o nazwie podawania linii i przemieszcza kursor na następnej linii w konsoli. Jeśli użyjesz \ r, nadpisujesz poprzednio zapisaną linię. Więc najpierw napisać wiersz tak:

[   ] 

następnie dodać znak dla każdego kleszcza

\r[=   ] 

\r[==  ] 

... 

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

i tak dalej. Możesz użyć 10 znaków, z których każdy reprezentuje 10%. Ponadto, jeśli chcesz wyświetlać komunikat po zakończeniu, nie zapomnij także dodać wystarczającą ilość białych znaków tak, że zastąpienie poprzednio napisane znaki równości tak:

\r[done  ] 
+1

To działało, całkowicie. W mojej opinii jest to również znacznie prostsze. – Erutan409

Powiązane problemy