Python, aby wydrukować pasek stanu i procent


160

Aby zaimplementować pasek stanu, jak poniżej:

[==========                ]  45%
[================          ]  60%
[==========================] 100%

Chcę, aby to zostało wydrukowane na standardowe wyjście i ciągle je odświeżać, a nie drukować do innej linii. Jak to zrobić?


Opublikowałem nowy rodzaj paska postępu, który możesz wydrukować, zobaczyć przepustowość i eta, a nawet wstrzymać, oprócz bardzo fajnych animacji! Spójrz na: github.com/rsalmei/alive-progress ! żywy postęp
rsalmei

Odpowiedzi:


146

Istnieje moduł Pythona, który można pobrać z PyPI o nazwie, progressbarktóry implementuje taką funkcjonalność. Jeśli nie masz nic przeciwko dodaniu zależności, to jest dobre rozwiązanie. W przeciwnym razie przejdź do jednej z pozostałych odpowiedzi.

Prosty przykład, jak go używać:

import progressbar
from time import sleep
bar = progressbar.ProgressBar(maxval=20, \
    widgets=[progressbar.Bar('=', '[', ']'), ' ', progressbar.Percentage()])
bar.start()
for i in xrange(20):
    bar.update(i+1)
    sleep(0.1)
bar.finish()

Aby go zainstalować, możesz użyć easy_install progressbarlub, pip install progressbarjeśli wolisz, pip.


Wszystkie odpowiedzi są niesamowite, jednak moduł podoba mi się najbardziej. Dziękuję wszystkim.
Stan

8
Może ten przykład potrzebuje czegoś takiego bar.start()?
Jim Raynor

2
Nie działa na moim komputerze - trzeba dodaćbar.start()
Zach

1
Ten moduł nie był aktualizowany od ponad 2 lat. Nie używaj go do nowego oprogramowania!
Navin,

4
Instalacja: sudo -H pip install progressbar2.
Martin Thoma

254

'\r'Charakter (powrót karetki) resetuje kursor na początku linii i pozwala zapisać ponad to, co było wcześniej na linii.

from time import sleep
import sys

for i in range(21):
    sys.stdout.write('\r')
    # the exact output you're looking for:
    sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
    sys.stdout.flush()
    sleep(0.25)

Nie jestem w 100% pewien, czy jest to całkowicie przenośne we wszystkich systemach, ale działa przynajmniej na Linuksie i OSX.


7
Jest to lepsze niż zaznaczona odpowiedź, ponieważ działa również z Pythonem 3.
Gerhard Hagerer

masz jakiś pomysł, dlaczego czasami dostaję dziwne znaki na końcu wiersza, nawet jeśli kończę napisany ciąg znaków z wartością null?
Reservoirman

23
Jak napisano, kod nie wyjaśnia, jak dostosować to do dowolnego rozmiaru, po którym iterujesz (nie 21). Chciałbym pozwolić n=21, wymienić range(21)się range(n), a następnie dodać j = (i + 1) / nwewnątrz pętli i zastąpić writeoświadczenie z tej niewielkiej modyfikacji: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Teraz jedyną zmianą, jaką musisz wprowadzić, jest umieszczenie go n=21przed pętlą (bardziej prawdopodobne n=len(iterable)), a następnie wyliczenie na iterowalnym obiekcie. Poleciłem tę zmianę, ale została odrzucona; pozornie funkcjonalność „odbiega od pierwotnej intencji postu”.
Steven C. Howell

1
Adaptacyjny z dowolnym rozmiarem n sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))
,,

3
@ Steven C. Jak więc dlaczego nie opublikujesz tego jako odpowiedzi, cytując Marka? jest to inna wersja i bardzo pomocne jest jej przygotowanie
GM

91

Znalazłem przydatną bibliotekę tqdm ( https://github.com/tqdm/tqdm/ , wcześniej: https://github.com/noamraph/tqdm ). Automatycznie szacuje czas ukończenia i może być używany jako iterator.

Stosowanie:

import tqdm
import time

for i in tqdm.tqdm(range(1000)):
    time.sleep(0.01)
    # or other long operations

Prowadzi do:

|####------| 450/1000  45% [elapsed: 00:04 left: 00:05, 99.15 iters/sec]

tqdm może zawijać dowolne iterowalne.


4
Projekt tqdm jest teraz obsługiwany przez nowy zespół programistów.
hałaśliwy

1
waw, ten tqdm jest po prostu niesamowity i łatwy w użyciu :).
Sidahmed

6
jest to najprostsze rozwiązanie
kd88

2
jest dostarczany w pakiecie z dystrybucją Anaconda! (co najmniej w przypadku Pythona 3.5 i nowszych)
Jacquot

