Zastąp dane wyjściowe konsoli w Pythonie

106

Zastanawiam się, jak mógłbym stworzyć jeden z tych sprytnych liczników konsoli w Pythonie, jak w niektórych programach C / C ++.

Mam pętlę wykonującą różne czynności, a bieżące dane wyjściowe są następujące:

Doing thing 0
Doing thing 1
Doing thing 2
...

co byłoby ładniejsze, to po prostu zaktualizować ostatnią linię;

X things done.

Widziałem to w wielu programach konsolowych i zastanawiam się, czy / jak bym to zrobił w Pythonie.

dutt
źródło
3
Powinieneś spojrzeć na przekleństwa .
Björn Pollex
2
Po prostu użyj print: stackoverflow.com/a/8436827/1959808
Ioannis Filippidis Kwietnia
1
@ BjörnPollex, cursesto przesada (zobacz zaakceptowaną odpowiedź).
Alexey
Powiązane: Pasek postępu tekstu w konsoli .
Alexey

Odpowiedzi:

151

Łatwym rozwiązaniem jest po prostu wpisanie go "\r"przed łańcuchem bez dodawania nowej linii; jeśli sznurek nigdy się nie skróci, to wystarczy ...

sys.stdout.write("\rDoing thing %i" % i)
sys.stdout.flush()

Nieco bardziej wyrafinowany jest pasek postępu ... to jest coś, czego używam:

def startProgress(title):
    global progress_x
    sys.stdout.write(title + ": [" + "-"*40 + "]" + chr(8)*41)
    sys.stdout.flush()
    progress_x = 0

def progress(x):
    global progress_x
    x = int(x * 40 // 100)
    sys.stdout.write("#" * (x - progress_x))
    sys.stdout.flush()
    progress_x = x

def endProgress():
    sys.stdout.write("#" * (40 - progress_x) + "]\n")
    sys.stdout.flush()

Wołasz startProgressprzekazanie opisu operacji, a następnie progress(x)gdzie xjest procent i na końcuendProgress()

6502
źródło
2
Co jeśli sznurek jest krótszy niż poprzedni?
math2001
6
@ math2001 dopełnienie spacjami.
felipsmartins
Zagłosowano tylko na pierwsze 2 wiersze kodu. W kilku przypadkach pasek postępu zwalnia. W każdym razie dzięki @ 6502
WaterRocket8236
Niektóre programy ( restic, flatpak) mogą aktualizować kilka wierszy danych wyjściowych konsoli. Czy przypadkiem wiesz, jak można to osiągnąć?
Alexey
1
@Alexey: możesz używać kodów ucieczki ANSI, aby przesuwać kursor, czyścić części ekranu i zmieniać kolory ... patrz en.wikipedia.org/wiki/ANSI_escape_code
6502
39

Bardziej eleganckim rozwiązaniem mogłoby być:

def progressBar(current, total, barLength = 20):
    percent = float(current) * 100 / total
    arrow   = '-' * int(percent/100 * barLength - 1) + '>'
    spaces  = ' ' * (barLength - len(arrow))

    print('Progress: [%s%s] %d %%' % (arrow, spaces, percent), end='\r')

wywołaj tę funkcję z valuei endvalue, wynik powinien być

Progress: [------------->      ] 69 %

Uwaga: Python 2.x wersja tutaj .

Aravind Voggu
źródło
Powinieneś używać Halo, aby uzyskać lepsze paski postępu i obrotówki.
Aravind Voggu
18

W Pythonie 3 możesz to zrobić, aby drukować w tej samej linii:

print('', end='\r')

Szczególnie przydatne do śledzenia najnowszych aktualizacji i postępów.

Poleciłbym również tqdm stąd, jeśli ktoś chce zobaczyć postęp pętli. Wyświetla bieżącą iterację i całkowitą liczbę iteracji jako pasek postępu z oczekiwanym czasem zakończenia. Super przydatne i szybkie. Działa dla python2 i python3.

Joop
źródło
7

Inna odpowiedź może być lepsza, ale oto, co robiłem. Najpierw utworzyłem funkcję o nazwie progress, która wypisuje znak backspace:

def progress(x):
    out = '%s things done' % x  # The output
    bs = '\b' * 1000            # The backspace
    print bs,
    print out,

Następnie nazwałem to w pętli w mojej głównej funkcji w następujący sposób:

def main():
    for x in range(20):
        progress(x)
    return

Spowoduje to oczywiście wymazanie całej linii, ale możesz z nią zepsuć, aby zrobić dokładnie to, co chcesz. Skończyło się na utworzeniu paska postępu za pomocą tej metody.

Bryce Siedschlaw
źródło
4
Działa, ale jeśli poprzednia linia miała więcej znaków niż następna, znaki po końcu nowej linii pozostają z poprzedniej linii: „Zapis sprawdzania pisowni 417/701 [służebność zmieniona na powierzchnię], gdy] uminescencja] cence] shmentarianism] "
Lil 'Bits
7

