Ersetzen Sie die Konsolenausgabe in Python

105

Ich frage mich, wie ich in Python wie in bestimmten C / C ++ - Programmen einen dieser raffinierten Konsolenzähler erstellen könnte.

Ich habe eine Schleife, die Dinge erledigt, und die aktuelle Ausgabe ist wie folgt:

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

Was sauberer wäre, wäre, nur die letzte Zeile zu aktualisieren.

X things done.

Ich habe dies in einer Reihe von Konsolenprogrammen gesehen und frage mich, ob / wie ich dies in Python tun würde.

dutt
quelle
3
Sie sollten sich Flüche ansehen .
Björn Pollex
2
Verwenden Sie einfach print: stackoverflow.com/a/8436827/1959808
Ioannis Filippidis
1
@ BjörnPollex, cursesist ein Overkill (siehe die akzeptierte Antwort).
Alexey

Antworten:

150

Eine einfache Lösung besteht darin, nur "\r"vor die Zeichenfolge zu schreiben und keine neue Zeile hinzuzufügen. Wenn die Saite nie kürzer wird, ist dies ausreichend ...

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

Etwas ausgefeilter ist ein Fortschrittsbalken ... das ist etwas, das ich benutze:

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

Sie rufen an, startProgressindem Sie die Beschreibung der Operation übergeben, progress(x)wo xist dann der Prozentsatz und schließlichendProgress()

6502
quelle
2
Was ist, wenn die Zeichenfolge kürzer als die vorherige ist?
Math2001
6
@ math2001 Auffüllen mit Leerzeichen.
Felipsmartins
Nur für die ersten 2 Codezeilen gewählt. Der Teil des Fortschrittsbalkens wird in einigen Fällen langsam.
Trotzdem
Einige Programme ( restic, flatpak) können mehrere Zeilen der Konsolenausgabe aktualisieren. Wissen Sie zufällig, wie dies erreicht werden kann?
Alexey
1
@Alexey: Sie können ANSI-Escape-Codes verwenden, um den Cursor zu bewegen, Teile des Bildschirms zu löschen und Farben zu ändern ... siehe en.wikipedia.org/wiki/ANSI_escape_code
6502
39

Eine elegantere Lösung könnte sein:

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

Rufen Sie diese Funktion mit valueund auf endvalue, Ergebnis sollte sein

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

Hinweis: Python 2.x-Version hier .

Aravind Voggu
quelle
Sie sollten Halo für bessere Fortschrittsbalken und Spinner verwenden.
Aravind Voggu
17

In Python 3 können Sie dies tun, um in derselben Zeile zu drucken:

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

Besonders nützlich, um die neuesten Updates und Fortschritte zu verfolgen.

Ich würde auch tqdm von hier aus empfehlen , wenn man den Fortschritt einer Schleife sehen möchte. Es druckt die aktuelle Iteration und die Gesamtiterationen als Fortschrittsbalken mit einer erwarteten Endzeit. Super nützlich und schnell. Funktioniert für Python2 und Python3.

Joop
quelle
7

Die andere Antwort mag besser sein, aber hier ist, was ich getan habe. Zuerst habe ich eine Funktion namens progress erstellt, die das Backspace-Zeichen druckt:

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

Dann habe ich es in einer Schleife in meiner Hauptfunktion so aufgerufen:

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

Dadurch wird natürlich die gesamte Zeile gelöscht, aber Sie können damit herumspielen, um genau das zu tun, was Sie wollen. Mit dieser Methode habe ich einen Fortschrittsbalken erstellt.

Bryce Siedschlaw
quelle
4
Funktioniert, aber wenn die vorherige Zeile mehr Zeichen als die nächste hatte, bleiben die Zeichen nach dem Ende der neuen Zeile von der vorherigen Zeile übrig: "Rechtschreibprüfungsdatensatz 417/701 [Leibeigenschaft auf Oberfläche geändert], wenn] Umineszenz] Cence] Shmentarismus] "
Lil 'Bits
7

Für alle, die dieses Jahr später darauf stoßen (wie ich), habe ich die Methoden von 6502 ein wenig optimiert, damit der Fortschrittsbalken sowohl verringert als auch erhöht werden kann. Nützlich in etwas mehr Fällen. Danke 6502 für ein tolles Tool!

Grundsätzlich besteht der einzige Unterschied darin, dass bei jedem Aufruf von progress (x) die gesamte Zeile mit #s und -s geschrieben wird und der Cursor immer zum Anfang des Balkens zurückkehrt.

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()
jat255
quelle
1
Ich habe jedoch festgestellt, dass dies einige Verlangsamungen verursachen kann, wenn es zu häufig vom Code aufgerufen wird, also denke ich, YMMV
jat255
6

Wenn ich gut verstanden habe (nicht sicher), möchten Sie mit drucken <CR>und nicht <LR>?

Wenn dies möglich ist, solange das Konsolenterminal dies zulässt (es wird unterbrochen, wenn die Ausgabe in eine Datei umgeleitet wird).

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

Wenn wir uns die print()Funktion ansehen, kann dies ohne Verwendung der sys-Bibliothek erfolgen

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

Hier ist mein Code:

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

Ich habe das vor einiger Zeit geschrieben und bin sehr zufrieden damit. Fühlen Sie sich frei, es zu benutzen.

Es dauert ein indexund totalund optional titleoder bar_length. Ersetzen Sie anschließend die Sanduhr durch ein Häkchen.

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

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

Ich habe ein Beispiel beigefügt, das zum Testen ausgeführt werden kann.

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)

Ich habe auch eine Version mit reaktionsschnellem Fortschrittsbalken, abhängig von der Terminalbreite, shutil.get_terminal_size()wenn dies von Interesse ist.

Ivan Procopovich
quelle
4

Dem Beispiel von Aravind Voggu wurde etwas mehr Funktionalität hinzugefügt :

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

Jetzt können Sie mehrere Fortschrittsbalken generieren, ohne den vorherigen zu ersetzen.

Ich habe auch nameals Wert mit einer festen Breite hinzugefügt .

Für zwei Schleifen und zweimal progressBar()sieht die Verwendung des Ergebnisses folgendermaßen aus:

Fortschrittsbalkenanimation

Nils Kohlmey
quelle
-1

Der folgende Code zählt die Nachricht von 0 bis 137 alle 0,3 Sekunden und ersetzt die vorherige Nummer.

Anzahl der Symbole hinter der Bühne = Anzahl der Ziffern.

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)
laggerok19
quelle