Python para imprimir la barra de estado y el porcentaje

160

Para implementar una barra de estado como la siguiente:

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

Quiero que esto se imprima en stdout, y siga actualizándolo, no imprima en otra línea. ¿Como hacer esto?

Stan
fuente
¡He publicado un nuevo tipo de barra de progreso, que puedes imprimir, ver el rendimiento y el eta, incluso pausarlo, además de las animaciones geniales! Por favor, eche un vistazo: github.com/rsalmei/alive-progress ! progreso vivo
rsalmei

Respuestas:

146

Hay un módulo de Python que puede obtener de PyPI llamado progressbarque implementa dicha funcionalidad. Si no le importa agregar una dependencia, es una buena solución. De lo contrario, vaya con una de las otras respuestas.

Un ejemplo simple de cómo 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()

Para instalarlo, puede usar easy_install progressbar, o pip install progressbarsi prefiere pip.

icktoofay
fuente
Todas las respuestas son impresionantes, sin embargo, me gusta más el módulo. Gracias a todos.
Stan
8
¿Quizás este ejemplo necesita algo así bar.start()?
Jim Raynor
2
Does't trabajo en mi máquina- necesario añadirbar.start()
Zach
1
Este módulo no se ha actualizado en más de 2 años. ¡No lo use para un nuevo software!
Navin
44
Instalación: sudo -H pip install progressbar2.
Martin Thoma
254

El '\r'carácter (retorno de carro) restablece el cursor al comienzo de la línea y le permite escribir sobre lo que estaba anteriormente en la línea.

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)

No estoy 100% seguro de si esto es completamente portátil en todos los sistemas, pero al menos funciona en Linux y OSX.

Mark Rushakoff
fuente
77
Esto es mejor que la respuesta marcada, porque también funciona con Python 3.
Gerhard Hagerer
¿Alguna idea de por qué a veces aparecen caracteres extraños al final de una línea, incluso si termino la cadena que se está escribiendo?
reservoirman
23
Tal como está escrito, el código no deja en claro cómo adaptar esto al tamaño que esté iterando (no 21). Que iba a dejar n=21, sustituir range(21)con range(n), a continuación, añadir j = (i + 1) / nel interior del bucle, y sustituir la writedeclaración con esta ligera modificación: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Ahora el único cambio que necesita hacer es n=21antes del ciclo (más probable n=len(iterable)), luego enumerar sobre el objeto iterable. Recomiendo esta edición pero fue rechazada; aparentemente la funcionalidad "se desvía de la intención original de la publicación".
Steven C. Howell
1
Adaptable con tamaño arbitrario de n, sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))solo Python 3
GabrielChu
3
@ StevenC. ¿Por qué no lo publicas como una respuesta citando a Mark? que es otra versión y es muy útil para que esté listo
GM
91

Encontré la biblioteca útil tqdm ( https://github.com/tqdm/tqdm/ , anteriormente: https://github.com/noamraph/tqdm ). Estima automáticamente el tiempo de finalización y puede usarse como iterador.

Uso:

import tqdm
import time

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

Resultados en:

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

tqdm puede envolver cualquier iterable.

omikron
fuente
44
El proyecto tqdm ahora es mantenido aquí por un nuevo equipo de desarrolladores.
Gaborous
1
waw, este tqdm es simplemente increíble y muy fácil de usar :).
Sidahmed
66
esta es fácilmente la solución más simple
kd88
2
¡también viene incluido con la distribución Anaconda! (en cuanto a python 3.5 y superior al menos)
Jacquot
Gracias, increíble, simple y efectivo
DavideL
23

Puede usar \r( retorno de carro ). Manifestación:

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()
Matthew Flaschen
fuente
Intenta totalque 10, a continuación, se obtiene mensaje de errorZeroDivisionError: long division or modulo by zero
unseen_rider
19

Aquí puede usar el siguiente código como función:

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 el uso de .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()
Jacob CUI
fuente
Para mí, la primera línea no se modifica con el cursor, solo la segunda línea sí. Por lo tanto, se obtuvieron varias líneas para la barra de progreso, y una para, por ejemplo] percent * 100 %
unseen_rider el
6

