¿Salida a la misma línea que sobrescribe la salida anterior?

97

Estoy escribiendo un descargador de FTP. Parte del código es algo como esto:

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

Estoy llamando al proceso de función para manejar la devolución de llamada:

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

y la salida es algo como esto:

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

pero quiero que imprima esta línea y la próxima vez la vuelva a imprimir / actualice para que solo la muestre una vez y veré el progreso de esa descarga.

¿Cómo puede hacerse esto?

Kristian
fuente
Duplicado de la barra de progreso de texto en la consola .
Alexey
Posible duplicado de la barra de progreso
Alexey

Respuestas:

189

Aquí está el código para Python 3.x:

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

La end=palabra clave es lo que funciona aquí; de forma predeterminada, print()termina en un carácter de nueva línea ( \n), pero esto se puede reemplazar con una cadena diferente. En este caso, terminar la línea con un retorno de carro devuelve el cursor al inicio de la línea actual. Por lo tanto, no es necesario importar el sysmódulo para este tipo de uso simple. print()en realidad tiene una serie de argumentos de palabras clave que pueden usarse para simplificar mucho el código.

Para usar el mismo código en Python 2.6+, coloque la siguiente línea en la parte superior del archivo:

from __future__ import print_function
Kudzu
fuente
6
Tenga en cuenta que la función de impresión también se puede utilizar en Python 2.6+, añadiendo la siguiente import al principio del archivo: from __future__ import print_function.
janin
12
en python <3.0, una coma al final de la declaración evitará un "\ n": print "foo",Sin embargo, aún necesita descargar después de eso para ver el cambio:sys.stdout.flush()
Tobias Domhan
31
Descubrí que necesitaba incluir el \ral comienzo de la cadena y configurarlo end=''para que esto funcione. No creo que a mi terminal le guste cuando termino con\r
Jezzamon
2
En todas las versiones actuales de Python 3, puede agregar flush=Truea la print()función para asegurarse de que el búfer se vacíe (el búfer de línea estándar solo se eliminaría cuando \nse escribe una nueva línea).
Martijn Pieters
4
En un cuaderno jupyter, usar el \ral principio y end=''funcionó mejor y más suave. Usar flush=Truecon \ral final si la cuerda y end='\r'también funcionó pero no fue suave y borró la línea y su salto de línea.
Dave X
40

Si todo lo que desea hacer es cambiar una sola línea, use \r. \rsignifica retorno de carro. Su efecto es únicamente volver a colocar el símbolo de intercalación al comienzo de la línea actual. No borra nada. De manera similar, \bse puede usar para retroceder un carácter. (es posible que algunos terminales no admitan todas esas funciones)

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)
Sam Dolan
fuente
1
Yo agregaría que \rsignifica retorno de carro. su efecto es únicamente volver a colocar el símbolo de intercalación al comienzo de la línea actual. no borra nada. de manera similar, \bse puede usar para retroceder un carácter. (Es posible que algunos terminales no admitan todas esas funciones)
Adrien Plisson
sys.stdout.write('%s\r', size_str')Creo que te refieres a sys.stdout.write('%s\r' % size_str)pero no funciona.
Kristian
@Adrien: Genial, agregó eso. @Kristian: Gracias, actualicé eso, era bastante tarde.
Sam Dolan
También tenga en cuenta que aún puede usar en printlugar de sys.stdout.writesi lo prefiere: print size_str + "\r",funciona bien. El ,al final de la declaración de impresión suprime la nueva línea; sys.stdout.flush()todavía se necesita
Jeff
19

Eche un vistazo a la documentación del módulo de curses y al HOWTO del módulo de curses .

Ejemplo realmente básico:

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()
Andrea Spadaccini
fuente
10
desafortunadamente, curses solo está disponible en Unix. el OP no nos dijo a qué sistema operativo se dirige su aplicación ...
Adrien Plisson
Estoy tan acostumbrado a trabajar en Linux que ni siquiera noté la advertencia de que el módulo es solo para UNIX en los documentos del módulo. Gracias por señalar eso, @Adrien.
Andrea Spadaccini
3
@AdrienPlisson, sé que esta pregunta se hizo hace años, pero, de hecho, puede obtener Curses en Windows: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz
Cuando pegué esto en mi intérprete de Python, mi terminal se convirtió en fubar. Salir del intérprete no ayudó. ¡¿WTF ?!
allyourcode
Úselo clrtoeolpara cuando la segunda línea sea más corta que la primera.
Serge Stroobandt
9

Aquí está mi pequeña clase que puede reimprimir bloques de texto. Borra correctamente el texto anterior para que pueda sobrescribir su texto anterior con texto nuevo más corto sin crear un lío.

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")
Bouke Versteegh
fuente
2
Esto no funcionará en Windows hasta Windows 10, porque Windows 10 es la primera ventana que admite esos caracteres de control en.wikipedia.org/wiki/ANSI_escape_code
user136036
8

Descubrí que para una declaración de impresión simple en Python 2.7, simplemente coloque una coma al final después de su '\r'.

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

Esto es más corto que otras soluciones que no son de Python 3, pero también más difícil de mantener.

Matt Ellen
fuente
1
Python 2.7.10 y no imprime nada. Tal vez, ¿puede compilarlo en línea y enviarnos un enlace para ver cómo funciona ...?
Shougo Makishima
5

Simplemente puede agregar '\ r' al final de la cadena más una coma al final de la función de impresión. Por ejemplo:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),
Moustafa Saleh
fuente
1
¿Esto es para Python 3? Si es así, con el endparámetro predeterminado configurado para \nimprimir (...)KB downloaded!\r\n. ¿Me equivoco?
SR
Usar en su end = '\r'lugar soluciona el problema en Python 3.
Abhimanyu Pallavi Sudhir
4

Estoy usando spyder 3.3.1 - windows 7 - python 3.6 aunque es posible que no se necesite flush. basado en esta publicación - 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()
JoePythonKing
fuente
1

para sobrepasar la línea anterior en Python, todo lo que necesita es agregar end = '\ r' a la función de impresión, pruebe este ejemplo:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Amirouche Zeggagh
fuente