Uscita sulla stessa riga sovrascrivendo l'output precedente?


95

Sto scrivendo un downloader FTP. Parte del codice è qualcosa del genere:

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

Sto chiamando il processo della funzione per gestire la richiamata:

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

e l'output è qualcosa del genere:

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

ma voglio che stampi questa riga e la prossima volta lo ristampi / aggiorni in modo che lo mostrerà solo una volta e vedrò lo stato di avanzamento del download.

Come si può fare?



Possibile duplicato della barra di avanzamento
Alexey

Risposte:


185

Ecco il codice per Python 3.x:

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

La end=parola chiave è ciò che funziona qui: per impostazione predefinita, print()termina con un carattere newline ( \n), ma questo può essere sostituito con una stringa diversa. In questo caso, terminare la riga con un ritorno a capo riporta invece il cursore all'inizio della riga corrente. Pertanto, non è necessario importare il sysmodulo per questo tipo di utilizzo semplice. print()ha effettivamente una serie di argomenti di parole chiave che possono essere utilizzati per semplificare notevolmente il codice.

Per utilizzare lo stesso codice su Python 2.6+, inserisci la seguente riga all'inizio del file:

from __future__ import print_function

6
Si noti che la funzione di stampa può essere utilizzata anche in Python 2.6 + aggiungendo la seguente importazione nella parte superiore del file: from __future__ import print_function.
gennaio

12
in python <3.0 una virgola alla fine print "foo",sys.stdout.flush()
dell'istruzione

30
Ho scoperto che avevo bisogno di includere \rall'inizio della stringa e impostare end=''invece per farlo funzionare. Non credo che al mio terminale piaccia quando \r
finisco

2
In tutte le versioni correnti di Python 3, è possibile aggiungere flush=Truealla print()funzione per assicurarsi che il buffer venga scaricato (il buffer di riga standard viene scaricato solo quando \nviene scritta una nuova riga).
Martijn Pieters

4
In un notebook jupyter, l'utilizzo \rdi all'inizio e ha end=''funzionato meglio e senza intoppi. Usando flush=Truecon \ralla fine se la stringa e end='\r'anche ha funzionato ma non è stato regolare e ha cancellato la linea e il suo avanzamento riga.
Dave X

40

Se tutto ciò che vuoi fare è cambiare una singola riga, usa \r. \rsignifica ritorno a capo. Il suo effetto è solo quello di rimettere il cursore all'inizio della riga corrente. Non cancella nulla. Allo stesso modo, \bpuò essere utilizzato per andare indietro di un carattere. (alcuni terminali potrebbero non supportare tutte queste funzionalità)

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
aggiungerei che \rsignifica ritorno a capo. il suo effetto è solo quello di rimettere il cursore all'inizio della riga corrente. non cancella nulla. allo stesso modo, \bpuò essere utilizzato per andare indietro di un carattere. (alcuni terminali potrebbero non supportare tutte queste funzionalità)
Adrien Plisson

sys.stdout.write('%s\r', size_str')Penso che intendessi sys.stdout.write('%s\r' % size_str)ma non funziona.
Kristian

@Adrien: Cool, ha aggiunto quello. @Kristian: Grazie, aggiornato, era piuttosto tardi.
Sam Dolan

Nota anche che puoi ancora usare al printposto di sys.stdout.writese preferisci: print size_str + "\r",funziona bene. Alla ,fine dell'istruzione print sopprime la nuova riga; sys.stdout.flush()è ancora necessario
Jeff

19

Dai un'occhiata alla documentazione del modulo curses e al modulo curses HOWTO .

Esempio davvero semplice:

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
sfortunatamente, curses è disponibile solo su Unix. l'OP non ci ha detto a quale sistema operativo si rivolge la sua applicazione ...
Adrien Plisson

Sono così abituato a lavorare sotto Linux che non ho nemmeno notato l'avvertimento sul modulo che è solo per UNIX nella documentazione del modulo .. Grazie per averlo sottolineato, @Adrien.
Andrea Spadaccini

3
@AdrienPlisson, so che questa domanda è stata posta anni fa, ma puoi effettivamente portare Curses su Windows: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz

Quando l'ho incollato nel mio interprete Python, il mio terminale è diventato fubar. Uscire dall'interprete non ha aiutato. WTF ?!
allyourcode

Utilizzare clrtoeolper quando la seconda riga è più corta della prima.
Serge Stroobandt

9

Ecco la mia piccola classe che può ristampare blocchi di testo. Cancella correttamente il testo precedente in modo da poter sovrascrivere il vecchio testo con un nuovo testo più breve senza creare confusione.

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
Questo non funzionerà in Windows fino a Windows 10, perché Windows 10 è la prima finestra a supportare quei caratteri di controllo en.wikipedia.org/wiki/ANSI_escape_code
user136036

8

Ho scoperto che per una semplice istruzione print in python 2.7, metti semplicemente una virgola alla fine dopo il tuo '\r'.

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

È più breve di altre soluzioni non Python 3, ma anche più difficile da mantenere.


1
Python 2.7.10 e non stampa nulla. Forse puoi compilarlo online e inviarci un link per vedere come funziona ...?
Shougo Makishima

5

Puoi semplicemente aggiungere "\ r" alla fine della stringa più una virgola alla fine della funzione di stampa. Per esempio:

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

1
Questo è per Python 3? Se è così, cosa con il endparametro predefinito impostato su \nso u print (...)KB downloaded!\r\n. Ho sbagliato?
SR

L'utilizzo end = '\r'invece risolve il problema in Python 3.
Abhimanyu Pallavi Sudhir

4

Sto usando Spyder 3.3.1 - Windows 7 - Python 3.6 anche se il flush potrebbe non essere necessario. in base a questo messaggio: 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

per sovrascrivere la riga precedente in python tutto ciò di cui hai bisogno è aggiungere end = '\ r' alla funzione di stampa, prova questo esempio:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.