Python pour imprimer la barre d'état et le pourcentage

160

Pour implémenter une barre d'état comme ci-dessous:

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

Je veux que cela soit imprimé sur stdout, et continuer à le rafraîchir, pas imprimer sur une autre ligne. Comment faire cela?

Stan
la source
J'ai publié un nouveau type de barre de progression, que vous pouvez imprimer, voir le débit et eta, même la mettre en pause, en plus des animations très cool! Veuillez jeter un œil: github.com/rsalmei/alive-progress !
live

Réponses:

146

Il existe un module Python que vous pouvez obtenir de PyPI appelé progressbarqui implémente une telle fonctionnalité. Si cela ne vous dérange pas d'ajouter une dépendance, c'est une bonne solution. Sinon, optez pour l'une des autres réponses.

Un exemple simple de son utilisation:

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

Pour l'installer, vous pouvez utiliser easy_install progressbar, ou pip install progressbarsi vous préférez pip.

icktoofay
la source
Toutes les réponses sont géniales, cependant, j'aime le plus le module. Merci à tout le monde.
Stan
8
Peut-être que cet exemple a besoin de quelque chose comme bar.start()?
Jim Raynor
2
Ne fonctionne pas sur ma machine - besoin d'ajouterbar.start()
Zach
1
Ce module n'a pas été mis à jour depuis plus de 2 ans. Ne l'utilisez pas pour un nouveau logiciel!
Navin le
4
Installation: sudo -H pip install progressbar2.
Martin Thoma
254

Le '\r'caractère (retour chariot) remet le curseur au début de la ligne et vous permet d'écrire sur ce qui était auparavant sur la ligne.

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)

Je ne suis pas sûr à 100% si cela est complètement portable sur tous les systèmes, mais cela fonctionne au moins sous Linux et OSX.

Mark Rushakoff
la source
7
C'est mieux que la réponse marquée, car cela fonctionne également avec Python 3.
Gerhard Hagerer
une idée pourquoi j'obtiens parfois des caractères étranges à la fin d'une ligne, même si je termine par null la chaîne qui est en cours d'écriture?
reservoirman
23
Tel qu'il est écrit, le code n'indique pas clairement comment l'adapter à la taille sur laquelle vous itérez (pas 21). Je laisserais n=21, remplacer range(21)avec range(n), puis ajouter j = (i + 1) / ndans la boucle, et remplacer la writedéclaration avec cette légère modification: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Maintenant, le seul changement que vous devez faire est de faire n=21avant la boucle (plus probablement n=len(iterable)), puis d'énumérer sur l'objet itérable. J'ai recommandé cette modification mais elle a été rejetée; apparemment, la fonctionnalité "s'écarte de l'intention originale du message".
Steven C. Howell
1
Adaptatif avec une taille arbitraire de n sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i))),, Python 3 uniquement
GabrielChu
3
@ StevenC.How, pourquoi ne le postez-vous pas comme réponse citant Mark? c'est une autre version et c'est très utile de le préparer
GM
91

J'ai trouvé la bibliothèque utile tqdm ( https://github.com/tqdm/tqdm/ , précédemment: https://github.com/noamraph/tqdm ). Il estime automatiquement le temps d'achèvement et peut être utilisé comme itérateur.

Usage:

import tqdm
import time

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

Résulte en:

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

tqdm peut envelopper tout itérable.

Omikron
la source
4
Le projet tqdm est maintenant maintenu ici par une nouvelle équipe de développeurs.
gaborous
1
waw, ce tqdm est tout simplement incroyable et si facile à utiliser :).
Sidahmed
6
c'est facilement la solution la plus simple
kd88
2
il est également livré avec la distribution Anaconda! (comme pour python 3.5 et plus au moins)
Jacquot
Merci, incroyable, simple et efficace
DavideL
23

Vous pouvez utiliser \r( retour chariot ). Démo:

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
la source
Essayez en totaltant que 10, puis vous obtenez un message d'erreurZeroDivisionError: long division or modulo by zero
unseen_rider
19

Ici, vous pouvez utiliser le code suivant comme fonction:

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