basado en las respuestas anteriores y otras preguntas similares sobre la barra de progreso de la CLI, creo que obtuve una respuesta común general a todas ellas. Compruébelo en https://stackoverflow.com/a/15860757/2254146

Aquí hay una copia de la función, pero modificada para adaptarse a su estilo:

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

Parece

Porcentaje: [====================] 99.0%

Brian Khuu
fuente
La barra de progreso no aparece para mí
unseen_rider
Creo que no necesita tantos decimales, es decir, redondo (progreso * 100,2)
Andrés Sánchez
4

Si está desarrollando una interfaz de línea de comandos, le sugiero que eche un vistazo, clickque es muy agradable:

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)

Aquí la salida que obtienes:

$ python test.py
  [====================================]  100%
  [====================================]  100%
  [=========                           ]   27%
nowox
fuente
4

Mejora aún más, utilizando una función como:

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

ejemplo de llamada:

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

salida:

[================================================  ] 96%  blah  
STG
fuente
¡Perfecto! y no se necesitan módulos
yurividal
3

Encontré este hilo hoy y después de haber probado esta solución de 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)

Puedo decir que esto funciona bien en W7-64 con Python 3.4.3 de 64 bits, pero solo en la consola nativa. Sin embargo, cuando se utiliza la consola incorporada de spyder 3.0.0dev, los saltos de línea siguen / nuevamente presentes. Como esto me llevó un tiempo resolver, me gustaría informar esta observación aquí.

Mr.Bum
fuente
2

Sobre la base de algunas de las respuestas aquí y en otros lugares, he escrito esta función simple que muestra una barra de progreso y el tiempo restante transcurrido / estimado. Debería funcionar en la mayoría de las máquinas basadas en 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
Sam Jordan
fuente
2

Este es un enfoque bastante simple que se puede usar con cualquier bucle.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),
NILESH KUMAR
fuente
¿Qué hace el '#'?
unseen_rider
@unseen_rider Aquí '#' es solo un símbolo que se repetirá a medida que aumente la iteración del bucle.
NILESH KUMAR
2

Más fácil aún es

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 clave es convertir el tipo entero a cadena.

bfree67
fuente
2

Como se describe en la solución de Mark Rushakoff , puede generar el carácter de retorno de carro sys.stdout.write('\r'), para restablecer el cursor al comienzo de la línea. Para generalizar esa solución, al tiempo que implementa las cadenas f de Python 3 , puede usar

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
Steven C. Howell
fuente
1

Prueba PyProg. PyProg es una biblioteca de código abierto para Python para crear indicadores y barras de progreso súper personalizables.

Actualmente se encuentra en la versión 1.0.2; está alojado en Github y está disponible en PyPI (enlaces a continuación). Es compatible con Python 3 y 2 y también se puede usar con Qt Console.

Es realmente fácil de usar. El siguiente código:

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

producirá exactamente lo que desea (¡incluso la longitud de la barra!):

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

Para obtener más opciones para personalizar la barra de progreso, vaya a la página de Github de este sitio web.

Realmente hice PyProg porque necesitaba una biblioteca de barra de progreso simple pero súper personalizable. Se puede instalar fácilmente con: pip install pyprog.

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

Bill Kudo
fuente
1

Utilizando la respuesta de @ Mark-Rushakoff, elaboré un enfoque más simple, sin necesidad de llamar a la biblioteca del sistema. Funciona con Python 3. Probado en 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)
Stein
fuente
Como se describe en la respuesta a una pregunta diferente, print hay una envoltura delgada que formatea la entrada y llama a la función de escritura de un objeto dado. Por defecto este objeto es sys.stdout.
Steven C. Howell
0

Aquí hay algo que hice usando la solución de @ Mark-Rushakoff. Para adaptarse adaptativamente al ancho del terminal.

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("")
swarnava112
fuente