Заменить вывод консоли в Python

106

Мне интересно, как я мог бы создать один из тех изящных счетчиков консоли в Python, как в некоторых программах на C / C ++.

У меня есть цикл, и текущий результат выглядит следующим образом:

Doing thing 0
Doing thing 1
Doing thing 2
...

что было бы аккуратнее, было бы просто обновить последнюю строку;

X things done.

Я видел это в ряде консольных программ, и мне интересно, как бы я сделал это в Python.

dutt
источник
3
Вы должны взглянуть на проклятия .
Björn Pollex
2
Просто используйте print: stackoverflow.com/a/8436827/1959808
Иоаннис Филиппидис,
1
@ BjörnPollex, cursesэто излишество (см. Принятый ответ).
Алексей

Ответы:

151

Простое решение - просто написать "\r"перед строкой, а не добавлять новую строку; если строка никогда не становится короче, этого достаточно ...

sys.stdout.write("\rDoing thing %i" % i)
sys.stdout.flush()

Немного сложнее индикатор выполнения ... вот что я использую:

def startProgress(title):
    global progress_x
    sys.stdout.write(title + ": [" + "-"*40 + "]" + chr(8)*41)
    sys.stdout.flush()
    progress_x = 0

def progress(x):
    global progress_x
    x = int(x * 40 // 100)
    sys.stdout.write("#" * (x - progress_x))
    sys.stdout.flush()
    progress_x = x

def endProgress():
    sys.stdout.write("#" * (40 - progress_x) + "]\n")
    sys.stdout.flush()

Вы вызываете startProgressпередачу описания операции, затем progress(x)где xпроцент и наконецendProgress()

6502
источник
2
Что делать, если строка короче предыдущей?
math2001
6
@ math2001 заполнение пробелом.
felipsmartins 06
Проголосовал только за первые 2 строчки кода. Часть индикатора выполнения становится медленной в некоторых случаях. В любом случае, спасибо @ 6502
WaterRocket8236
Некоторые программы ( restic, flatpak) могут обновлять несколько строк вывода консоли. Вы случайно не знаете, как этого добиться?
Алексей
1
@Alexey: вы можете использовать escape-коды ANSI для перемещения курсора, очистки частей экрана и изменения цвета ... см. En.wikipedia.org/wiki/ANSI_escape_code
6502,
39

Более элегантным решением может быть:

def progressBar(current, total, barLength = 20):
    percent = float(current) * 100 / total
    arrow   = '-' * int(percent/100 * barLength - 1) + '>'
    spaces  = ' ' * (barLength - len(arrow))

    print('Progress: [%s%s] %d %%' % (arrow, spaces, percent), end='\r')

вызовите эту функцию с помощью valueи endvalue, результат должен быть

Progress: [------------->      ] 69 %

Примечание: версия Python 2.x здесь .

Аравинд Воггу
источник
Вы должны использовать Halo для улучшения индикаторов выполнения и счетчиков.
Аравинд Воггу,
18

В python 3 вы можете сделать это для печати в той же строке:

print('', end='\r')

Особенно полезно отслеживать последние обновления и прогресс.

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

Joop
источник
8

Для тех, кто наткнется на это годы спустя (как и я), я немного изменил методы 6502, чтобы индикатор выполнения уменьшался, а также увеличивался. Полезно в чуть большем количестве случаев. Спасибо 6502 за отличный инструмент!

По сути, единственное отличие состоит в том, что вся строка #s и -s записывается каждый раз, когда вызывается progress (x), и курсор всегда возвращается в начало полосы.

def startprogress(title):
    """Creates a progress bar 40 chars long on the console
    and moves cursor back to beginning with BS character"""
    global progress_x
    sys.stdout.write(title + ": [" + "-" * 40 + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = 0


def progress(x):
    """Sets progress bar to a certain percentage x.
    Progress is given as whole percentage, i.e. 50% done
    is given by x = 50"""
    global progress_x
    x = int(x * 40 // 100)                      
    sys.stdout.write("#" * x + "-" * (40 - x) + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = x


def endprogress():
    """End of progress bar;
    Write full bar, then move to next line"""
    sys.stdout.write("#" * 40 + "]\n")
    sys.stdout.flush()
jat255
источник
1
Однако я обнаружил, что это может вызвать некоторое замедление, если оно слишком часто вызывается кодом, поэтому я предполагаю, что YMMV
jat255
7

Другой ответ может быть лучше, но вот что я делал. Во-первых, я создал функцию под названием progress, которая выводит символ возврата:

def progress(x):
    out = '%s things done' % x  # The output
    bs = '\b' * 1000            # The backspace
    print bs,
    print out,

Затем я вызвал его в цикле своей основной функции следующим образом:

def main():
    for x in range(20):
        progress(x)
    return

Это, конечно, сотрет всю строку, но вы можете повозиться с ней, чтобы делать именно то, что хотите. В итоге я сделал индикатор выполнения, используя этот метод.

Брайс Зидшлав
источник
4
Работает, но если в предыдущей строке было больше символов, чем в следующей, символы после конца новой строки остаются от предыдущей: «Запись проверки орфографии 417/701 [услуга изменена на поверхность], когда] uminescence] cence] shmentarianism] "
Lil 'Bits
6

