Sostituisci l'output della console in Python


106

Mi chiedo come potrei creare uno di quegli ingegnosi contatori di console in Python come in alcuni programmi C / C ++.

Ho un ciclo che fa le cose e l'output corrente è sulla falsariga di:

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

quello che sarebbe più ordinato sarebbe avere solo l'aggiornamento dell'ultima riga;

X things done.

L'ho visto in una serie di programmi per console e mi chiedo se / come lo farei in Python.


3
Dovresti dare un'occhiata alle maledizioni .
Björn Pollex


1
@ BjörnPollex, cursesè eccessivo (vedi la risposta accettata).
Alexey

Risposte:


151

Una soluzione semplice è semplicemente scrivere "\r"prima della stringa e non aggiungere una nuova riga; se la stringa non si accorcia mai è sufficiente ...

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

Un po 'più sofisticata è una barra di avanzamento ... questo è qualcosa che sto usando:

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

Chiami startProgresspassando la descrizione dell'operazione, poi progress(x)dov'è xla percentuale e infineendProgress()


2
E se la stringa è più corta della precedente?
math2001

6
@ math2001 riempimento con spazi bianchi.
felipsmartins

Votato solo per le prime 2 righe di codice. La parte della barra di avanzamento sta diventando lenta in alcuni casi. Comunque grazie @ 6502
WaterRocket8236

Alcuni programmi ( restic, flatpak) possono aggiornare diverse righe di output della console. Sai per caso come si può ottenere questo risultato?
Alexey

1
@Alexey: puoi usare i codici di escape ANSI per spostare il cursore, cancellare parti dello schermo e cambiare i colori ... vedi en.wikipedia.org/wiki/ANSI_escape_code
6502

39

Una soluzione più elegante potrebbe essere:

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

chiama questa funzione con valuee endvalue, il risultato dovrebbe essere

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

Nota: versione Python 2.x qui .


Dovresti usare Halo per migliorare le barre di avanzamento e gli spinner.
Aravind Voggu

18

In python 3 puoi farlo per stampare sulla stessa riga:

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

Particolarmente utile per tenere traccia degli ultimi aggiornamenti e progressi.

Consiglierei anche tqdm da qui se si vuole vedere l'avanzamento di un ciclo. Stampa l'iterazione corrente e le iterazioni totali come una barra di progressione con un tempo di fine previsto. Super utile e veloce. Funziona per python2 e python3.


7

L'altra risposta potrebbe essere migliore, ma ecco cosa stavo facendo. Per prima cosa, ho creato una funzione chiamata progress che stampa il carattere backspace:

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

Quindi l'ho chiamato in un ciclo nella mia funzione principale in questo modo:

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

Questo ovviamente cancellerà l'intera riga, ma puoi scherzare per fare esattamente quello che vuoi. Ho finito per creare una barra di avanzamento usando questo metodo.


4
Funziona, ma se la riga precedente aveva più caratteri della successiva, i caratteri dopo la fine della nuova riga rimangono dalla riga precedente: "Record controllo ortografico 417/701 [servizio cambiato in superficie] quando] uminescenza] cence] shmentarianism] "
Lil 'Bits

7

Per chiunque si imbattesse in questo anno dopo (come ho fatto io), ho ottimizzato un po 'i metodi di 6502 per consentire alla barra di avanzamento di diminuire oltre che aumentare. Utile in un numero leggermente maggiore di casi. Grazie 6502 per un ottimo strumento!

Fondamentalmente, l'unica differenza è che l'intera riga di #s e -s viene scritta ogni volta che viene chiamato progress (x) e il cursore viene sempre riportato all'inizio della barra.

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
Tuttavia, ho scoperto che questo può causare alcuni rallentamenti se viene chiamato troppo frequentemente dal codice, quindi immagino
YMMV

6

Se ho capito bene (non sono sicuro) vuoi stampare usando <CR>e no <LR>?

Se è così è possibile, fintanto che il terminale della console lo consente (si interromperà quando l'output viene reindirizzato a un file).

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

5

Può essere fatto senza usare la libreria sys se guardiamo la print()funzione

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

Ecco il mio codice:

def update(n):
    for i in range(n):
        print("i:",i,sep='',end="\r",flush=True)
        #time.sleep(1)

5

L'ho scritto qualche tempo fa e ne sono davvero felice. Sentiti libero di usarlo.

Ci vuole un indexand totale opzionalmente titleo bar_length. Una volta terminato, sostituisce la clessidra con un segno di spunta.

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

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

Ho incluso un esempio che può essere eseguito per testarlo.

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)

Ho anche una versione con barra di avanzamento reattiva a seconda della larghezza del terminale utilizzata shutil.get_terminal_size()se questo è di interesse.


4

Aggiunte un po 'più di funzionalità all'esempio di 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')

Ora puoi generare più barre di avanzamento senza sostituire quella precedente.

Ho anche aggiunto nameun valore con una larghezza fissa.

Per due cicli e due volte l'uso del progressBar()risultato sarà simile a:

animazione della barra di avanzamento


-1

Il codice seguente conterà il messaggio da 0 a 137 ogni 0,3 secondi sostituendo il numero precedente.

Numero di simboli sul backstage = numero di cifre.

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