Вывод в ту же строку, что и предыдущий вывод?

103

Пишу загрузчик FTP. Часть кода выглядит примерно так:

ftp.retrbinary("RETR " + file_name, process)

Я вызываю функциональный процесс для обработки обратного вызова:

def process(data):
    print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    file.write(data)

и вывод будет примерно таким:

1784  KB / KB 1829 downloaded!
1788  KB / KB 1829 downloaded!
etc...   

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

Как это сделать?

Кристиан
источник
Дубликат индикатора выполнения текста в консоли .
Алексей
Возможный дубликат Text Progress Bar в консоли
Алексей

Ответы:

200

Вот код для Python 3.x:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!', end='\r')

Здесь работает end=ключевое слово - по умолчанию print()оканчивается символом новой строки ( \n), но его можно заменить другой строкой. В этом случае завершение строки символом возврата каретки возвращает курсор в начало текущей строки. Таким образом, нет необходимости импортировать sysмодуль для такого простого использования. print()на самом деле имеет ряд аргументов ключевого слова, которые можно использовать для значительного упрощения кода.

Чтобы использовать тот же код в Python 2.6+, поместите следующую строку вверху файла:

from __future__ import print_function
Кудзу
источник
6
Обратите внимание , что функция печати также может быть использована в питоне 2.6+, добавив следующий импорт в верхней части файла: from __future__ import print_function.
janin
12
в python <3.0 запятая в конце оператора предотвратит появление "\ n": print "foo",однако вам все равно нужно промывать после этого, чтобы увидеть изменение:sys.stdout.flush()
Tobias Domhan
32
Я обнаружил, что мне нужно включить \rв начало строки и установить end=''вместо этого, чтобы это работало. Не думаю, что моему терминалу нравится, когда я заканчиваю\r
Джеззамон
3
Во всех текущих выпусках Python 3 вы можете добавить flush=Trueк print()функции, чтобы буфер был очищен (стандартный строчный буфер будет очищаться только при записи \nновой строки).
Мартин Питерс
4
В ноутбуке jupyter использование \rв начале end=''работало лучше всего и плавнее. Использование flush=Trueс \rв конце, если строка и end='\r'также работали, но не были гладкими и стирали линию и ее перевод строки.
Dave X
40

Если все, что вам нужно сделать, это изменить одну строку, используйте \r. \rозначает возврат каретки. Его эффект заключается исключительно в том, чтобы вернуть курсор в начало текущей строки. Ничего не стирает. Точно так же \bможно использовать для перехода на один символ назад. (некоторые терминалы могут не поддерживать все эти функции)

import sys

def process(data):
    size_str = os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    sys.stdout.write('%s\r' % size_str)
    sys.stdout.flush()
    file.write(data)
Сэм Долан
источник
1
Я бы добавил, что это \rозначает возврат каретки. его эффект заключается исключительно в том, чтобы вернуть курсор в начало текущей строки. он ничего не стирает. аналогично, \bможно использовать для перехода на один символ назад. (некоторые терминалы могут не поддерживать все эти функции)
Адриен Плиссон
sys.stdout.write('%s\r', size_str')Я думаю, вы имели в виду, sys.stdout.write('%s\r' % size_str)но это не работает.
Кристиан
@ Адриен: Круто, добавил это. @Kristian: Спасибо, обновил, было уже довольно поздно.
Сэм Долан
Также обратите внимание, что вы все равно можете использовать printвместо, sys.stdout.writeесли хотите: print size_str + "\r",работает нормально. Знак ,в конце оператора печати подавляет перевод строки; sys.stdout.flush()по-прежнему нужен
Джефф
19

Посмотрите документацию модуля curses и HOWTO модуля curses .

Действительно базовый пример:

import time
import curses

stdscr = curses.initscr()

stdscr.addstr(0, 0, "Hello")
stdscr.refresh()

time.sleep(1)

stdscr.addstr(0, 0, "World! (with curses)")
stdscr.refresh()
Андреа Спадаччини
источник
10
к сожалению, curses доступен только в Unix. ОП не сообщил нам, на какую операционную систему нацелено его приложение ...
Адриан Плиссон
Я так привык работать под Linux, что даже не заметил предупреждения о том, что модуль предназначен только для UNIX, в документации модуля. Спасибо за указание, @Adrien.
Андреа Спадаччини
3
@AdrienPlisson, я знаю, что этот вопрос был задан много лет назад, но вы действительно можете получить Curses в Windows: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz
Когда я вставил это в свой интерпретатор Python, мой терминал стал fubar. Выход из интерпретатора не помог. Какого черта ?!
allyourcode
Используйте clrtoeolдля , когда вторая линия короче , чем первый.
Serge
9

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

import re, sys

class Reprinter:
    def __init__(self):
        self.text = ''

    def moveup(self, lines):
        for _ in range(lines):
            sys.stdout.write("\x1b[A")

    def reprint(self, text):
        # Clear previous text by overwritig non-spaces with spaces
        self.moveup(self.text.count("\n"))
        sys.stdout.write(re.sub(r"[^\s]", " ", self.text))

        # Print new text
        lines = min(self.text.count("\n"), text.count("\n"))
        self.moveup(lines)
        sys.stdout.write(text)
        self.text = text

reprinter = Reprinter()

reprinter.reprint("Foobar\nBazbar")
reprinter.reprint("Foo\nbar")
Буке Верстех
источник
2
Это не будет работать в Windows вплоть до Windows 10, потому что Windows 10 - первое окно, поддерживающее эти управляющие символы en.wikipedia.org/wiki/ANSI_escape_code
user136036 03
8

Я обнаружил, что для простого оператора печати в python 2.7 просто поставьте запятую в конце после вашего '\r'.

print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!\r',

Это короче, чем другие решения, отличные от Python 3, но и сложнее в обслуживании.

Мэтт Эллен
источник
1
Python 2.7.10, и он ничего не печатает. Может быть, скомпилировать онлайн и прислать ссылку, чтобы посмотреть, как он работает ...?
Сёго Макисима
5

Вы можете просто добавить '\ r' в конец строки плюс запятую в конце функции печати. Например:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),
Мустафа Салех
источник
1
Это для питона 3? Если да, то что с endпараметром по умолчанию, установленным \nтак, чтобы печатать (...)KB downloaded!\r\n. Я ошибся?
SR
Использование end = '\r'вместо этого устраняет проблему в Python 3.
Абхиманью Паллави Судхир
4

Я использую spyder 3.3.1 - windows 7 - python 3.6, хотя промывка может не понадобиться. на основе этой публикации - https://github.com/spyder-ide/spyder/issues/3437

   #works in spyder ipython console - \r at start of string , end=""
import time
import sys
    for i in range(20):
        time.sleep(0.5)
        print(f"\rnumber{i}",end="")
        sys.stdout.flush()
ДжоПайтонКинг
источник
1

чтобы переопределить предыдущую строку в python, все, что вам нужно, это добавить end = '\ r' в функцию печати, проверьте этот пример:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Амируш Зегга
источник