Замініть вихід консолі в Python


106

Мені цікаво, як я міг створити один із цих вишуканих консольних лічильників у Python, як у певних програмах C / C ++.

У мене є цикл, що робить речі, і поточний висновок відповідає лінії:

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

що було б акуратніше, щоб тільки оновити останній рядок;

X things done.

Я бачив це в ряді консольних програм і мені цікаво, чи / як я б це зробив у Python.


3
Вам слід поглянути на прокляття .
Бьорн Поллекс

2
Просто використовуйте print: stackoverflow.com/a/8436827/1959808
Іоанніс Filippidis

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


2
Що робити, якщо рядок коротший за попередній?
math2001

6
@ math2001 підкладка з пробілом.
felipsmartins

Проголосували лише за перші 2 рядки коду. Частина смуги прогресу в декількох випадках стає повільною. У будь-якому випадку дякую @ 6502
WaterRocket8236

Деякі програми ( restic, flatpak) можуть оновлювати кілька рядків консольного виводу. Чи знаєте ви випадково, як цього можна досягти?
Олексій

1
@ Алекс: ви можете використовувати 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 для кращого прогресу барів і прядок.
Аравінд Воггу

17

У python 3 ви можете це зробити для друку в одному рядку:

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

Особливо корисно слідкувати за останнім оновленням та прогресом.

Я також рекомендую tqdm звідси, якщо хоче бачити хід циклу. Він друкує поточну ітерацію та загальні ітерації як смугу прогресування з очікуваним часом завершення. Супер корисний і швидкий. Працює для python2 та python3.


7

Інша відповідь може бути кращою, але ось що я робив. По-перше, я зробив функцію під назвою "прогрес", яка друкує символ задньої області:

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 [serfice змінено на поверхню], коли] uminescence] cence] shmentarianism] "
Lil 'Bits

7

Для тих, хто натрапляє на це років пізніше (як і я), я трохи змінив методи 6502, щоб дозволити зменшити та збільшити смугу прогресу. Корисний у трохи більше випадків. Дякую 6502 за чудовий інструмент!

По суті, різниця лише в тому, що весь рядок #s і -s пишеться кожного разу, коли викликається прогрес (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()

1
Однак я виявив, що це може спричинити уповільнення, якщо його закликають занадто часто кодом, тому я здогадуюсь YMMV
jat255

6

Якщо я добре зрозумів (не впевнений), ви хочете друкувати за допомогою, <CR>а ні <LR>?

Якщо так, це можливо, доки термінал консолі дозволяє це (він порушиться, коли вихід буде перенаправлений у файл).

from __future__ import print_function
print("count x\r", file=sys.stdout, end=" ")

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)

5

Я написав це деякий час тому і дуже задоволений цим. Сміливо користуйтеся ним.

Він приймає indexі, 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()якщо це цікавить.


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