Если я хорошо понял (не уверен), вы хотите печатать с помощью, <CR>а не <LR>?

Если да, то это возможно, если это позволяет консольный терминал (он сломается, когда вывод si перенаправляется в файл).

from __future__ import print_function
print("count x\r", file=sys.stdout, end=" ")
Сорин
источник
6

Я написал это некоторое время назад и очень им доволен. Не стесняйтесь использовать это.

Требуется indexand totalи необязательно titleили bar_length. После этого заменяет песочные часы галочкой.

⏳ Calculating: [████░░░░░░░░░░░░░░░░░░░░░] 18.0% done

✅ Calculating: [█████████████████████████] 100.0% done

Я включил пример, который можно запустить для проверки.

import sys
import time

def print_percent_done(index, total, bar_len=50, title='Please wait'):
    '''
    index is expected to be 0 based index. 
    0 <= index < total
    '''
    percent_done = (index+1)/total*100
    percent_done = round(percent_done, 1)

    done = round(percent_done/(100/bar_len))
    togo = bar_len-done

    done_str = '█'*int(done)
    togo_str = '░'*int(togo)

    print(f'\t⏳{title}: [{done_str}{togo_str}] {percent_done}% done', end='\r')

    if round(percent_done) == 100:
        print('\t✅')


r = 50
for i in range(r):
    print_percent_done(i,r)
    time.sleep(.02)

У меня также есть версия с отзывчивым индикатором выполнения в зависимости от ширины терминала, shutil.get_terminal_size()если это интересно.

Иван Прокопович
источник
5

Это можно сделать без использования библиотеки sys, если мы посмотрим на print()функцию

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Вот мой код:

def update(n):
    for i in range(n):
        print("i:",i,sep='',end="\r",flush=True)
        #time.sleep(1)
Суман Саураб
источник
4

На примере Aravind Voggu добавлено немного больше функциональности :

def progressBar(name, value, endvalue, bar_length = 50, width = 20):
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent*bar_length) - 1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        sys.stdout.write("\r{0: <{1}} : [{2}]{3}%".format(\
                         name, width, arrow + spaces, int(round(percent*100))))
        sys.stdout.flush()
        if value == endvalue:     
             sys.stdout.write('\n\n')

Теперь вы можете создавать несколько индикаторов выполнения, не заменяя предыдущий.

Я также добавил nameзначение фиксированной ширины.

Для двух циклов и двух раз использование progressBar()результата будет выглядеть так:

прогресс бар анимация

Нильс Кольмей
источник
-1

Код ниже будет считать сообщение от 0 до 137 каждые 0,3 секунды, заменяя предыдущее число.

Количество символов, выводимых за кулисы = количество цифр.

stream = sys.stdout
for i in range(137):
    stream.write('\b' * (len(str(i)) + 10))
    stream.write("Message : " + str(i))
    stream.flush()
    time.sleep(0.3)
лаггерок19
источник