Dla każdego, kto natknie się na to lata później (tak jak ja), poprawiłem trochę metody 6502, aby pasek postępu mógł się zmniejszać, a także zwiększać. Przydatne w nieco większej liczbie przypadków. Dzięki 6502 za świetne narzędzie!

Zasadniczo jedyną różnicą jest to, że cały wiersz #s i -s jest zapisywany za każdym razem, gdy wywoływana jest funkcja progress (x), a kursor jest zawsze zwracany na początek słupka.

def startprogress(title):
    """Creates a progress bar 40 chars long on the console
    and moves cursor back to beginning with BS character"""
    global progress_x
    sys.stdout.write(title + ": [" + "-" * 40 + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = 0


def progress(x):
    """Sets progress bar to a certain percentage x.
    Progress is given as whole percentage, i.e. 50% done
    is given by x = 50"""
    global progress_x
    x = int(x * 40 // 100)                      
    sys.stdout.write("#" * x + "-" * (40 - x) + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = x


def endprogress():
    """End of progress bar;
    Write full bar, then move to next line"""
    sys.stdout.write("#" * 40 + "]\n")
    sys.stdout.flush()
jat255
źródło
1
Odkryłem jednak, że może to powodować spowolnienia, jeśli jest wywoływane zbyt często przez kod, więc myślę, że YMMV
jat255
6

Jeśli dobrze zrozumiałem (nie jestem pewien), chcesz drukować używając, <CR>a nie <LR>?

Jeśli tak, jest to możliwe, o ile terminal konsoli na to pozwala (zepsuje się, gdy wyjście si zostanie przekierowane do pliku).

from __future__ import print_function
print("count x\r", file=sys.stdout, end=" ")
sorin
źródło
5

Można to zrobić bez korzystania z biblioteki sys, jeśli spojrzymy na print()funkcję

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Oto mój kod:

def update(n):
    for i in range(n):
        print("i:",i,sep='',end="\r",flush=True)
        #time.sleep(1)
Suman Saurabh
źródło
5

Napisałem to jakiś czas temu i bardzo się z tego cieszę. Zapraszam do korzystania z niego.

Zajmuje indexi totali opcjonalnie titlelub bar_length. Po zakończeniu zastępuje klepsydrę znaczkiem wyboru.

⏳ Calculating: [████░░░░░░░░░░░░░░░░░░░░░] 18.0% done

✅ Calculating: [█████████████████████████] 100.0% done

Dołączyłem przykład, który można uruchomić, aby go przetestować.

import sys
import time

def print_percent_done(index, total, bar_len=50, title='Please wait'):
    '''
    index is expected to be 0 based index. 
    0 <= index < total
    '''
    percent_done = (index+1)/total*100
    percent_done = round(percent_done, 1)

    done = round(percent_done/(100/bar_len))
    togo = bar_len-done

    done_str = '█'*int(done)
    togo_str = '░'*int(togo)

    print(f'\t⏳{title}: [{done_str}{togo_str}] {percent_done}% done', end='\r')

    if round(percent_done) == 100:
        print('\t✅')


r = 50
for i in range(r):
    print_percent_done(i,r)
    time.sleep(.02)

Mam również wersję z responsywnym paskiem postępu w zależności od szerokości terminala używanego, shutil.get_terminal_size()jeśli jest to interesujące.

Ivan Procopovich
źródło
4

Dodano trochę więcej funkcjonalności do przykładu Aravind Voggu :

def progressBar(name, value, endvalue, bar_length = 50, width = 20):
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent*bar_length) - 1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        sys.stdout.write("\r{0: <{1}} : [{2}]{3}%".format(\
                         name, width, arrow + spaces, int(round(percent*100))))
        sys.stdout.flush()
        if value == endvalue:     
             sys.stdout.write('\n\n')

Teraz możesz generować wiele pasków postępu bez zastępowania poprzedniego.

Dodałem również namejako wartość o stałej szerokości.

Dla dwóch pętli i dwukrotnego użycia progressBar()wyniku będzie wyglądać następująco:

animacja paska postępu

Nils Kohlmey
źródło
-1

Poniższy kod będzie liczyć wiadomość od 0 do 137 co 0,3 sekundy zastępując poprzednią liczbę.

Liczba symboli za kulisami = liczba cyfr.

stream = sys.stdout
for i in range(137):
    stream.write('\b' * (len(str(i)) + 10))
    stream.write("Message : " + str(i))
    stream.flush()
    time.sleep(0.3)
laggerok19
źródło