Dzięki, niesamowite, proste i skuteczne
DavideL

23

Możesz użyć \r( powrót karetki ). Próbny:

import sys
total = 10000000
point = total / 100
increment = total / 20
for i in xrange(total):
    if(i % (5 * point) == 0):
        sys.stdout.write("\r[" + "=" * (i / increment) +  " " * ((total - i)/ increment) + "]" +  str(i / point) + "%")
        sys.stdout.flush()

Spróbuj totaljako 10, a otrzymasz komunikat o błędzieZeroDivisionError: long division or modulo by zero
unseen_rider

19

Tutaj możesz użyć następującego kodu jako funkcji:

def drawProgressBar(percent, barLen = 20):
    sys.stdout.write("\r")
    progress = ""
    for i in range(barLen):
        if i < int(barLen * percent):
            progress += "="
        else:
            progress += " "
    sys.stdout.write("[ %s ] %.2f%%" % (progress, percent * 100))
    sys.stdout.flush()

Przy użyciu .format:

def drawProgressBar(percent, barLen = 20):
    # percent float from 0 to 1. 
    sys.stdout.write("\r")
    sys.stdout.write("[{:<{}}] {:.0f}%".format("=" * int(barLen * percent), barLen, percent * 100))
    sys.stdout.flush()

U mnie pierwsza linia nie jest zmieniana przez kursor - tylko druga linia. Stąd wiele wierszy uzyskanych dla paska postępu, a jedna dla np.] percent * 100 %
unseen_rider

6

Bazując na powyższych odpowiedziach i innych podobnych pytaniach dotyczących paska postępu CLI, myślę, że uzyskałem ogólną wspólną odpowiedź na wszystkie z nich. Sprawdź to na https://stackoverflow.com/a/15860757/2254146

Oto kopia funkcji, ale zmodyfikowana w celu dopasowania do Twojego stylu:

import time, sys

# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a 'halt'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
    barLength = 20 # Modify this to change the length of the progress bar
    status = ""
    if isinstance(progress, int):
        progress = float(progress)
    if not isinstance(progress, float):
        progress = 0
        status = "error: progress var must be float\r\n"
    if progress < 0:
        progress = 0
        status = "Halt...\r\n"
    if progress >= 1:
        progress = 1
        status = "Done...\r\n"
    block = int(round(barLength*progress))
    text = "\rPercent: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), progress*100, status)
    sys.stdout.write(text)
    sys.stdout.flush()

Wygląda jak

Procent: [====================] 99,0%


Pasek postępu nie pojawia się dla mnie
unseen_rider

Myślę, że nie potrzeba tylu miejsc po przecinku, czyli runda (postęp * 100,2)
Andrés Sánchez

4

Jeśli tworzysz interfejs wiersza poleceń, proponuję przyjrzeć się, clickktóry jest bardzo przyjemny:

import click
import time

for filename in range(3):
    with click.progressbar(range(100), fill_char='=', empty_char=' ') as bar:
        for user in bar:
            time.sleep(0.01)

Tutaj otrzymujesz wynik:

$ python test.py
  [====================================]  100%
  [====================================]  100%
  [=========                           ]   27%

4

Dalsze ulepszanie, używając funkcji jak:

import sys

def printProgressBar(i,max,postText):
    n_bar =10 #size of progress bar
    j= i/max
    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%  {postText}")
    sys.stdout.flush()

przykład dzwonienia:

total=33
for i in range(total):
    printProgressBar(i,total,"blah")
    sleep(0.05)  

wynik:

[================================================  ] 96%  blah  

idealny! i żadne moduły nie są potrzebne
yurividal

3

Natknąłem się na ten wątek dzisiaj i po wypróbowaniu tego rozwiązania od Marka Rushakoffa

from time import sleep
import sys

for i in range(21):
sys.stdout.write('\r')
# the exact output you're looking for:
sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
sys.stdout.flush()
sleep(0.25)

Mogę powiedzieć, że działa to dobrze na W7-64 z 64-bitowym Pythonem 3.4.3, ale tylko w natywnej konsoli. Jednak podczas korzystania z wbudowanej konsoli spyder 3.0.0dev podziały wierszy są nadal / ponownie obecne. Ponieważ zrozumienie tego zajęło mi trochę czasu, chciałbym zgłosić tę obserwację tutaj.


2

Opierając się na niektórych odpowiedziach tutaj i gdzie indziej, napisałem tę prostą funkcję, która wyświetla pasek postępu i upływający / szacowany pozostały czas. Powinien działać na większości maszyn z systemem UNIX.

import time
import sys

