Python para imprimir barra de status e porcentagem


160

Para implementar uma barra de status como abaixo:

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

Quero que isso seja impresso no stdout e continue atualizando-o, não imprima para outra linha. Como fazer isso?


Publiquei um novo tipo de barra de progresso, que você pode imprimir, ver taxa de transferência e eta, até pausá-la, além das animações muito legais! Por favor, dê uma olhada: github.com/rsalmei/alive-progress ! viva-progress
rsalmei

Respostas:


146

Há um módulo Python que você pode obter do PyPI chamado progressbarque implementa essa funcionalidade. Se você não se importa em adicionar uma dependência, é uma boa solução. Caso contrário, vá com uma das outras respostas.

Um exemplo simples de como usá-lo:

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 instalá-lo, você pode usar easy_install progressbar, ou pip install progressbarse preferir pip.


Todas as respostas são impressionantes, no entanto, eu gosto mais do módulo. Obrigado a todos.
Stan

8
Talvez este exemplo precise de algo como bar.start()?
Jim Raynor

2
Trabalho does't no meu máquina- necessário para adicionarbar.start()
Zach

1
Este módulo não é atualizado há mais de 2 anos. Não use para novos softwares!
Navin

4
Instalação: sudo -H pip install progressbar2.
Martin Thoma

254

O '\r'caractere (retorno de carro) redefine o cursor para o início da linha e permite que você escreva sobre o que estava anteriormente na linha.

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)

Não tenho 100% de certeza se isso é completamente portátil em todos os sistemas, mas funciona no Linux e OSX, pelo menos.


7
Isso é melhor do que a resposta marcada, porque também funciona com o Python 3.
Gerhard Hagerer

Alguma idéia de por que, às vezes, recebo caracteres estranhos no final de uma linha, mesmo que eu nulo a string que está sendo escrita?
reservoirman

23
Conforme escrito, o código não deixa claro como adaptar isso ao tamanho que você está repetindo (não 21). Eu deixaria n=21, substitua range(21)com range(n), em seguida, adicione j = (i + 1) / ndentro do loop, e substituir a writeindicação com esta ligeira modificação: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Agora, a única alteração que você precisa fazer é n=21antes do loop (mais provável n=len(iterable)) e depois enumerar sobre o objeto iterável. Eu recomendei esta edição, mas ela foi rejeitada; aparentemente a funcionalidade "se desvia da intenção original do post".
Steven C. Howell

1
Adaptável com tamanho arbitrário de n sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i))),, somente Python 3 #
7788 GabrielChu

3
@ StevenC.Howell por que você não o publica como resposta citando Mark? é uma outra versão e é muito útil para obtê-lo pronto
GM

91

Encontrei a biblioteca útil tqdm ( https://github.com/tqdm/tqdm/ , anteriormente: https://github.com/noamraph/tqdm ). Ele estima automaticamente o tempo de conclusão e pode ser usado como iterador.

Uso:

import tqdm
import time

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

Resulta em:

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

tqdm pode envolver qualquer iterável.


4
O projeto tqdm agora é mantido aqui por uma nova equipe de desenvolvedores.
gaborous

1
uau, esse tqdm é simplesmente incrível e fácil de usar :).
Sidahmed 28/02

6
este é facilmente a mais simples solução
kd88

2
ele também vem junto com o distrito Anaconda! (quanto ao python 3.5 e acima, pelo menos) #
1150 Jacquot

Obrigado, incrível, simples e eficaz
DavideL

23

Você pode usar \r( retorno de carro ). 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()

Tente totalcomo 10, então você recebe a mensagem de erroZeroDivisionError: long division or modulo by zero
unseen_rider 15/10

19

Aqui você pode usar o seguinte código como uma função:

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

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

Para mim, a primeira linha não é alterada pelo cursor - apenas a segunda linha. Por isso, várias linhas obtidas por barra de progresso, e um para, por exemplo] percent * 100 %
unseen_rider

6

com base nas respostas acima e em outras perguntas semelhantes sobre a barra de progresso da CLI, acho que recebi uma resposta geral geral para todas elas. Verifique em https://stackoverflow.com/a/15860757/2254146

Aqui está uma cópia da função, mas modificada para se adequar ao seu 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

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


A barra de progresso não aparece para mim
unseen_rider 15/17

Eu acho que não precisa de tantas casas decimais, isto é, arredondado (progresso * 100,2)
Andrés Sánchez

4

Se você estiver desenvolvendo uma interface de linha de comando, sugiro que você dê uma olhada no clickque é muito bom:

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)

Aqui a saída que você obtém:

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

4

Melhorando ainda mais, usando uma função 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()

exemplo de chamada:

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

resultado:

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

perfeito! e nenhum módulo necessário
yurividal

3

Encontrei este tópico hoje e depois de experimentar essa solução 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 dizer que isso funciona bem no W7-64 com o python 3.4.3 de 64 bits, mas apenas no console nativo. No entanto, ao usar o console interno do spyder 3.0.0dev, as quebras de linha ainda estão presentes novamente. Como isso levou algum tempo para descobrir, gostaria de relatar esta observação aqui.


2

Com base em algumas das respostas aqui e em outros lugares, escrevi essa função simples que exibe uma barra de progresso e o tempo restante decorrido / estimado. Deve funcionar na maioria das máquinas baseadas em 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

Essa é uma abordagem bastante simples que pode ser usada com qualquer loop.

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

O que o '#' faz?
Unseen_rider 15/10/19

@unseen_rider Aqui '#' é apenas um símbolo que será repetido à medida que a iteração do loop aumentar.
NILESH KUMAR

2

Mais fácil ainda

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

A chave é converter o tipo inteiro em string.


2

Conforme descrito na solução de Mark Rushakoff , você pode gerar o caractere de retorno de carro sys.stdout.write('\r'), para redefinir o cursor para o início da linha. Para generalizar essa solução, além de implementar as f-Strings do Python 3 , você pode 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

1

Experimente o PyProg. PyProg é uma biblioteca de código aberto para Python para criar barras e indicadores de progresso super personalizáveis.

Atualmente, está na versão 1.0.2; está hospedado no Github e disponível no PyPI (links abaixo). É compatível com Python 3 e 2 e também pode ser usado com o Qt Console.

É realmente fácil de usar. O código a seguir:

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

produzirá exatamente o que você deseja (até o comprimento da barra!):

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

Para mais opções para personalizar a barra de progresso, vá para a página Github deste site.

Na verdade, eu criei o PyProg porque precisava de uma biblioteca de barras de progresso simples, mas super personalizável. Você pode facilmente instalá-lo com: pip install pyprog.

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


1

Usando a resposta do @ Mark-Rushakoff, desenvolvi uma abordagem mais simples, sem necessidade de chamar a biblioteca sys. Funciona com o Python 3. Testado no 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)

Conforme descrito na resposta a uma pergunta diferente, print é um invólucro fino que formata a entrada e chama a função de gravação de um determinado objeto. Por padrão, esse objeto é sys.stdout.
Steven C. Howell

0

Aqui está algo que eu fiz usando a solução de @ Mark-Rushakoff. Ajustar adaptativamente à largura do 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("")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.