Как сделать еще прогресс в python?

Мне нужно создать индикатор выполнения на Python, который будет распечатан на x%.
Так, например, если значение составляет 62%, есть ли способ, чтобы он мог распечатать что-то вроде этого?

▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯ 62%

Или, например, 23%

▮▮▮▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯ 23%

Ответ 1

Чистое решение с использованием параметра end print():

Оператор print() позволяет указать, какие последние chars будут напечатаны в конце вызова. По умолчанию для параметра end установлено значение '\r\n', которое является возвратом каретки и новой линией. Это перемещает "курсор" в начало следующей строки, чего вы хотели бы в большинстве случаев.

Однако для индикатора выполнения вы хотите иметь возможность распечатывать обратно по панели, чтобы вы могли изменить текущую позицию и процент, без конечного результата, выглядящего плохо на нескольких строках.

Для этого нам нужно установить параметр end только для возврата каретки: '\r'. Это вернет нашу текущую позицию в начало строки, чтобы мы могли re-print на индикаторе выполнения.

Как демонстрация того, как это может работать, я сделал этот простой маленький код, который увеличит индикатор выполнения на 1% каждые 0.5 секунды. Ключевой частью кода является оператор print, так что это часть, которую вы, вероятно, уберете и поместите в свой основной script.

import time, math

bl = 50  #the length of the bar
for p in range(101):
   chars = math.ceil(p * (bl/100))
   print('▮' * chars + '▯' * (bl-chars), str(p) + '%', end='\r')
   time.sleep(0.5)

(nb используемые здесь символы будут скопированы из вашего вопроса ( "▮, ▯" ), и для меня они не печатались на консоли, и в этом случае вам нужно будет заменить их на другие: "█" и '')

Этот код делает то, что вы хотите, и постоянно увеличивает процент, вот пример того, как он будет выглядеть, когда p = 42:

▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯ 42%

Ответ 2

Основываясь на вашем вопросе и Ответ Джо Иддона, я сделал еще один шаг, основываясь на своем понимании от возиться с tqdm. Как отметил Irmen de Jong, если вы не против использовать библиотеку для этого tqdm, это классно.

Панель прогресса с градиентными блоками + обновление масштабирования терминала (python 3.3 +)

Это примерное решение, если вы хотите создать его с нуля (будет работать на Python 3.3+ для более ранних версий):

import os
import time

# This will only work for python 3.3+ due to use of
# os.get_terminal_size the print function etc.

FULL_BLOCK = '█'
# this is a gradient of incompleteness
INCOMPLETE_BLOCK_GRAD = ['░', '▒', '▓']

def progress_percentage(perc, width=None):
    assert(isinstance(perc, float))
    assert(0. <= perc <= 100.)
    # if width unset use full terminal
    if width is None:
        width = os.get_terminal_size().columns
    # progress bar is block_widget separator perc_widget : ####### 30%
    max_perc_widget = '[100.00%]' # 100% is max
    separator = ' '
    blocks_widget_width = width - len(separator) - len(max_perc_widget)
    assert(blocks_widget_width >= 10) # not very meaningful if not
    perc_per_block = 100.0/blocks_widget_width
    # epsilon is the sensitivity of rendering a gradient block
    epsilon = 1e-6
    # number of blocks that should be represented as complete
    full_blocks = int((perc + epsilon)/perc_per_block)
    # the rest are "incomplete"
    empty_blocks = blocks_widget_width - full_blocks

    # build blocks widget
    blocks_widget = ([FULL_BLOCK] * full_blocks)
    blocks_widget.extend([INCOMPLETE_BLOCK_GRAD[0]] * empty_blocks)
    # marginal case - remainder due to how granular our blocks are
    remainder = perc - full_blocks*perc_per_block
    # epsilon needed for rounding errors (check would be != 0.)
    # based on reminder modify first empty block shading
    # depending on remainder
    if remainder > epsilon:
        grad_index = int((len(INCOMPLETE_BLOCK_GRAD) * remainder)/perc_per_block)
        blocks_widget[full_blocks] = INCOMPLETE_BLOCK_GRAD[grad_index]

    # build perc widget
    str_perc = '%.2f' % perc
    # -1 because the percentage sign is not included
    perc_widget = '[%s%%]' % str_perc.ljust(len(max_perc_widget) - 3)

    # form progressbar
    progress_bar = '%s%s%s' % (''.join(blocks_widget), separator, perc_widget)
    # return progressbar as string
    return ''.join(progress_bar)

def test_pbar(width=None):
    import random
    random.seed(42)
    i = 0
    while(i < 10000):
        print(progress_percentage(i/100, width=width), end='\r')
        time.sleep(0.25)
        i += random.randrange(3000)
    print(progress_percentage(100.00, width=width), end='\r')
    print('\nDone!')

if __name__ == "__main__":
    print('=== First test with width = 20 ===')
    test_pbar(width=20)
    print('=== Now with terminal width ===')
    test_pbar(width=None)

Вот как должен выглядеть вывод при запуске с различными размерами терминалов (tmux panes): Выполняется код в разных размерах терминала (tmux panes)

Ответ 3

Я всегда нашел tqdm библиотеку, очень удобную для этого. Он маленький, имеет низкие накладные расходы и очень прост в использовании: просто обертывание итерации часто делает работу уже.

Пример

from tqdm import tqdm
for i in tqdm(range(10000)):
    ...