percent = 50.0
start = time.time()
draw_progress_bar(percent, start)


def draw_progress_bar(percent, start, barLen=20):
sys.stdout.write("\r")
progress = ""
for i in range(barLen):
    if i < int(barLen * percent):
        progress += "="
    else:
        progress += " "

elapsedTime = time.time() - start;
estimatedRemaining = int(elapsedTime * (1.0/percent) - elapsedTime)

if (percent == 1.0):
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: Done!\n" % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60))
    sys.stdout.flush()
    return
else:
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: %im%02is " % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60,
         estimatedRemaining/60, estimatedRemaining%60))
    sys.stdout.flush()
    return

2

Jest to dość proste podejście, które można zastosować w przypadku dowolnej pętli.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),

Co robi „#”?
unseen_rider

@unseen_rider Tutaj „#” to tylko symbol, który będzie powtarzany wraz ze wzrostem iteracji pętli.
NILESH KUMAR

2

Najłatwiej jest nadal

import sys
total_records = 1000
for i in range (total_records):
    sys.stdout.write('\rUpdated record: ' + str(i) + ' of ' + str(total_records))
    sys.stdout.flush()

Kluczem jest konwersja typu liczby całkowitej na łańcuch.


2

Jak opisano w rozwiązaniu Marka Rushakoffa , można wyprowadzić znak powrotu karetki sys.stdout.write('\r'), aby zresetować kursor do początku wiersza. Aby uogólnić to rozwiązanie, jednocześnie implementując f-Strings w Pythonie 3 , możesz użyć

from time import sleep
import sys

n_bar = 50
iterable = range(33)  # for demo purposes
n_iter = len(iterable)
for i, item in enumerate(iterable):
    j = (i + 1) / n_iter

    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%")
    sys.stdout.flush()

    sleep(0.05)  
    # do something with <item> here

1

Wypróbuj PyProg. PyProg to biblioteka open source dla Pythona do tworzenia super konfigurowalnych wskaźników postępu i pasków.

Obecnie jest w wersji 1.0.2; jest hostowany na Github i dostępny na PyPI (linki poniżej). Jest kompatybilny z Pythonem 3 i 2 i może być również używany z Qt Console.

Jest naprawdę łatwy w użyciu. Poniższy kod:

import pyprog
from time import sleep

# Create Object
prog = pyprog.ProgressBar(" ", " ", total=34, bar_length=26, complete_symbol="=", not_complete_symbol=" ", wrap_bar_prefix=" [", wrap_bar_suffix="] ", progress_explain="", progress_loc=pyprog.ProgressBar.PROGRESS_LOC_END)
# Update Progress Bar
prog.update()

for i in range(34):
    # Do something
    sleep(0.1)
    # Set current status
    prog.set_stat(i + 1)
    # Update Progress Bar again
    prog.update()

# Make the Progress Bar final
prog.end()

wyprodukuje dokładnie to, czego chcesz (nawet długość paska!):

[===========               ] 45%
[===============           ] 60%
[==========================] 100%

Aby uzyskać więcej opcji dostosowywania paska postępu, przejdź do strony Github w tej witrynie.

Zrobiłem PyProg, ponieważ potrzebowałem prostej, ale super konfigurowalnej biblioteki paska postępu. Można łatwo zainstalować go z: pip install pyprog.

PyProg Github: https://github.com/Bill13579/pyprog
PyPI: https://pypi.python.org/pypi/pyprog/


1

Używając odpowiedzi @ Mark-Rushakoff, wypracowałem prostsze podejście, bez konieczności dzwonienia do biblioteki sys. Działa z Pythonem 3. Przetestowano w systemie Windows:

from time import sleep
for i in range(21):
    # the exact output you're looking for:
    print ("\r[%-20s] %d%%" % ('='*i, 5*i), end='')
    sleep(0.25)

Jak opisano w odpowiedzi na inne pytanie, print jest cienka otoka, która formatuje dane wejściowe i wywołuje funkcję zapisu danego obiektu. Domyślnie tym obiektem jest sys.stdout.
Steven C. Howell

0

Oto coś, co zrobiłem za pomocą rozwiązania autorstwa @ Mark-Rushakoff. Adaptacyjne dopasowanie do szerokości terminala.

from time import sleep
import os
import sys
from math import ceil

l = list(map(int,os.popen('stty size','r').read().split()))
col = l[1]
col = col - 6

for i in range(col):
    sys.stdout.write('\r')
    getStr = "[%s " % ('='*i)
    sys.stdout.write(getStr.ljust(col)+"]"+"%d%%" % (ceil((100/col)*i)))
    sys.stdout.flush()
    sleep(0.25)
print("")
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.