Python для друку рядка стану та відсотків


160

Щоб застосувати рядок стану, як показано нижче:

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

Я хочу, щоб це було надруковано на stdout, і продовжуйте оновити його, а не друкувати в інший рядок. Як це зробити?


Я опублікував новий вид смужки прогресу, який можна надрукувати, побачити пропускну здатність та ета, навіть призупинити його, окрім дуже крутих анімацій! Погляньте: github.com/rsalmei/alive-progress !
live

Відповіді:


146

Існує модуль Python, який можна отримати від PyPI під назвою, progressbarякий реалізує таку функціональність. Якщо ви не проти додати залежність, це хороше рішення. В іншому випадку перейдіть з однією з інших відповідей.

Простий приклад того, як ним користуватися:

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

Щоб встановити його, ви можете використовувати easy_install progressbarабо, pip install progressbarякщо ви віддаєте перевагу pip.


Усі відповіді приголомшливі, проте найбільше мені подобається модуль. Дякую всім.
Стен

8
Можливо, цей приклад потребує чогось подібного bar.start()?
Джим Рейнор

2
Не працює на моїй машині - потрібно додатиbar.start()
Зак

1
Цей модуль не оновлювався протягом 2 років. Не використовуйте його для нового програмного забезпечення!
Навін

4
Установка: sudo -H pip install progressbar2.
Мартін Тома

254

'\r'Символ (повернення каретки) скидає курсор на початок рядка і дозволяє писати над тим, що було раніше на лінії.

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)

Я не на 100% впевнений, що це повністю портативний в усіх системах, але він працює як мінімум на Linux та OSX.


7
Це краще, ніж позначена відповідь, оскільки вона працює і з Python 3.
Герхард Хагерер

будь-яка ідея, чому я часом отримую дивні символи в кінці рядка, навіть якщо я скасовує нульовий рядок, який записується?
водосховище

23
Як написано, у коді не з’ясовано, як пристосувати це до того, який розмір ви повторюєте (не 21). Я дозволив би n=21замінити range(21)з range(n), а потім додати j = (i + 1) / nв цикл, і замініть writeінструкцію з невеликою зміною: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Тепер єдина зміна, яку потрібно внести - це n=21перед циклом (скоріше n=len(iterable)), а потім перерахувати над ітерабельним об'єктом. Я рекомендував це редагування, але його було відхилено; мабуть, функціональність "відхиляється від початкового наміру повідомлення".
Стівен К. Хоуелл

1
Адаптив з довільним розміром n sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))
,,

3
@ StevenC.Hwell, чому ви не розміщуєте це як відповідь, цитуючи Марка? це ще одна версія, і це дуже корисно, щоб підготуватися
GM

91

Я знайшов корисну бібліотеку tqdm ( https://github.com/tqdm/tqdm/ , раніше: https://github.com/noamraph/tqdm ). Він автоматично оцінює час завершення і може використовуватися як ітератор.

Використання:

import tqdm
import time

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

Призводить до:

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

tqdm може обгортати будь-який ітерабельний.



1
Уау, цей tqdm просто дивовижний і такий простий у використанні :).
Сідахмед

6
це легко найпростіше рішення
kd88

2
він також поставляється в комплекті з дистрибутором Anaconda! (що стосується пітона 3,5 і вище принаймні)
Жакот

Дякую, дивовижно, просто та ефективно
DavideL

23

Ви можете використовувати \r( повернення вагона ). Демонстрація:

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

Спробуйте totalяк 10, тоді ви отримаєте повідомлення про помилкуZeroDivisionError: long division or modulo by zero
unseen_rider

19

Тут ви можете використовувати наступний код як функцію:

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

За допомогою .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()

Для мене перший рядок не змінюється курсором - лише другий рядок. Отже, кілька рядків, отриманих для панелі прогресу, і одна для, наприклад,] percent * 100 %
unseen_rider

6

Грунтуючись на вищенаведених відповідях та інших подібних питаннях щодо панелі прогресу CLI, я думаю, що я отримав загальну загальну відповідь на всі вони. Перевірте це за адресою https://stackoverflow.com/a/15860757/2254146

Ось копія функції, але змінена відповідно до вашого стилю:

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

Виглядає наче

Відсоток: [====================] 99,0%


Рядок прогресу для мене не з’являється
unseen_rider

Я думаю, що не потрібно стільки знаків після коми, тобто кругла (прогрес * 100,2)
Андрес Санчес

4

Якщо ви розробляєте інтерфейс командного рядка, пропоную вам поглянути на clickце дуже добре:

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)

Ось вихід, який ви отримуєте:

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

4

Подальше вдосконалення, використовуючи функцію як:

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

приклад виклику:

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

вихід:

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

ідеально! і ніякі модулі не потрібні
yurividal

3

Я натрапив на цю нитку сьогодні і після того, як випробував це рішення у Марка Рушакова

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)

Я можу сказати, що це добре працює на W7-64 з 64-бітним пітоном 3.4.3, але лише на рідній консолі. Однак при використанні вбудованої консолі шпидера 3.0.0dev перерив рядків все ще / знову присутній. Оскільки це знадобило мені певний час, щоб розібратися, я хотів би повідомити про це спостереження тут.


2

Спираючись на деякі відповіді тут і в інших місцях, я написав цю просту функцію, яка відображає смугу прогресу та пройшов / оцінив час, що залишився. Потрібно працювати на більшості машин на базі 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

Це досить простий підхід, який можна використовувати з будь-яким циклом.

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

Що робить "#"?
unseen_rider

@unseen_rider Тут "#" - це лише символ, який буде повторюватися в міру збільшення ітерації циклу.
НІЛЕШ КУМАР

2

Найпростіше все-таки

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

Ключовим є перетворення цілого типу в рядок.


2

Як описано в рішенні Марка Рушакова , ви можете вивести символ повернення каретки sys.stdout.write('\r'), щоб скинути курсор на початок рядка. Щоб узагальнити це рішення, одночасно реалізуючи f-Strings Python 3 , ви можете використовувати

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

Спробуйте PyProg. PyProg - це бібліотека з відкритим кодом для Python для створення супер настроюваних показників та барів прогресу.

Наразі він знаходиться у версії 1.0.2; він розміщений на Github та доступний на PyPI (Посилання нижче). Він сумісний з Python 3 & 2, а також може використовуватися з консоллю Qt.

Це дуже просто у використанні. Наступний код:

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

вийде саме те, що ви хочете (навіть довжина планки!):

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

Щоб дізнатися більше про налаштування панелі прогресу, перейдіть на сторінку Github цього веб-сайту.

Насправді я створив PyProg, тому що мені потрібна була проста, але супер настроювана бібліотека бар прогресу. Ви можете легко встановити з допомогою : pip install pyprog.

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


1

Використовуючи відповідь @ Mark-Rushakoff, я розробив простіший підхід, не потрібно викликати бібліотеку sys. Він працює з Python 3. Тестований у 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)

Як описано у відповіді на інше запитання print , це тонка обгортка, яка форматує вхід і викликає функцію запису певного об'єкта. За замовчуванням цей об’єкт є sys.stdout.
Стівен К. Хоуелл

0

Ось щось я зробив, використовуючи рішення @ Mark-Rushakoff. Для адаптивного налаштування під ширину клеми.

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("")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.