2010-07-01 111 views
133

¿Cómo uso una barra de progreso cuando mi script está realizando alguna tarea que probablemente lleve tiempo?Barra de progreso de Python

Por ejemplo, una función que tarda un tiempo en completarse y devuelve True cuando haya terminado. ¿Cómo puedo visualizar una barra de progreso durante el tiempo que se está ejecutando la función?

Tenga en cuenta que necesito que esto sea en tiempo real, por lo que no sé qué hacer al respecto. ¿Necesito un thread para esto? No tengo idea.

Ahora mismo no estoy imprimiendo nada mientras se está ejecutando la función, sin embargo, una barra de progreso sería bueno. También estoy más interesado en cómo se puede hacer esto desde el punto de vista del código.

+0

¿Está utilizando un kit de herramientas GUI o CLI solamente? – Bobby

+0

CLI. Pero puedo usar una biblioteca de terceros, eso no es un problema. Con GUI puedo hacerlo, pero estaba interesado en la parte CLI. – user225312

+0

Posible duplicado de [Barra de progreso de texto en la consola] (http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console) Tenga en cuenta que, si bien esta pregunta se publicó tres días antes, la pregunta vinculada se ve con más frecuencia. – Greenstick

Respuesta

114

hay bibliotecas específicas (like this one here), pero tal vez algo muy simple haría:

import time 
import sys 

toolbar_width = 40 

# setup toolbar 
sys.stdout.write("[%s]" % (" " * toolbar_width)) 
sys.stdout.flush() 
sys.stdout.write("\b" * (toolbar_width+1)) # return to start of line, after '[' 

for i in xrange(toolbar_width): 
    time.sleep(0.1) # do real work here 
    # update the bar 
    sys.stdout.write("-") 
    sys.stdout.flush() 

sys.stdout.write("\n") 

Nota: este progressbar es un tenedor de progressbar que no se ha mantenido en años.

+6

esto no escala para muchos pasos ... https://pypi.python.org/pypi/progress es mucho más fácil de usar – m13r

+2

Probé este código, y lanzó un 'NameError: name 'xrange' no está definido' error. ¿Me estoy perdiendo un módulo? –

+2

@ GokuMcSpock9733 ¿Qué versión de Python estás usando? Python's 2 'xrange' es el 'rango' de Python. – quapka

1

Si su trabajo no puede ser dividido en trozos medibles, usted podría llamar a su función en un nuevo hilo y medir el tiempo que tarda:

import thread 
import time 
import sys 

def work(): 
    time.sleep(5) 

def locked_call(func, lock): 
    lock.acquire() 
    func() 
    lock.release() 

lock = thread.allocate_lock() 
thread.start_new_thread(locked_call, (work, lock,)) 

# This part is icky... 
while(not lock.locked()): 
    time.sleep(0.1) 

while(lock.locked()): 
    sys.stdout.write("*") 
    sys.stdout.flush() 
    time.sleep(1) 
print "\nWork Done" 

Puede obviamente aumentar la precisión de tiempo como sea necesario.

+0

Leeré el módulo de hilo. Gracias por el código – user225312

+0

¿Dónde se debería medir el trabajo en el código como respuesta? –

0

Debe vincular la barra de progreso de la tarea en cuestión (para que mida el progreso: D). Por ejemplo, si está transfiriendo un archivo FTP, puede decirle a ftplib que tome un cierto tamaño de búfer, digamos 128K, y luego agregue a su barra de progreso cualquier porcentaje del tamaño del archivo 128k. Si está utilizando la CLI, y su medidor de progreso tiene 20 caracteres, agregará un carácter cuando se haya transferido la 20/20 del archivo.

+0

En mi caso, estoy usando una API y no proporciona ninguna posibilidad para obtener fragmentos específicos. Gracias por la idea, es agradable. – user225312

3

Utilice esta biblioteca: fish (GitHub).

Uso:

>>> import fish 
>>> while churning: 
...  churn_churn() 
...  fish.animate() 

Que se diviertan!

+3

fish no funciona (al menos mi) Windows XP –

+0

Es posible. Debe preguntar al desarrollador o dejar el ticket: https://github.com/lericson/fish. – Etienne

18

para una aplicación similar (hacer el seguimiento de los avances en un bucle) utilicé simplemente el python-progressbar:

Su ejemplo es algo como esto,

from progressbar import *    # just a simple progress bar 


widgets = ['Test: ', Percentage(), ' ', Bar(marker='0',left='[',right=']'), 
      ' ', ETA(), ' ', FileTransferSpeed()] #see docs for other options 

pbar = ProgressBar(widgets=widgets, maxval=500) 
pbar.start() 

for i in range(100,500+1,50): 
    # here do something long at each iteration 
    pbar.update(i) #this adds a little symbol at each iteration 
pbar.finish() 
print 
55

Las sugerencias anteriores son bastante buenos, pero Creo que la mayoría de la gente solo quiere una solución preparada, sin dependencias en paquetes externos, pero también es reutilizable.

Obtuve los mejores puntos de todos los anteriores y los convertí en una función, junto con un caso de prueba.

Para usarlo, solo copie las líneas en "def update_progress (progress)" pero no en el script de prueba. No olvides importar sys. Llámalo cuando necesites visualizar o actualizar la barra de progreso.

Esto funciona mediante el envío directamente el símbolo "\ r" a la consola para mover el cursor de nuevo al comienzo."Imprimir" en Python no recongise el símbolo anterior para este fin, por lo tanto necesitamos 'SYS'

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 = 10 # 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() 


# update_progress test script 
print "progress : 'hello'" 
update_progress("hello") 
time.sleep(1) 

print "progress : 3" 
update_progress(3) 
time.sleep(1) 

print "progress : [23]" 
update_progress([23]) 
time.sleep(1) 

print "" 
print "progress : -10" 
update_progress(-10) 
time.sleep(2) 

print "" 
print "progress : 10" 
update_progress(10) 
time.sleep(2) 

print "" 
print "progress : 0->1" 
for i in range(100): 
    time.sleep(0.1) 
    update_progress(i/100.0) 

print "" 
print "Test completed" 
time.sleep(10) 

Esto es lo que el resultado de los programas de test script (La última barra de progreso anime):