Avec l'utilisation 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
la source
Pour moi, la première ligne n'est pas modifiée par le curseur - seule la deuxième ligne le fait. D'où plusieurs lignes obtenues pour la barre de progression, et une pour par exemple] percent * 100 %
unseen_rider
6

sur la base des réponses ci-dessus et d'autres questions similaires sur la barre de progression de la CLI, je pense avoir obtenu une réponse générale commune à toutes. Vérifiez-le sur https://stackoverflow.com/a/15860757/2254146

Voici une copie de la fonction, mais modifiée pour s'adapter à votre style:

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

Ressemble à

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

Brian Khuu
la source
La barre de progression n'apparaît pas pour moi
unseen_rider
Je pense qu'il n'a pas besoin de beaucoup de décimales c'est à dire rond (progression * 100,2)
Andrés Sánchez
4

Si vous développez une interface de ligne de commande, je vous suggère de jeter un œil à clickce qui est très agréable:

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)

Voici la sortie que vous obtenez:

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

Amélioration supplémentaire, en utilisant une fonction comme:

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

exemple d'appel:

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

production:

[================================================  ] 96%  blah  
STG
la source
parfait! et aucun module requis
yurividal
3

Je suis tombé sur ce fil aujourd'hui et après avoir essayé cette solution 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)

Je peux dire que cela fonctionne bien sur W7-64 avec python 3.4.3 64 bits mais uniquement dans la console native. Cependant, lors de l'utilisation de la console intégrée de spyder 3.0.0dev, les sauts de ligne sont toujours / à nouveau présents. Comme cela m'a pris un certain temps à comprendre, j'aimerais signaler cette observation ici.

Monsieur le cul
la source
2

En me basant sur certaines des réponses ici et ailleurs, j'ai écrit cette fonction simple qui affiche une barre de progression et le temps restant écoulé / estimé. Devrait fonctionner sur la plupart des machines basées sur 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
la source
2

C'est une approche assez simple qui peut être utilisée avec n'importe quelle boucle.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),
NILESH KUMAR
la source
Que fait le «#»?
unseen_rider
@unseen_rider Ici '#' est juste un symbole qui sera répété à mesure que l'itération de la boucle augmente.
NILESH KUMAR
2

Le plus simple est encore

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 clé est de convertir le type entier en chaîne.

bfree67
la source
2

Comme décrit dans la solution de Mark Rushakoff , vous pouvez afficher le caractère de retour chariot,, sys.stdout.write('\r')pour réinitialiser le curseur au début de la ligne. Pour généraliser cette solution, tout en implémentant les f-Strings de Python 3 , vous pouvez utiliser

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
la source
1

Essayez PyProg. PyProg est une bibliothèque open-source pour Python permettant de créer des indicateurs et des barres de progression super personnalisables.

Il est actuellement à la version 1.0.2; il est hébergé sur Github et disponible sur PyPI (liens ci-dessous). Il est compatible avec Python 3 & 2 et il peut également être utilisé avec Qt Console.

C'est vraiment facile à utiliser. Le code suivant:

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

produira exactement ce que vous voulez (même la longueur de la barre!):

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

Pour plus d'options pour personnaliser la barre de progression, accédez à la page Github de ce site Web.

J'ai en fait créé PyProg parce que j'avais besoin d'une bibliothèque de barres de progression simple mais super personnalisable. Vous pouvez facilement l' installer avec: pip install pyprog.

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

Bill Kudo
la source
1

En utilisant la réponse @ Mark-Rushakoff, j'ai élaboré une approche plus simple, pas besoin d'appeler la bibliothèque sys. Il fonctionne avec Python 3. Testé sous 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
la source
Comme décrit dans la réponse à une autre question, il print existe un wrapper léger qui formate l'entrée et appelle la fonction d'écriture d'un objet donné. Par défaut, cet objet est sys.stdout.
Steven C. Howell
0

Voici quelque chose que j'ai fait en utilisant la solution de @ Mark-Rushakoff. Pour s'adapter de manière adaptative à la largeur du 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
la source