Ausgabe in dieselbe Zeile, die die vorherige Ausgabe überschreibt?

94

Ich schreibe einen FTP-Downloader. Ein Teil des Codes ist ungefähr so:

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

Ich rufe den Funktionsprozess auf, um den Rückruf zu behandeln:

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

und die Ausgabe ist ungefähr so:

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

Ich möchte jedoch, dass diese Zeile gedruckt und beim nächsten Mal erneut gedruckt / aktualisiert wird, sodass sie nur einmal angezeigt wird und der Fortschritt dieses Downloads angezeigt wird.

Wie geht das?

Kristian
quelle
Mögliches Duplikat der Textfortschrittsleiste in der Konsole
Alexey

Antworten:

185

Hier ist Code für Python 3.x:

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

Das end=Schlüsselwort ist das, was hier funktioniert - standardmäßig print()endet es mit einem newline ( \n) - Zeichen, das jedoch durch eine andere Zeichenfolge ersetzt werden kann. In diesem Fall setzt das Beenden der Zeile mit einem Wagenrücklauf stattdessen den Cursor an den Anfang der aktuellen Zeile. Daher ist es nicht erforderlich, das sysModul für diese einfache Verwendung zu importieren . print()hat tatsächlich eine Reihe von Schlüsselwortargumenten, mit denen Code erheblich vereinfacht werden kann.

Um denselben Code unter Python 2.6+ zu verwenden, setzen Sie die folgende Zeile oben in die Datei:

from __future__ import print_function
Kudzu
quelle
6
Beachten Sie, dass die Druckfunktion auch in Python 2.6+ verwendet werden kann, indem der folgende Import oben in die Datei eingefügt wird : from __future__ import print_function.
Janin
12
In Python <3.0 verhindert ein Komma am Ende der Anweisung ein "\ n": print "foo",Sie müssen jedoch danach noch spülen, um die Änderung zu sehen:sys.stdout.flush()
Tobias Domhan
29
Ich stellte fest, dass ich das \ram Anfang der Zeichenfolge einfügen und end=''stattdessen festlegen musste , damit dies funktioniert. Ich glaube nicht, dass es meinem Terminal gefällt, wenn ich mit\r
Jezzamon
2
In allen aktuellen Python 3-Versionen können Sie flush=Truedie print()Funktion erweitern, um sicherzustellen, dass der Puffer \ngeleert wird (der Standardzeilenpuffer wird nur geleert, wenn eine neue Zeile geschrieben wird).
Martijn Pieters
4
In einem Jupyter-Notebook mit dem \ram Anfang und am end=''besten und reibungslosesten funktioniert. Verwenden Sie flush=Truemit \ram Ende, wenn die Zeichenfolge und end='\r'auch funktioniert hat, aber nicht glatt war und die Linie und ihren Zeilenvorschub gelöscht hat.
Dave X
40

Wenn Sie nur eine einzelne Zeile ändern möchten, verwenden Sie \r. \rbedeutet Wagenrücklauf. Der Effekt besteht ausschließlich darin, das Caret wieder an den Anfang der aktuellen Zeile zu setzen. Es löscht nichts. Ebenso \bkann verwendet werden, um ein Zeichen rückwärts zu gehen. (Einige Terminals unterstützen möglicherweise nicht alle diese Funktionen.)

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
quelle
1
Ich würde hinzufügen, dass \rdies Wagenrücklauf bedeutet. Der Effekt besteht ausschließlich darin, das Caret wieder an den Anfang der aktuellen Zeile zu setzen. es löscht nichts. In ähnlicher Weise \bkann ein Zeichen rückwärts gehen. (Einige Terminals unterstützen möglicherweise nicht alle diese Funktionen)
Adrien Plisson
sys.stdout.write('%s\r', size_str')Ich denke du sys.stdout.write('%s\r' % size_str)meintest , aber es funktioniert nicht.
Kristian
@ Adrien: Cool, fügte das hinzu. @Kristian: Danke, aktualisiert, es war ziemlich spät.
Sam Dolan
Beachten Sie auch, dass Sie weiterhin verwenden können, printanstatt, sys.stdout.writewenn Sie es vorziehen: print size_str + "\r",funktioniert gut. Die ,Anweisung am Ende der print-Anweisung unterdrückt den Zeilenumbruch. sys.stdout.flush()wird noch benötigt
Jeff
19

Schauen Sie sich die Dokumentation zum Curses-Modul und das HOWTO des Curses-Moduls an .

Wirklich einfaches Beispiel:

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
quelle
10
Flüche sind leider nur unter Unix verfügbar. Das OP hat uns nicht mitgeteilt, auf welches Betriebssystem seine Anwendung abzielt ...
Adrien Plisson
Ich bin es so gewohnt, unter Linux zu arbeiten, dass ich in den Moduldokumenten nicht einmal die Warnung bemerkt habe, dass das Modul nur für UNIX ist. Vielen Dank für den Hinweis, @Adrien.
Andrea Spadaccini
3
@AdrienPlisson, ich weiß, dass diese Frage vor Jahren gestellt wurde, aber Sie können tatsächlich Flüche auf Windows bekommen: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz
Als ich dies in meinen Python-Interpreter einfügte, wurde mein Terminal fubar. Das Verlassen des Dolmetschers half nicht. WTF?!
Allyourcode
Verwendung clrtoeolfür , wenn die zweite Linie kürzer ist als die erste.
Serge Stroobandt
9

Hier ist meine kleine Klasse, die Textblöcke nachdrucken kann. Der vorherige Text wird ordnungsgemäß gelöscht, sodass Sie Ihren alten Text mit kürzerem neuen Text überschreiben können, ohne dass es zu Unordnung kommt.

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
quelle
2
Das wird nicht funktionieren in Windows bis Windows 10, da Windows 10 die ersten Fenster ist diese Steuerzeichen zu unterstützen en.wikipedia.org/wiki/ANSI_escape_code
user136036
8

Ich habe festgestellt, dass für eine einfache print-Anweisung in Python 2.7 einfach ein Komma am Ende nach Ihrem steht '\r'.

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

Dies ist kürzer als bei anderen Nicht-Python-3-Lösungen, aber auch schwieriger zu warten.

Matt Ellen
quelle
1
Python 2.7.10, und es druckt nichts. Vielleicht können Sie es online kompilieren und uns einen Link senden, um zu sehen, wie es funktioniert ...?
Shougo Makishima
5

Sie können einfach '\ r' am Ende der Zeichenfolge und ein Komma am Ende der Druckfunktion hinzufügen. Beispielsweise:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),
Moustafa Saleh
quelle
1
Dies ist für Python 3? Wenn ja, was ist mit dem Standardparameter so endeingestellt, \ndass Sie drucken (...)KB downloaded!\r\n. Liege ich falsch?
SR
Die Verwendung von end = '\r'stattdessen behebt das Problem in Python 3.
Abhimanyu Pallavi Sudhir
4

Ich verwende Spyder 3.3.1 - Windows 7 - Python 3.6, obwohl möglicherweise kein Flush erforderlich ist. basierend auf diesem Beitrag - 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
quelle
1

Um die vorherige Zeile in Python zu überschreiben, müssen Sie lediglich end = '\ r' zur Druckfunktion hinzufügen. Testen Sie dieses Beispiel:

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