progress : 'hello' 
Percent: [----------] 0% error: progress var must be float 
progress : 3 
Percent: [##########] 100% Done... 
progress : [23] 
Percent: [----------] 0% error: progress var must be float 

progress : -10 
Percent: [----------] 0% Halt... 

progress : 10 
Percent: [##########] 100% Done... 

progress : 0->1 
Percent: [##########] 99.0% 
Test completed 
+2

gracias por mencionar "\ r" – haudoing

+5

La prueba animada (la última) debe decir 'en el rango (101)' no 100, el progreso se detiene en 99% y nunca se visualiza. –

0

@Massagran: Funciona bien en mis programas. Además, necesitamos agregar un contador para indicar los tiempos de ciclo. Este contador juega como el argumento del método update. Por ejemplo: lea todas las líneas de un archivo de prueba y trátelas en algo. Supongamos que la función dosth() no se refiere a la variable i.

lines = open(sys.argv[1]).readlines() 
i = 0 
widgets=[Percentage(), Bar()] 
pbar = ProgressBar(widgets=widgets,maxval=len(lines)).start() 
pbar.start() 
for line in lines:<pre> 
    dosth(); 
    i += 1 
    pbar.update(i)</pre> 
pbar.finish() 

Las variables i controla el estado de pbar a través del método update

6

Trate progreso de https://pypi.python.org/pypi/progress.

from progress.bar import Bar 

bar = Bar('Processing', max=20) 
for i in range(20): 
    # Do some work 
    bar.next() 
bar.finish() 

El resultado será un bar como los siguientes:

Processing |#############     | 42/100 
+0

Acabo de probar esto. MUY fácil de usar Me tomó como 2 minutos (incluido el progreso de instalación de pip) para tener una barra de estado en funcionamiento. – perelin

94

Con tqdm puede agregar un indicador de progreso a sus bucles en un segundo:

In [20]: import time 

In [21]: from tqdm import tqdm 

In [23]: for i in tqdm(range(10)): 
    ....:  time.sleep(3) 

60%|██████ | 6/10 [00:18<00:12, 0.33 it/s] 
+4

Esta es la única solución que encontré para trabajar con terminal, qtconsole y notebook – Ivelin

+3

¿Funciona con cualquier iterable? He tenido problemas para que funcione con una lista de cadenas. –

+0

Curiosamente, no funciona con IDLE bajo Windows. – AnotherParker

2

Me gusta este page .

Comienza con un ejemplo simple y se mueve a una versión de subprocesos múltiples. Funciona de la caja. No se requieren paquetes de terceros.

El código se verá algo como esto:

import time 
import sys 

def do_task(): 
    time.sleep(1) 

def example_1(n): 
    for i in range(n): 
     do_task() 
     print '\b.', 
     sys.stdout.flush() 
    print ' Done!' 

print 'Starting ', 
example_1(10) 

O aquí es ejemplo para utilizar hilos con el fin de ejecutar la barra de giro de carga, mientras que el programa se está ejecutando:

import sys 
import time 
import threading 

class progress_bar_loading(threading.Thread): 

    def run(self): 
      global stop 
      global kill 
      print 'Loading.... ', 
      sys.stdout.flush() 
      i = 0 
      while stop != True: 
        if (i%4) == 0: 
         sys.stdout.write('\b/') 
        elif (i%4) == 1: 
         sys.stdout.write('\b-') 
        elif (i%4) == 2: 
         sys.stdout.write('\b\\') 
        elif (i%4) == 3: 
         sys.stdout.write('\b|') 

        sys.stdout.flush() 
        time.sleep(0.2) 
        i+=1 

      if kill == True: 
       print '\b\b\b\b ABORT!', 
      else: 
       print '\b\b done!', 


kill = False  
stop = False 
p = progress_bar_loading() 
p.start() 

try: 
    #anything you want to run. 
    time.sleep(1) 
    stop = True 
except KeyboardInterrupt or EOFError: 
     kill = True 
     stop = True 
5

me gusta mucho el python-progressbar, ya que es muy simple de usar.

Para el caso más sencillo, es simplemente:

import progressbar 
import time 

progress = progressbar.ProgressBar() 
for i in progress(range(80)): 
    time.sleep(0.01) 

La apariencia puede ser personalizado y puede mostrar el tiempo restante estimado. Para un ejemplo usar el mismo código que el anterior pero con:

progress = progressbar.ProgressBar(widgets=[progressbar.Bar('=', '[', ']'), ' ', 
              progressbar.Percentage(), ' ', 
              progressbar.ETA()]) 
3

Muchas de las respuestas anteriores se basan en paquetes externos, pero también creo que (como algunos establecidas anteriormente) que la mayoría de la gente sólo quiere una solución ya preparada. El código de abajo se puede adaptar a sus necesidades personalizando la parte de la cadena.

Es más simple y funciona sin la necesidad de un segundo hilo para actualizar la barra. Algunos paquetes anteriores hacen eso. Un segundo hilo puede ser un problema, por ejemplo, para un cuaderno ipython.

El código de abajo solo funciona con iteradores que proporcionan una longitud (es decir, debe definirse len (iterador)).

import sys 

def progressbar(it, prefix="", size=60): 
    count = len(it) 
    def _show(_i): 
     x = int(size*_i/count) 
     sys.stdout.write("%s[%s%s] %i/%i\r" % (prefix, "#"*x, "."*(size-x), _i, count)) 
     sys.stdout.flush() 

    _show(0) 
    for i, item in enumerate(it): 
     yield item 
     _show(i+1) 
    sys.stdout.write("\n") 
    sys.stdout.flush() 

Ejemplo:

import time 

for i in progressbar(range(15), "Computing: ", 40): 
    time.sleep(0.1) # any calculation you need 

de salida:

Computing: [........................................] 0/15 

...

Computing: [########................................] 3/15 

...

Computing: [########################################] 15/15 

it puede ser cualquier objeto iterable con len, p. Ej. ['a', 'b', 'c'] `funciona bien.

1

Aquí hay una solución corta que construye la barra de carga programáticamente (debe decidir cuánto tiempo la quiere).

import time 

n = 33 # or however many loading slots you want to have 
load = 0.01 # artificial loading time! 
loading = '.' * n # for strings, * is the repeat operator 

for i in range(n+1): 
    # this loop replaces each dot with a hash! 
    print('\r%s Loading at %3d percent!' % (loading, i*100/n), end='') 
    loading = loading[:i] + '#' + loading[i+1:] 
    time.sleep(load) 
10

Acabo de hacer una clase de progreso simple para mis necesidades después de buscar aquí una solución equivalente. Es difícil que pueda publicarlo.

from __future__ import print_function 
import sys 
import re 


class ProgressBar(object): 
    DEFAULT = 'Progress: %(bar)s %(percent)3d%%' 
    FULL = '%(bar)s %(current)d/%(total)d (%(percent)3d%%) %(remaining)d to go' 

    def __init__(self, total, width=40, fmt=DEFAULT, symbol='=', 
       output=sys.stderr): 
     assert len(symbol) == 1 

     self.total = total 
     self.width = width 
     self.symbol = symbol 
     self.output = output 
     self.fmt = re.sub(r'(?P<name>%\(.+?\))d', 
      r'\g<name>%dd' % len(str(total)), fmt) 

     self.current = 0 

    def __call__(self): 
     percent = self.current/float(self.total) 
     size = int(self.width * percent) 
     remaining = self.total - self.current 
     bar = '[' + self.symbol * size + ' ' * (self.width - size) + ']' 

     args = { 
      'total': self.total, 
      'bar': bar, 
      'current': self.current, 
      'percent': percent * 100, 
      'remaining': remaining 
     } 
     print('\r' + self.fmt % args, file=self.output, end='') 

    def done(self): 
     self.current = self.total 
     self() 
     print('', file=self.output) 

Ejemplo:

from time import sleep 

progress = ProgressBar(80, fmt=ProgressBar.FULL) 

for x in xrange(progress.total): 
    progress.current += 1 
    progress() 
    sleep(0.1) 
progress.done() 

imprimirá el siguiente:

[======== ] 17/80 (21%) 63 to go

+1

Impresionante, gracias por esto. Por cierto, se puede añadir el 'progress.current' incremento en el final de' __call__' para limitar la interacción con el objeto del código principal aún más. – npit

6

me gusta Brian Khuu's answer por su sencillez y no necesitar paquetes externos. He cambiado un poco, así que estoy añadiendo mi versión aquí:

import sys 
import time 


def updt(total, progress): 
    """ 
    Displays or updates a console progress bar. 

    Original source: https://stackoverflow.com/a/15860757/1391441 
    """ 
    barLength, status = 20, "" 
    progress = float(progress)/float(total) 
    if progress >= 1.: 
     progress, status = 1, "\r\n" 
    block = int(round(barLength * progress)) 
    text = "\r[{}] {:.0f}% {}".format(
     "#" * block + "-" * (barLength - block), round(progress * 100, 0), 
     status) 
    sys.stdout.write(text) 
    sys.stdout.flush() 


runs = 300 
for run_num in range(runs): 
    time.sleep(.1) 
    updt(runs, run_num + 1) 

Se necesita el número total de carreras (total) y el número de carreras procesado hasta ahora (progress) asumiendo total >= progress. El resultado es el siguiente:

[#####---------------] 27% 
0

me gusta Gabriel respuesta, pero lo cambié a ser flexible. Puede enviar una barra de longitud a la función y obtener su barra de progreso con la longitud que desee. Y no puede tener una barra de progreso con longitud cero o negativa. Además, puede usar esta función como Gabriel respuesta (Mire el Ejemplo # 2).

import sys 
import time 

def ProgressBar(Total, Progress, BarLength=20, ProgressIcon="#", BarIcon="-"): 
    try: 
     # You can't have a progress bar with zero or negative length. 
     if BarLength <1: 
      BarLength = 20 
     # Use status variable for going to the next line after progress completion. 
     Status = "" 
     # Calcuting progress between 0 and 1 for percentage. 
     Progress = float(Progress)/float(Total) 
     # Doing this conditions at final progressing. 
     if Progress >= 1.: 
      Progress = 1 
      Status = "\r\n" # Going to the next line 
     # Calculating how many places should be filled 
     Block = int(round(BarLength * Progress)) 
     # Show this 
     Bar = "[{}] {:.0f}% {}".format(ProgressIcon * Block + BarIcon * (BarLength - Block), round(Progress * 100, 0), Status) 
     return Bar 
    except: 
     return "ERROR" 

def ShowBar(Bar): 
    sys.stdout.write(Bar) 
    sys.stdout.flush() 

if __name__ == '__main__': 
    print("This is a simple progress bar.\n") 

    # Example #1: 
    print('Example #1') 
    Runs = 10 
    for i in range(Runs + 1): 
     progressBar = "\rProgress: " + ProgressBar(10, i, Runs) 
     ShowBar(progressBar) 
     time.sleep(1) 

    # Example #2: 
    print('\nExample #2') 
    Runs = 10 
    for i in range(Runs + 1): 
     progressBar = "\rProgress: " + ProgressBar(10, i, 20, '|', '.') 
     ShowBar(progressBar) 
     time.sleep(1) 

    print('\nDone.') 

# Example #2: 
Runs = 10 
for i in range(Runs + 1): 
    ProgressBar(10, i) 
    time.sleep(1) 

Resultado:

This is a simple progress bar.

Example #1

Progress: [###-------] 30%

Example #2

Progress: [||||||||||||........] 60%

Done.

0

Si se trata de un gran bucle con una cantidad fija de iteraciones que se está produciendo una gran cantidad de tiempo que puede utilizar esta función hice. Cada iteración del ciclo agrega progreso. Donde count es la iteración actual del bucle, total es el valor al que está pasando el bucle y el tamaño (int) es el tamaño que desea la barra en incrementos de 10, es decir(tamaño 1 = 10 caracteres, tamaño 2 = 20 caracteres)

import sys 
def loadingBar(count,total,size) 
    percent = float(count)/float(total)*100 
    sys.stdout.write("\r" + str(int(count)).rjust(3,'0')+"/"+str(int(total)).rjust(3,'0') + ' [' + '='*int(percent/10)*size + ' '*(10-int(percent/10))*size + ']') 

ejemplo:

for i in range(0,100): 
    loadingBar(i,100,2) 
    #do some code 

salida:

i = 50 
>> 050/100 [==========   ] 
1

Trate PyProg. PyProg es una biblioteca de código abierto para Python para crear barras de indicadores 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 abajo). Es compatible con Python 3 & 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(" ", "", 34) 
# 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á:

Initial State: 
Progress: 0% -------------------------------------------------- 

When half done: 
Progress: 50% #########################------------------------- 

Final State: 
Progress: 100% ################################################## 

De hecho, me hizo PyProg porque necesitaba un simple pero muy personalizable biblioteca barra de progreso. Puede instalarlo fácilmente con: pip install pyprog.

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