Python для распечатки строки состояния и процентов

160

Для реализации строки состояния, как показано ниже:

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

Я хочу, чтобы это распечатывалось на стандартный вывод и продолжало обновлять его, а не печатать на другую строку. Как это сделать?

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

Ответы:

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.

icktoofay
источник
Все ответы потрясающие, но мне больше всего нравится модуль. Спасибо всем.
Стэн
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))), только для Python 3
GabrielChu
3
@ StevenC. Но почему вы не публикуете это как ответ со ссылкой на Марка? это еще одна версия, и ее очень полезно подготовить
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 может обернуть любой повторяемый.

Omikron
источник
4
Проект tqdm теперь поддерживается здесь новой командой разработчиков.
Габорист
1
Вау, этот TQDM просто удивительно, и так прост в использовании :).
Сидахмед
6
это,
пожалуй,
2
он поставляется в комплекте с дистрибьютором Anaconda! (как для python 3.5 и выше по крайней мере)
Jacquot
Спасибо, удивительно, просто и эффективно
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%
nowox
источник
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  
STG
источник
идеальный! и никаких модулей не требуется
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 с Python 3.4.3 64-битной, но только в родной консоли. Однако при использовании встроенной консоли spyder 3.0.0dev разрывы строк по-прежнему / снова присутствуют. Поскольку мне потребовалось некоторое время, чтобы понять, я хотел бы сообщить об этом наблюдении здесь.

Mr.Bum
источник
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()

Ключ должен преобразовать целочисленный тип в строку.

bfree67
источник
2

Как описано в решении Марка Рушакова , вы можете вывести символ возврата каретки, sys.stdout.write('\r')чтобы сбросить курсор на начало строки. Чтобы обобщить это решение, а также реализовать f-строки 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 Console.

Это действительно легко использовать. Следующий код:

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("")
swarnava112
источник