Python per stampare la barra di stato e la percentuale


160

Per implementare una barra di stato come di seguito:

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

Voglio che questo venga stampato su stdout e continui ad aggiornarlo, non stamparlo su un'altra riga. Come fare questo?


Ho pubblicato un nuovo tipo di barra di avanzamento, che puoi stampare, vedere la velocità effettiva e eta, persino metterla in pausa, oltre alle animazioni molto interessanti! Dai un'occhiata: github.com/rsalmei/alive-progress ! alive-progress
rsalmei,

Risposte:


146

C'è un modulo Python che puoi ottenere da PyPI chiamato progressbarche implementa tale funzionalità. Se non ti dispiace aggiungere una dipendenza, è una buona soluzione. Altrimenti, vai con una delle altre risposte.

Un semplice esempio di come usarlo:

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

Per installarlo, puoi usare easy_install progressbar, o pip install progressbarse preferisci pip.


Tutte le risposte sono fantastiche, tuttavia, mi piace di più il modulo. Grazie a tutti.
Stan,

8
Forse questo esempio ha bisogno di qualcosa del genere bar.start()?
Jim Raynor,

2
Non funziona sulla mia macchina - è necessario aggiungerebar.start()
Zach

1
Questo modulo non è stato aggiornato da oltre 2 anni. Non usarlo per il nuovo software!
Navin,

4
Installazione: sudo -H pip install progressbar2.
Martin Thoma,

254

Il '\r'carattere (ritorno a capo) reimposta il cursore all'inizio della riga e consente di scrivere su ciò che era precedentemente sulla riga.

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)

Non sono sicuro al 100% se questo è completamente portatile su tutti i sistemi, ma funziona almeno su Linux e OSX.


7
È meglio della risposta contrassegnata, perché funziona anche con Python 3.
Gerhard Hagerer il

qualche idea sul perché a volte ottengo strani caratteri alla fine di una riga, anche se termino con null la stringa che viene scritta?
reservoirman,

23
Come scritto, il codice non chiarisce come adattarlo a qualsiasi dimensione su cui stai ripetendo (non 21). Io lascerei n=21, sostituire range(21)con range(n), quindi aggiungere j = (i + 1) / nall'interno del ciclo, e sostituire la writedichiarazione con la piccola modifica: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Ora l'unica modifica che è necessario apportare è n=21prima del ciclo (più probabile n=len(iterable)), quindi enumerare l'oggetto iterabile. Ho raccomandato questa modifica ma è stata rifiutata; apparentemente la funzionalità "si discosta dall'intento originale del post".
Steven C. Howell,

1
Adattivo con dimensione arbitraria di n, sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))solo Python 3
GabrielChu

3
@ StevenC.Howell perché non lo pubblichi come risposta citando Mark? è un'altra versione ed è molto utile prepararlo
GM

91

Ho trovato utile la libreria tqdm ( https://github.com/tqdm/tqdm/ , in precedenza: https://github.com/noamraph/tqdm ). Stima automaticamente i tempi di completamento e può essere utilizzato come iteratore.

Uso:

import tqdm
import time

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

Risultati in:

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

tqdm può avvolgere qualsiasi iterabile.


4
Il progetto tqdm è ora gestito qui da un nuovo team di sviluppatori.
gaborous

1
waw, questo tqdm è semplicemente fantastico e così facile da usare :).
Sidahmed,

6
questa è facilmente la soluzione più semplice
kd88,

2
viene fornito in bundle anche con la distribuzione Anaconda! (almeno per Python 3.5 e versioni successive)
Jacquot

Grazie, incredibile, semplice ed efficace
DavideL

23

È possibile utilizzare \r( ritorno a capo ). demo:

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

Prova totalcome 10, quindi ricevi il messaggio di erroreZeroDivisionError: long division or modulo by zero
unseen_rider,

19

Qui è possibile utilizzare il seguente codice come funzione:

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

Con l'uso di .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()

Per me la prima riga non viene modificata dal cursore, ma solo la seconda riga. Quindi più righe ottenute per la barra di avanzamento e una per es.] percent * 100 %
unseen_rider,

6

sulla base delle risposte di cui sopra e di altre domande simili sulla barra di avanzamento della CLI, penso di avere una risposta comune generale a tutte. Controllalo su https://stackoverflow.com/a/15860757/2254146

Ecco una copia della funzione, ma modificata per adattarla al tuo stile:

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

Sembra

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


La barra di avanzamento non appare per me
unseen_rider,

Penso che non ci sia bisogno di molti decimali, cioè intorno (progresso * 100,2)
Andrés Sánchez,

4

Se stai sviluppando un'interfaccia a riga di comando, ti suggerisco di dare un'occhiata a ciò clickche è molto bello:

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)

Ecco l'output che ottieni:

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

4

Ulteriore miglioramento, utilizzando una funzione come:

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

esempio di chiamata:

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

produzione:

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

Perfetto! e nessun modulo necessario
yurividal

3

Mi sono imbattuto in questo thread oggi e dopo aver provato questa soluzione da Mark Rushakoff

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)

Posso dire che funziona benissimo su W7-64 con python 3.4.3 a 64 bit ma solo nella console nativa. Tuttavia, quando si utilizza la console integrata di spyder 3.0.0dev, le interruzioni di riga sono ancora / nuovamente presenti. Dato che mi ci è voluto del tempo per capire, vorrei riportare questa osservazione qui.


2

Basandomi su alcune delle risposte qui e altrove, ho scritto questa semplice funzione che visualizza una barra di avanzamento e il tempo rimanente trascorso / stimato. Dovrebbe funzionare sulla maggior parte delle macchine basate su 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

Questo è un approccio abbastanza semplice che può essere utilizzato con qualsiasi loop.

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

Cosa fa il '#'?
unseen_rider,

@unseen_rider Qui '#' è solo un simbolo che verrà ripetuto all'aumentare dell'iterazione del ciclo.
NILESH KUMAR,

2

Il più semplice è ancora

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

La chiave è convertire il tipo intero in stringa.


2

Come descritto nella soluzione di Mark Rushakoff , è possibile emettere il carattere di ritorno a caposys.stdout.write('\r') , per ripristinare il cursore all'inizio della riga. Per generalizzare quella soluzione, implementando anche le f-string di Python 3 , è possibile utilizzare

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

Prova PyProg. PyProg è una libreria open source per Python per creare indicatori e barre di avanzamento super personalizzabili.

È attualmente alla versione 1.0.2; è ospitato su Github e disponibile su PyPI (link in basso). È compatibile con Python 3 e 2 e può essere utilizzato anche con Qt Console.

È davvero facile da usare. Il seguente codice:

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

produrrà esattamente quello che vuoi (anche la lunghezza della barra!):

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

Per ulteriori opzioni per personalizzare la barra di avanzamento, vai alla pagina Github di questo sito Web.

In realtà ho creato PyProg perché avevo bisogno di una libreria di barre di avanzamento semplice ma super personalizzabile. Si può facilmente installare con: pip install pyprog.

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


1

Usando la risposta di @ Mark-Rushakoff, ho elaborato un approccio più semplice, senza bisogno di chiamare la libreria sys. Funziona con Python 3. Testato in 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)

Come descritto nella risposta a una domanda diversa print è un wrapper sottile che formatta l'input e chiama la funzione di scrittura di un determinato oggetto. Per impostazione predefinita, questo oggetto è sys.stdout.
Steven C. Howell,

0

Ecco qualcosa che ho realizzato usando la soluzione di @ Mark-Rushakoff. Per adattarsi in modo adattivo alla larghezza del terminale.

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("")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.